PluginProbe
Meow Gallery / 4.2.2
Meow Gallery v4.2.2
5.5.6 5.5.5 5.5.4 5.5.3 5.5.2 5.5.1 5.5.0 5.4.9 5.4.8 5.4.7 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 All 158 releases
← All changes | classes/core.php +36 -1241 5.4.8 → 4.2.2 View file →
@@ -2,69 +2,27 @@
2 2
3 3 class Meow_MGL_Core {
4 4
5 5 private $gallery_process = false;
6 - private $gallery_layout = 'tiles';
7 6 private $is_gallery_used = true; // TODO: Would be nice to detect if the gallery is actually used on the current page.
8 - private $skeleton_handler;
9 - private $pro_module = false;
10 7
11 - private static $plugin_option_name = 'mgl_options';
12 - private $option_name = 'mgl_options';
13 - private $infinite_layouts = [
14 - 'tiles',
15 - 'masonry',
16 - 'justified',
17 - 'square',
18 - 'cascade',
19 - // 'carousel', Added dynamically if the option is enabled
20 - ];
21 -
22 - private $rewrittenMwlData = [];
23 -
24 8 public function __construct() {
25 9 load_plugin_textdomain( MGL_DOMAIN, false, MGL_PATH . '/languages' );
26 10
27 - //TODO: Move Skeleton into PRO
28 - // Initialize skeleton handler
29 - require_once( MGL_PATH . '/classes/skeleton.php' );
30 - $this->skeleton_handler = new Meow_MGL_Skeleton();
31 -
32 11 // Initializes the classes needed
33 - MeowKit_MGL_Helpers::is_rest() && new Meow_MGL_Rest( $this );
12 + MeowCommon_Helpers::is_rest() && new Meow_MGL_Rest( $this );
13 + is_admin() && new Meow_MGL_Admin();
14 + class_exists( 'MeowPro_MGL_Core' ) && new MeowPro_MGL_Core();
34 15
35 16 // The gallery build process should only be enabled if the request is non-asynchronous
36 - if ( !MeowKit_MGL_Helpers::is_asynchronous_request() ) {
17 + if ( !MeowCommon_Helpers::is_asynchronous_request() ) {
37 18 add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 25, 3 );
38 19 if ( is_admin() || $this->is_gallery_used ) {
39 20 new Meow_MGL_Run( $this );
40 21 }
41 22 }
42 -
43 - // Load the Pro version *after* loading the Run class due to the JS file was gatherd into one file.
44 -
45 - $this->pro_module = class_exists( 'MeowPro_MGL_Core' );
46 - if ( $this->pro_module ) {
47 - new MeowPro_MGL_Core( $this );
48 - } else {
49 - add_shortcode( 'meow-collection', array( $this, 'collection' ) );
50 - }
51 -
52 - // Initialize the Admin if needed
53 - add_action( 'init', array( $this, 'init' ) );
54 23 }
55 24
56 - function init() {
57 - is_admin() && new Meow_MGL_Admin( $this );
58 -
59 - global $wpmgl;
60 - $wpmgl = $this;
61 - }
62 -
63 - function collection() {
64 - return "<b>Meow Collection</b>: This is only available in the Pro version. Please <a href='https://meowapps.com/products/meow-gallery-pro/'>upgrade to Meow Gallery Pro</a> to use this feature.";
65 - }
66 -
67 25 public function can_access_settings() {
68 26 return apply_filters( 'mgl_allow_setup', current_user_can( 'manage_options' ) );
69 27 }
70 28
@@ -77,9 +35,8 @@
77 35 // Rewrite the sizes attributes of the src-set for each image
78 36 function wp_get_attachment_image_attributes( $attr, $attachment, $size ) {
79 37 if (!$this->gallery_process)
80 38 return $attr;
81 -
82 39 $sizes = null;
83 40 if ( $this->gallery_layout === 'tiles' )
84 41 $sizes = '50vw';
85 42 else if ( $this->gallery_layout === 'masonry' )
@@ -95,278 +52,59 @@
95 52 $attr['sizes'] = $sizes;
96 53 return $attr;
97 54 }
98 55
99 - function get_rewritten_mwl_data() {
100 - return $this->rewrittenMwlData;
101 - }
56 + function gallery( $atts, $isPreview = false ) {
57 + $atts = apply_filters( 'shortcode_atts_gallery', $atts, null, $atts );
102 58
103 - function gallery( $atts, $options = [] ) {
104 - $atts = apply_filters( 'shortcode_atts_gallery', $atts, null, $atts, 'gallery' );
105 -
106 - $isPreview = isset( $options['isPreview'] ) ? $options['isPreview'] : false;
107 - $isRest = isset( $options['isRest'] ) ? $options['isRest'] : false;
108 -
109 - // Sanitize the atts to avoid XSS
110 - $atts = array_map( function( $x ) {
111 - if ( is_array( $x ) ) {
112 - // In case it contains an array, we need to sanitize each element, and avoid a string conversion issue
113 - return array_map( function( $y ) { return is_null( $y ) ? $y : esc_attr( $y ); }, $x );
114 - } else {
115 - // We don't sanitize null value, as it would convert it to a empty string
116 - return is_null( $x ) ? $x : esc_attr( $x );
117 - }
118 - }, $atts );
119 -
120 - if ( isset( $atts['meow'] ) && $atts['meow'] === 'false' ) {
121 - return gallery_shortcode( $atts );
122 - }
123 -
124 - // If the attributes contain "collection" then use the collection shortcode instead
125 - if ( isset( $atts['collection'] ) && !empty( $atts['collection'] ) ) {
126 - return do_shortcode( '[meow-collection id="' . $atts['collection'] . '"]' );
127 - }
128 -
129 - $image_ids = array();
130 - $layout = '';
131 -
132 - if ( isset( $atts['id'] ) && isset( $atts['ids'] ) ) {
133 -
134 - // Check if the ids are empty, then we can use the id
135 - if ( empty( $atts['ids'] ) ) {
136 - unset( $atts['ids'] );
137 - } else {
138 - error_log( "⚠️ Meow Gallery: in gallery $atts[id] both 'id' and 'ids' attributes are used in the same shortcode. 'id' will be ignored." );
139 - }
140 - }
141 -
142 59 // Get the IDs
143 - #region media_ids
144 - if ( (isset( $atts['id'] ) && !empty($atts['id']) ) && !isset( $atts['ids'] ) ) {
145 - $shortcode_id = $atts['id'];
146 -
147 - try {
148 - $shortcode = $this->get_gallery_by_id( $shortcode_id );
149 - }
150 - catch ( Exception $e ) {
151 - return "<p class='meow-error'><b>Meow Gallery:</b> This ID wasn't found in the Gallery Manager. (ID: $shortcode_id). " . $e->getMessage() . "</p>";
152 - }
153 -
154 - if ( !isset( $shortcode['medias'] ) || !isset( $shortcode['medias']['thumbnail_ids'])) {
155 - return "<p class='meow-error'><b>Meow Gallery:</b> Thumbnail IDs not found.</p>";
156 - }
157 -
158 - $image_ids = $shortcode['medias']['thumbnail_ids'];
159 - unset( $shortcode['medias'] );
160 -
161 - //* We merge $atts into $shortcode ( not $shortcode into $atts ) so we can override the gallery settings even when using the ID.
162 - // Override specifics: "default" layout from atts should be the layout from shortcode
163 - if( array_key_exists('layout', $atts) && $atts['layout'] == 'default' )
164 - {
165 - $atts['layout'] = $shortcode['layout'];
166 - }
167 -
168 - $atts = array_merge( $shortcode, $atts );
169 - }
170 -
171 - if ( isset( $atts['ids'] ) ) {
172 - $image_ids = $atts['ids'];
173 - }
60 + $images = array();
61 + if ( isset( $atts['ids'] ) )
62 + $images = $atts['ids'];
174 63 if ( isset( $atts['include'] ) ) {
175 - $image_ids = is_array( $atts['include'] ) ? implode( ',', $atts['include'] ) : $atts['include'];
176 - $atts['include'] = $image_ids;
64 + $images = is_array( $atts['include'] ) ? implode( ',', $atts['include'] ) : $atts['include'];
65 + $atts['include'] = $images;
177 66 }
178 -
179 - // Tags support
180 - if ( isset( $atts['tags'] ) && !empty( $atts['tags'] ) ) {
181 - $tags = is_array( $atts['tags'] ) ? $atts['tags'] : array_map( 'trim', explode( ',', $atts['tags'] ) );
182 -
183 - // Try multiple common taxonomies used for media tagging
184 - $taxonomies_to_try = [ 'post_tag', 'media_tag', 'attachment_tag', 'attachment_category' ];
185 - $taxonomies_to_try = apply_filters( 'mgl_tags_taxonomies', $taxonomies_to_try );
186 -
187 - $tagged_media_ids = [];
188 -
189 - foreach ( $taxonomies_to_try as $taxonomy ) {
190 - if ( !taxonomy_exists( $taxonomy ) ) {
191 - continue;
192 - }
193 -
194 - $args = [
195 - 'post_type' => 'attachment',
196 - 'post_status' => 'inherit',
197 - 'posts_per_page' => -1,
198 - 'fields' => 'ids',
199 - 'tax_query' => [
200 - [
201 - 'taxonomy' => $taxonomy,
202 - 'field' => 'slug',
203 - 'terms' => $tags,
204 - ]
205 - ]
206 - ];
207 -
208 - $query = new WP_Query( $args );
209 - if ( !empty( $query->posts ) ) {
210 - $tagged_media_ids = array_merge( $tagged_media_ids, $query->posts );
211 - }
212 - }
213 -
214 - $tagged_media_ids = array_unique( $tagged_media_ids );
215 -
216 - if ( !empty( $tagged_media_ids ) ) {
217 - if ( !empty( $image_ids ) ) {
218 - // Merge with existing IDs
219 - $existing_ids = is_array( $image_ids ) ? $image_ids : explode( ',', $image_ids );
220 - $image_ids = implode( ',', array_unique( array_merge( $existing_ids, $tagged_media_ids ) ) );
221 - } else {
222 - $image_ids = implode( ',', $tagged_media_ids );
223 - }
224 - }
225 - }
226 -
227 - if ( isset( $atts['latest_posts'] ) ) {
228 - $num_posts = intval( $atts['latest_posts'] );
229 -
230 - if ( $num_posts > 0 ) {
231 -
232 - $latest_posts = get_posts( [ 'numberposts' => $num_posts ] );
233 - $latest_posts_ids = array_map( function( $x ) { return $x->ID; }, $latest_posts );
234 -
235 - if ( isset( $atts['posts'] ) ) {
236 - error_log( "⚠️ Meow Gallery: in gallery $atts[id] both 'latest_posts' and 'posts' attributes are used in the same shortcode. 'latest_posts' will be merged with 'posts'.");
237 - $atts['posts'] = array_merge( $latest_posts_ids, explode(',', $atts['posts']) );
238 - }
239 - else {
240 - $atts['posts'] = implode( ',', $latest_posts_ids );
241 - }
242 - }
243 -
244 - }
245 -
246 - $posts_ids = [];
247 - if (isset($atts['posts'])) {
248 -
249 - $posts_ids = is_array( $atts['posts'] ) ? $atts['posts'] : explode( ',', $atts['posts'] );
250 - $featured_images = [];
251 -
252 - foreach ($posts_ids as $key => $post_id) {
253 - $image_id = get_post_thumbnail_id($post_id);
254 - if ($image_id === false || $image_id == 0) {
255 - unset($posts_ids[$key]);
256 - } else {
257 - $featured_images[] = $image_id;
258 - }
259 - }
260 -
261 - if ( count( $posts_ids ) !== count( $featured_images ) ) {
262 - return "<p class='meow-error'><b>Meow Gallery:</b> The number of featured images and posts id should be the same.</p>";
263 - }
264 -
265 - $image_ids = implode(',', $featured_images);
266 - $posts_ids = array_values($posts_ids);
267 - }
268 -
269 -
270 - // Filter the IDs
271 - $ids = is_array( $image_ids ) ? $image_ids : explode( ',', $image_ids );
272 - $ids = apply_filters( 'mgl_ids', $ids, $atts );
273 - $image_ids = implode( ',', $ids );
274 -
275 - #endregion
276 -
277 - // Use attached images if still empty
278 - if ( empty( $image_ids ) ) {
67 + if ( empty( $images ) ) {
279 68 $attachments = get_attached_media( 'image' );
280 69 $attachmentIds = array_map( function($x) { return $x->ID; }, $attachments );
281 - if ( !empty( $attachmentIds ) ) {
282 - $image_ids = implode( ',', $attachmentIds );
283 - }
284 - else {
70 + if ( !empty( $attachmentIds ) )
71 + $images = implode( ',', $attachmentIds );
72 + else
285 73 return "<p class='meow-error'><b>Meow Gallery:</b> The gallery is empty.</p>";
286 - }
287 74 }
288 75
289 76 if ( $isPreview ) {
290 - $check = explode( ',', $image_ids );
77 + $check = explode( ',', $images );
291 78 $check = array_slice( $check, 0, 40 );
292 - $image_ids = implode( ',', $check );
79 + $images = implode( ',', $check );
293 80 }
294 81
295 - // Limit images on archive/listing pages (not viewing the full single post)
296 - $should_truncate = $this->get_option( 'truncate_on_listing', true );
297 - $is_archive_context = !is_singular() && !is_admin() && !$isPreview && !$isRest && $should_truncate;
298 - $is_archive_context = apply_filters( 'mgl_is_archive_context', $is_archive_context, $atts );
299 - if ( $is_archive_context ) {
300 - $archive_limit = intval( $this->get_option( 'truncate_count', 4 ) );
301 -
302 - if( $archive_limit === 0 ) {
303 - return ""; // If the user does not want the gallery to be shown at all on listing pages
304 - }
305 -
306 - if ( $archive_limit > 0 ) {
307 - $check = explode( ',', $image_ids );
308 - if ( count( $check ) > $archive_limit ) {
309 - $check = array_slice( $check, 0, $archive_limit );
310 - $image_ids = implode( ',', $check );
311 - $atts['is_truncated'] = true; // Flag to potentially show "view more" indicator
312 - }
313 - }
314 - }
315 -
316 82 // Ordering
317 - if ( isset( $atts['orderby'] ) || isset( $atts['order_by'] ) ) {
318 -
319 - $orderby = '';
320 - $order = 'asc';
321 -
322 - if ( isset( $atts['order'] ) ) {
323 - $order = $atts['order'];
324 - }
325 -
326 - if ( isset( $atts['orderby'] ) ) {
327 - $orderby = $atts['orderby'];
328 - }
329 -
330 - if ( isset( $atts['order_by'] ) ) {
331 - $orderby = $atts['order_by'];
332 - }
333 -
334 - if( strpos( $orderby, '-' ) === 0 ) {
335 - $orderby = explode( '-', $orderby )[0];
336 - $order = explode( '-', $orderby )[1];
337 - }
338 -
339 -
340 - $image_ids = explode( ',', $image_ids );
341 - $image_ids = Meow_MGL_OrderBy::run( $image_ids, $orderby, $order );
342 - $image_ids = implode( ',', $image_ids );
83 + if ( isset( $atts['orderby'] ) ) {
84 + $images = explode( ',', $images );
85 + $images = Meow_MGL_OrderBy::run( $images, $atts['orderby'], isset( $atts['order'] ) ? $atts['order'] : 'asc' );
86 + $images = implode( ',', $images );
343 87 }
344 -
88 +
345 89 // Layout
346 - if ( isset( $atts['layout'] ) && $atts['layout'] != 'default' ) {
90 + $layout = 'none';
91 + if ( isset( $atts['layout'] ) && $atts['layout'] != 'default' )
347 92 $layout = $atts['layout'];
348 - }
349 - else if ( isset( $atts['mgl-layout'] ) && $atts['mgl-layout'] != 'default' ) {
93 + else if ( isset( $atts['mgl-layout'] ) && $atts['mgl-layout'] != 'default' )
350 94 $layout = $atts['mgl-layout'];
351 - $atts['layout'] = $layout;
352 - } else {
353 - $layout = $this->get_option( 'layout', 'tiles' );
354 - $atts['layout'] = $layout;
355 - }
95 + else
96 + $layout = get_option( 'mgl_layout', 'tiles' );
356 97
357 -
358 - if ( $layout === 'none' || $layout === '' ) {
359 - $layout = $this->get_option( 'layout', 'tiles' );
360 - }
361 -
362 -
98 + // Check the settings
99 + if ( $layout === 'none' )
100 + return gallery_shortcode( $atts );
363 101 $layoutClass = 'Meow_MGL_Builders_' . ucfirst( $layout );
364 102 if ( !class_exists( $layoutClass ) ) {
365 103 error_log( "Meow Gallery: Class $layoutClass does not exist." );
366 104 return "<p class='meow-error'><b>Meow Gallery:</b> The layout $layout is not available in this version.</p>";
367 105 }
368 -
106 +
369 107 // Captions
370 108 if ( isset( $atts['captions'] ) && ( $atts['captions'] === false || $atts['captions'] === 'false' ) ) {
371 109 // This is to avoid issues linked to the old block editor for the Meow Gallery
372 110 $atts['captions'] = 'none';
@@ -371,13 +109,8 @@
371 109 // This is to avoid issues linked to the old block editor for the Meow Gallery
372 110 $atts['captions'] = 'none';
373 111 }
374 112
375 - // apply filter 'mgl_sort_ahead' to sort the images before anything else
376 - if ( !empty( $image_ids ) ){
377 - $image_ids = implode( ',', apply_filters( 'mgl_sort_ahead', explode( ',', $image_ids ), $layout, $atts ) );
378 - }
379 -
380 113 //DEBUG: Display $atts
381 114 //error_log( print_r( $atts, 1 ) );
382 115
383 116 // Start the process of building the gallery
@@ -382,955 +115,17 @@
382 115
383 116 // Start the process of building the gallery
384 117 $this->gallery_process = true;
385 118 $this->gallery_layout = $layout;
386 -
387 - // This should be probably removed.
388 - // wp_enqueue_style( 'mgl-css' );
389 -
390 - $infinite = $this->get_option( 'infinite', false ) && class_exists( 'MeowPro_MGL_Core' );
391 -
392 - // $gen = new $layoutClass( $atts, !$isPreview && $infinite, $isPreview );
393 - // $result = $gen->build( $image_ids );
394 -
119 + wp_enqueue_style( 'mgl-css' );
120 + $infinite = get_option( 'mgl_infinite', false ) && class_exists( 'MeowPro_MGL_Core' );
121 + $gen = new $layoutClass( $atts, !$isPreview && $infinite, $isPreview );
122 + $result = $gen->build( $images );
123 + $this->gallery_process = false;
395 124 do_action( 'mgl_' . $layout . '_gallery_created', $layout );
396 125 //$result = apply_filters( 'post_gallery', $result, $atts, null );
397 -
398 - $this->rewrittenMwlData = apply_filters('mgl_force_rewrite_mwl_data', explode( ',', $image_ids ) );
399 - do_action( 'mgl_gallery_created', $atts, explode( ',', $image_ids ), $layout );
400 126
401 - $gallery_options = $this->get_gallery_options( $image_ids, $atts, $infinite, $isPreview, $layout );
402 -
403 - // If infinite scroll option was enabled, get the images up to 12 at first.
404 - $loading_image_ids = explode(',', $image_ids);
405 - $loading_image_ids = apply_filters( 'mgl_sort', $loading_image_ids, [], $layout, $atts );
406 -
407 - // Only add the carousel to the infinite layouts if the option is enabled
408 - if( $infinite && $this->get_option( 'carousel_infinite', false ) ) {
409 - $this->infinite_layouts[] = 'carousel';
410 - }
411 -
412 - if (!$isPreview && $infinite && in_array( $layout, $this->infinite_layouts ) ) {
413 - $loading_image_ids = array_slice( $loading_image_ids, 0, 12 );
414 - }
415 -
416 - $gallery_images = $this->get_gallery_images( $loading_image_ids, $atts, $layout, $gallery_options['size'], $posts_ids );
417 -
418 - // Get the class and data attributes
419 - $class = $this->get_mgl_root_class( $atts );
420 - $data_atts = $this->get_data_as_json( $atts );
421 - $data_gallery_options = $this->get_data_as_json( $gallery_options );
422 - $data_gallery_images = $this->get_data_as_json( $gallery_images );
423 -
424 - $html = sprintf(
425 - '<div class="%s" data-gallery-options="%s" data-gallery-images="%s" data-atts="%s">',
426 - esc_attr( $class ),
427 - $data_gallery_options,
428 - $data_gallery_images,
429 - $data_atts
430 - );
431 -
432 - // Run at /wp-includes/formatting.php on line 3501
433 - $textarr = preg_split( '/(<.*>)/U', $html , -1, PREG_SPLIT_DELIM_CAPTURE);
434 - if ( $textarr === false ) {
435 - $error = preg_last_error();
436 - error_log( "[MEOW GALLERY] Regex: " . preg_last_error_msg() . " (Code $error)" );
437 - return "<p class='meow-error'><b>Meow Gallery:</b> There was an error while building the gallery. Check your PHP Logs.</p>";
438 - }
439 -
440 - //The Gallery Container is where the images in the right layout will be rendered.
441 - $html .= '<div class="mgl-gallery-container"></div>';
442 -
443 - // Add skeleton loading placeholder to prevent layout shift
444 - $skeleton_loading = $this->get_option( 'skeleton_loading', true );
445 - if ( $skeleton_loading ) {
446 - $image_count = count( $gallery_images );
447 - $html .= $this->skeleton_handler->get_skeleton_html( $layout, $gallery_options, $image_count );
448 - }
449 -
450 - // Use the DOM to generate the images (so that lightboxes can hook into them, and for better SEO)
451 - // If there are no images, the JS will look for the img_html and build the gallery from there.
452 - // TODO: We should check why it's not working with the carousel (for map, it's normal).
453 - if ( $layout !== 'map' && $layout !== 'carousel' && $this->get_option( 'rendering_mode', 'dom' ) === 'dom' ) {
454 - $html .= '<div class="mgl-gallery-images">';
455 -
456 - foreach ( $gallery_images as $image ) {
457 - if ( !empty( $image['link_href'] ) ) {
458 - // If there is a link, we will get the alt from the image id so we have a proper aria-label
459 - $aria = get_post_meta( $image['id'], '_wp_attachment_image_alt', true );
460 -
461 - $custom_link_classes = apply_filters( 'mgl_custom_link_classes', '', $image );
462 - $html .= '<a class="' . $custom_link_classes . '" href="' . $image['link_href'] . '" target="' . $image['link_target'] . '" rel="' . $image['link_rel'] .
463 - '" aria-label="' . $aria . '">';
464 - $html .= $image['img_html'];
465 - $html .= '</a>';
466 - }
467 - else {
468 - $html .= $image['img_html'];
469 - }
470 - }
471 - $html .= '</div>';
472 - }
473 -
474 - $html .= '</div>';
475 -
476 - $this->gallery_process = false;
477 -
478 - return $html;
479 - }
480 -
481 - public function get_gallery_options(string $image_ids, array $atts, bool $infinite, bool $is_preview, string $layout) {
482 - $image_ids = explode(',', $image_ids);
483 - $wp_upload_dir = wp_upload_dir();
484 - $options = $this->get_all_options();
485 - $id = uniqid();
486 - $size = isset( $atts['size'] ) ? $atts['size'] : 'large';
487 - $size = apply_filters( 'mgl_media_size', $size );
488 - $custom_class = isset( $atts['custom-class'] ) ? $atts['custom-class'] : null;
489 - $link = isset( $atts['link'] ) ? $atts['link'] : ( $options['link'] ?? null );
490 - $updir = trailingslashit( $wp_upload_dir['baseurl'] );
491 - $captions = isset( $atts['captions'] ) ? $atts['captions'] : ( $options['captions'] ?? 'none' );
492 - $animation = null;
493 - if ( isset( $atts['animation'] ) && $atts['animation'] != 'default' ) {
494 - $animation = $atts['animation'];
495 - } else {
496 - $animation = $options['animation'] ?? null;
497 - }
498 - $class_id = 'mgl-gallery-' . $id;
499 - $layouts = [];
500 -
501 - // Justified
502 - $justified_row_height = $options['justified_row_height'];
503 - $justified_gutter = $options['justified_gutter'];
504 - if ( $layout === 'justified' ) {
505 - $justified_row_height = $atts['row-height'] ?? $options['justified_row_height'];
506 - $justified_gutter = $atts['gutter'] ?? $options['justified_gutter'];
507 - }
508 - // Masonry
509 - $masonry_gutter = $options['masonry_gutter'];
510 - $masonry_columns = $options['masonry_columns'];
511 - if ( $layout === 'masonry' ) {
512 - $masonry_gutter = $atts['gutter'] ?? $options['masonry_gutter'];
513 - $masonry_columns = $atts['columns'] ?? $options['masonry_columns'];
514 - }
515 - // Square
516 - $square_gutter = $options['square_gutter'];
517 - $square_columns = $options['square_columns'];
518 - if ( $layout === 'square' ) {
519 - $square_gutter = $atts['gutter'] ?? $options['square_gutter'];
520 - $square_columns = $atts['columns'] ?? $options['square_columns'];
521 - }
522 - // Cascade
523 - $cascade_gutter = $options['cascade_gutter'];
524 - if ( $layout === 'cascade' ) {
525 - $layouts = [ 'o', 'i', 'ii' ];
526 - $cascade_gutter = $atts['gutter'] ?? $options['cascade_gutter'];
527 - }
528 - // Tiles
529 - $tiles_gutter = $options['tiles_gutter'];
530 - $tiles_gutter_tablet = $options['tiles_gutter_tablet'];
531 - $tiles_gutter_mobile = $options['tiles_gutter_mobile'];
532 - $tiles_density = $options['tiles_density'];
533 - $tiles_density_tablet = $options['tiles_density_tablet'];
534 - $tiles_density_mobile = $options['tiles_density_mobile'];
535 - if ( $layout === 'tiles' ) {
536 - $tiles_gutter = $atts['gutter'] ?? $options['tiles_gutter'];
537 - $tiles_gutter_tablet = $atts['gutter'] ?? $options['tiles_gutter_tablet'];
538 - $tiles_gutter_mobile = $atts['gutter'] ?? $options['tiles_gutter_mobile'];
539 - $tiles_density = $atts['density'] ?? $options['tiles_density'];
540 - $tiles_density_tablet = $atts['density'] ?? $options['tiles_density_tablet'];
541 - $tiles_density_mobile = $atts['density'] ?? $options['tiles_density_mobile'];
542 - }
543 - // Horizontal
544 - $horizontal_gutter = $options['horizontal_gutter'];
545 - $horizontal_image_height = $options['horizontal_image_height'];
546 - $horizontal_hide_scrollbar = $options['horizontal_hide_scrollbar'];
547 - if ( $layout === 'horizontal' ) {
548 - $horizontal_gutter = $atts['gutter'] ?? $options['horizontal_gutter'];
549 - $horizontal_image_height = $atts['image_height'] ?? $options['horizontal_image_height'];
550 - $horizontal_hide_scrollbar = $atts['hide_scrollbar'] ?? $options['horizontal_hide_scrollbar'];
551 - }
552 - // Carousel
553 - $carousel_gutter = $options['carousel_gutter'];
554 - $carousel_arrow_nav_enabled = $options['carousel_arrow_nav_enabled'];
555 - $carousel_dot_nav_enabled = $options['carousel_dot_nav_enabled'];
556 - $carousel_image_height = $options['carousel_image_height'];
557 - $carousel_keep_aspect_ratio = $options['carousel_aspect_ratio'] ?? false;
558 - if ( $layout === 'carousel' ) {
559 - $carousel_gutter = $atts['gutter'] ?? $options['carousel_gutter'];
560 - $carousel_arrow_nav_enabled = $atts['arrow_nav_enabled'] ?? $options['carousel_arrow_nav_enabled'];
561 - $carousel_dot_nav_enabled = $atts['dot_nav_enabled'] ?? $options['carousel_dot_nav_enabled'];
562 - $carousel_image_height = $atts['image_height'] ?? $options['carousel_image_height'];
563 - $carousel_keep_aspect_ratio = array_key_exists( 'keep-aspect-ratio', $atts ) ? $atts['keep-aspect-ratio'] : 1;
564 - }
565 - // Map
566 - $map_gutter = $options['map_gutter'];
567 - $map_height = $options['map_height'];
568 - if ( $layout === 'map' ) {
569 - $map_gutter = $atts['gutter'] ?? $options['map_gutter'];
570 - $map_height = $atts['map_height'] ?? $options['map_height'];
571 - }
572 -
573 - return compact(
574 - 'image_ids',
575 - 'id',
576 - 'size',
577 - 'infinite',
578 - 'custom_class',
579 - 'link',
580 - 'is_preview',
581 - 'updir',
582 - 'captions',
583 - 'animation',
584 - 'layout',
585 - 'justified_row_height',
586 - 'justified_gutter',
587 - 'masonry_gutter',
588 - 'masonry_columns',
589 - 'square_gutter',
590 - 'square_columns',
591 - 'cascade_gutter',
592 - 'class_id',
593 - 'layouts',
594 - 'tiles_gutter',
595 - 'tiles_gutter_tablet',
596 - 'tiles_gutter_mobile',
597 - 'tiles_density',
598 - 'tiles_density_tablet',
599 - 'tiles_density_mobile',
600 - 'horizontal_gutter',
601 - 'horizontal_image_height',
602 - 'horizontal_hide_scrollbar',
603 - 'carousel_gutter',
604 - 'carousel_arrow_nav_enabled',
605 - 'carousel_dot_nav_enabled',
606 - 'carousel_image_height',
607 - 'carousel_keep_aspect_ratio',
608 - 'map_gutter',
609 - 'map_height',
610 - );
611 - }
612 -
613 - // #region Options
614 -
615 - static function get_plugin_option_name() {
616 - return self::$plugin_option_name;
617 - }
618 -
619 - static function get_plugin_option( $option_name, $default = null ) {
620 - $options = get_option( self::$plugin_option_name, null );
621 - if ( !empty( $options ) && array_key_exists( $option_name, $options ) ) {
622 - return $options[$option_name];
623 - }
624 - return $default;
625 - }
626 -
627 - function get_option( $option_name, $default = null ) {
628 - $options = $this->get_all_options();
629 - if ( array_key_exists( $option_name, $options ) ) {
630 - return $options[$option_name];
631 - }
632 - return $default;
633 - }
634 -
635 - function reset_options() {
636 - delete_option( 'mgl_db_version');
637 - delete_option( $this->option_name );
638 - }
639 -
640 - function list_options() {
641 - return array(
642 - 'layout' => 'tiles',
643 - 'ç' => 'none',
644 - 'link' => null,
645 - 'caption_source' => 'caption',
646 - 'captions_alignment' => 'center',
647 - 'captions_background' => 'fade-black',
648 - 'animation' => false,
649 - 'image_size' => 'srcset',
650 - 'truncate_on_listing' => true,
651 - 'truncate_count' => 4,
652 -
653 - 'rendering_mode' => 'dom', // Can be 'dom' or 'js'
654 - 'tiles_gutter' => 10,
655 - 'tiles_gutter_tablet' => 10,
656 - 'tiles_gutter_mobile' => 10,
657 - 'tiles_density' => 'high',
658 - 'tiles_density_tablet' => 'medium',
659 - 'tiles_density_mobile' => 'low',
660 -
661 - 'justified_density' => 'low',
662 - 'justified_density_tablet' => 'medium',
663 - 'justified_density_mobile' => 'low',
664 -
665 - 'masonry_gutter' => 5,
666 - 'masonry_columns' => 3,
667 - 'masonry_left_to_right' => false,
668 - 'justified_gutter' => 5,
669 - 'justified_row_height' => 200,
670 - 'square_gutter' => 5,
671 - 'square_columns' => 5,
672 - 'cascade_gutter' => 10,
673 - 'horizontal_gutter' => 10,
674 - 'horizontal_image_height' => 500,
675 - 'horizontal_hide_scrollbar' => false,
676 - 'carousel_gutter' => 5,
677 - 'carousel_image_height' => 500,
678 - 'carousel_arrow_nav_enabled' => true,
679 - 'carousel_dot_nav_enabled' => true,
680 - 'carousel_infinite' => false,
681 - 'map_engine' => 'googlemaps',
682 - 'map_height' => 500,
683 - 'map_zoom' => 10,
684 - 'map_gutter' => 10,
685 - 'googlemaps_token' => '',
686 - 'googlemaps_style' => '[]',
687 - 'mapbox_token' => '',
688 - 'mapbox_style' => '{"username":"", "style_id":""}',
689 - 'maptiler_token' => '',
690 -
691 - // Stylish effect options
692 - 'stylish_enabled' => false,
693 - 'stylish_border_radius' => 6,
694 - 'stylish_shadow_opacity' => 0.08,
695 - 'stylish_shadow_opacity_hover' => 0.12,
696 - 'stylish_transition_speed' => 250,
697 -
698 - //PRO OPTIONS
699 - 'infinite' => false,
700 - 'infinite_buffer' => 0,
701 - 'right_click' => false,
702 - 'gallery_shortcode_override_disabled' => false,
703 - 'skeleton_loading' => false,
704 - );
705 - }
706 -
707 - function list_pro_options() {
708 - return array(
709 - 'infinite' => false,
710 - 'infinite_buffer' => 0,
711 - 'right_click' => false,
712 - 'gallery_shortcode_override_disabled' => false,
713 - 'skeleton_loading' => false,
714 - );
715 - }
716 -
717 - function get_all_options() {
718 - $options = get_option( $this->option_name, null );
719 - $options = $this->check_options( $options );
720 - return $options;
721 - }
722 -
723 - // Upgrade from the old way of storing options to the new way.
724 -
725 - function check_options( $options = [] ) {
726 - $plugin_options = $this->list_options();
727 - $pro_options = $this->list_pro_options();
728 -
729 - $options = empty( $options ) ? [] : $options;
730 - $hasChanges = false;
731 -
732 - foreach ( $plugin_options as $option => $default ) {
733 - // The option already exists
734 - if ( isset( $options[$option] ) ) {
735 - continue;
736 - }
737 - // The option does not exist, so we need to add it.
738 - // Let's use the old value if any, or the default value.
739 - $options[$option] = get_option( 'mgl_' . $option, $default );
740 - delete_option( 'mgl_' . $option );
741 - $hasChanges = true;
742 - }
743 -
744 - if( !$this->pro_module ) {
745 - foreach ( $pro_options as $pro_option => $default ) {
746 - if ( $options[$pro_option] !== $default ) {
747 - $options[$pro_option] = $default;
748 - $hasChanges = true;
749 - }
750 - }
751 - }
752 -
753 - if ( $hasChanges ) {
754 - update_option( $this->option_name , $options );
755 - }
756 - return $options;
757 - }
758 -
759 - function update_options( $options ) {
760 - if ( !update_option( $this->option_name, $options, false ) ) {
761 - return false;
762 - }
763 - $options = $this->sanitize_options();
764 - return $options;
765 - }
766 -
767 -
768 - // Validate and keep the options clean and logical.
769 - function sanitize_options() {
770 - $options = $this->get_all_options();
771 - // something to do
772 - return $options;
773 - }
774 -
775 - # endregion
776 -
777 - function get_caption_from_source( $image ) {
778 - $caption_source = $this->get_option( 'caption_source', 'caption' );
779 - $caption = '';
780 -
781 - switch ( $caption_source ) {
782 - case 'title':
783 - $caption = $image->title;
784 - break;
785 - case 'caption':
786 - $caption = $image->caption;
787 - break;
788 - case 'description':
789 - $caption = $image->description;
790 - break;
791 - case 'alt':
792 - $caption = $image->alt;
793 - break;
794 - default:
795 - $caption = $image->caption;
796 - break;
797 - }
798 -
799 - return $caption;
800 - }
801 -
802 - function get_gallery_images( array $image_ids, array $atts, string $layout, string $size, array $posts_ids = []) {
803 - global $wpdb;
804 -
805 - // Escape the array of IDs for SQL
806 - $ids = array_map( 'intval', $image_ids );
807 - $ids_str = implode( ',', $ids );
808 -
809 - $query = "SELECT
810 - p.ID id,
811 - p.post_title title,
812 - p.post_content description,
813 - p.post_excerpt caption,
814 - pm.meta_value alt,
815 - pm2.meta_value meta
816 -
817 - FROM $wpdb->posts p
818 - LEFT JOIN $wpdb->postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_wp_attachment_image_alt'
819 - LEFT JOIN $wpdb->postmeta pm2 ON pm2.post_id = p.ID AND pm2.meta_key = '_wp_attachment_metadata'
820 -
821 - WHERE p.post_type = 'attachment'
822 -
823 - AND p.ID IN (" . $ids_str . ")
824 - ";
825 -
826 - $res = $wpdb->get_results( $query );
827 -
828 - $ids = explode( ',', $ids_str );
829 - $images = [];
830 - foreach ( $res as $r ) {
831 - $images[$r->id] = [
832 - 'caption' => $this->get_caption_from_source( $r ),
833 - 'meta' => unserialize( $r->meta ),
834 - ];
835 - }
836 - $cleanIds = [];
837 - foreach ( $ids as $id ) {
838 - if ( isset( $images[$id] ) )
839 - array_push( $cleanIds, $id );
840 - }
841 - $ids = apply_filters( 'mgl_sort', $cleanIds, $images, $layout, $atts );
842 -
843 - if ($layout === 'map') {
844 - return $this->get_map_images( $ids, $images, $atts );
845 - }
846 -
847 - $result = [];
848 - foreach ($ids as $index => $id) {
849 - $image = $images[$id];
850 -
851 - // Determine orientation if layout is 'tiles'
852 - $orientation = [];
853 - if ($layout === 'tiles') {
854 - $orientation = [
855 - 'orientation' => ($image['meta']['width'] > $image['meta']['height'] ? 'o' : 'i')
856 - ];
857 - }
858 -
859 - $default_link = $this->get_option( 'link', null );
860 - $link_attr = $this->get_link_attributes( $id, $atts['link'] ?? $default_link, $image );
861 - $no_lightbox = $link_attr['type'] === 'link';
862 -
863 - $mergedArray = [
864 - 'id' => $id,
865 - 'caption' => wp_kses(
866 - html_entity_decode(apply_filters( 'mgl_caption', $image['caption'], $id ), ENT_QUOTES),
867 - [
868 - 'strong' => [], // Bold
869 - 'b' => [], // Bold alternative
870 - 'em' => [], // Italic
871 - 'i' => [] // Italic alternative
872 - ]
873 - ),
874 - 'img_html' => apply_filters( 'mgl_gallery_written',
875 - $this->get_img_html( $id, $size, $layout, $atts, $image, $no_lightbox ),
876 - $layout
877 - ),
878 - 'link_href' => $link_attr['href'] ?? null,
879 - 'link_target' => $link_attr['target'] ?? null,
880 - 'link_rel' => $link_attr['rel'] ?? null,
881 - 'attributes' => $this->get_attributes( $id, $image, $layout ),
882 - ];
883 -
884 - if( !empty( $posts_ids ) && isset( $atts['hero'] ) && $atts['hero'] ) {
885 -
886 - $post_id = $posts_ids[$index];
887 - $post = get_post( $post_id );
888 -
889 - $mergedArray['featured_post_id'] = $post_id;
890 - $mergedArray['featured_post_title'] = $post->post_title;
891 - $mergedArray['featured_post_excerpt'] = $post->post_excerpt;
892 - $mergedArray['featured_post_url'] = get_permalink( $post_id );
893 -
894 - }
895 -
896 - $result[] = array_merge( $image, $mergedArray, $orientation );
897 - }
898 -
899 127 return $result;
900 128 }
901 -
902 - private function get_image_class( $id, $layout, $noLightbox ) {
903 - $base_class = 'wp-image-' . $id;
904 -
905 - if( $layout === 'carousel' ) {
906 - $base_class .= ' skip-lazy';
907 - }
908 -
909 - if ( $noLightbox ) {
910 - $base_class .= ' no-lightbox';
911 - }
912 - return $base_class;
913 - }
914 -
915 - private function get_img_html( $id, $size, $layout, $atts, $data, $noLightbox ) {
916 -
917 - //check if the media is a video
918 - $media_type = get_post_mime_type( $id );
919 - if ( strpos( $media_type, 'video' ) !== false ) {
920 - $video = wp_get_attachment_url( $id );
921 - $video = apply_filters( 'mgl_video', $video, $id, $data );
922 - if ( !empty( $video ) ) {
923 - return '<video class="wp-video-'. $id .'" controls="controls" onclick="() => this.play();"><source src="' . $video . '" type="' . $media_type . '"></video>';
924 - }
925 - }
926 -
927 - $image_size = $this->get_option( 'image_size', 'srcset' );
928 - $img_html = null;
929 - if ( empty( $image_size ) || $image_size === 'srcset' ) {
930 - $img_html = wp_get_attachment_image( $id, $size, false, [
931 - 'class' => $this->get_image_class( $id, $layout, $noLightbox ),
932 - 'draggable' => $layout === 'carousel' ? 'false' : null,
933 - ]);
934 - }
935 - else {
936 - $info = wp_get_attachment_image_src( $id, $image_size );
937 - $alt_text = get_post_meta( $id, '_wp_attachment_image_alt', true );
938 -
939 - $img_html = '<img loading="lazy" src="' . $info[0] . '" class="' . $this->get_image_class( $id, $layout, $noLightbox ) . '" alt="' . esc_attr( $alt_text ) . '" />';
940 - }
941 -
942 - if ( $layout === 'masonry' ) {
943 - $masonry_column = $this->get_option( 'masonry_column', 3 );
944 - $columns = ( isset( $atts['columns'] ) ? $atts['columns'] : $masonry_column ) + 1;
945 - $img_html = str_replace( '100vw', 100 / $columns . 'vw', $img_html );
946 - }
947 - else if ( $layout === 'square' ) {
948 - $square_column = $this->get_option( 'square_columns', 5 );
949 - $columns = ( isset( $atts['columns'] ) ? $atts['columns'] : $square_column ) + 1;
950 - $img_html = str_replace( '100vw', 100 / $columns . 'vw', $img_html );
951 - }
952 - else if ( $layout === 'cascade' ) {
953 - $img_html = str_replace( '100vw', 100 / 3 . 'vw', $img_html );
954 - }
955 -
956 - return wp_kses( $img_html, [
957 - 'img' => [
958 - 'src' => true,
959 - 'srcset' => true,
960 - 'loading' => true,
961 - 'tabindex' => true,
962 - 'sizes' => true,
963 - 'class' => true,
964 - 'id' => true,
965 - 'width' => true,
966 - 'height' => true,
967 - 'alt' => true,
968 - 'align' => true,
969 - 'draggable' => true,
970 - ]
971 - ]
972 - );
973 - }
974 -
975 - private function get_link_attributes( $id, $link, $data ) {
976 - $link_url = null;
977 - $type = 'media';
978 - $rel = null;
979 - $target = '_self';
980 -
981 - if ( $link === 'attachment' ) {
982 - $link_url = get_permalink( (int)$id );
983 - }
984 - else if ( $link === 'media' || $link === 'file' ) {
985 - $wpUploadDir = wp_upload_dir();
986 - $updir = trailingslashit( $wpUploadDir['baseurl'] );
987 - if ( isset( $data['meta']['file'] ) ) {
988 - $link_url = $updir . $data['meta']['file'];
989 - } else {
990 - $link_url = get_permalink( (int)$id );
991 - }
992 - }
993 - else if ( $link === null ){
994 - $link_url = get_post_meta( $id, '_gallery_link_url', true );
995 - if ( !empty( $link_url ) ) {
996 - $type = 'link';
997 - $target = get_post_meta( $id, '_gallery_link_target', true );
998 - }
999 - }
1000 -
1001 - $link_attr = [
1002 - 'href' => !empty( $link_url ) ? esc_url( $link_url ) : null,
1003 - 'target' => $target,
1004 - 'type' => $type,
1005 - 'rel' => $rel,
1006 - ];
1007 -
1008 -
1009 - return apply_filters( 'mgl_link_attributes', $link_attr, (int)$id, $data );
1010 - }
1011 -
1012 - private function get_attributes( $id, $data, $layout ) {
1013 - $attributes = '';
1014 - if ( $layout === 'raw' ) {
1015 - if ( isset( $data['meta'] ) && isset( $data['meta']['width'] ) && isset( $data['meta']['height'] ) ) {
1016 - $attributes = 'data-mgl-id=' . $id . ' data-mgl-width=' . $data['meta']['width'] . ' data-mgl-height=' . $data['meta']['height'];
1017 - }
1018 - }
1019 - elseif ( $layout === 'tiles' ) {
1020 - if ( isset( $data['meta'] ) && isset( $data['meta']['width'] ) && isset( $data['meta']['height'] ) ) {
1021 - $attributes = 'data-mgl-id=' . $id . ' data-mgl-width=' . $data['meta']['width'] . ' data-mgl-height=' . $data['meta']['height'];
1022 - }
1023 - }
1024 - $attributes = apply_filters( 'mgl_attributes', $attributes, $id, $data );
1025 - if ( $attributes === '' ) {
1026 - return [];
1027 - }
1028 -
1029 - $attribute_list = explode( ' ', $attributes );
1030 - $attributes = [];
1031 - foreach ( $attribute_list as $attribute ) {
1032 - list( $key, $value ) = explode( '=', $attribute );
1033 - $attributes[$key] = $value;
1034 - }
1035 - return $attributes;
1036 - }
1037 -
1038 - private function get_map_images( $ids, $images, $atts = [] ) {
1039 - $map_images = array_map( function ( $id ) use ( $images, $atts ) {
1040 -
1041 - $image = $images[$id];
1042 - $default_link = $this->get_option( 'link', null );
1043 - $link_attr = $this->get_link_attributes( $id, $atts['link'] ?? $default_link, $image );
1044 -
1045 - $geo_coordinates = MeowPro_MGL_Exif::get_gps_data( $id, $image['meta'] );
1046 - if ( empty( $geo_coordinates ) ) {
1047 - return null;
1048 - }
1049 - $callback = function ( &$value, $key ) use ( $id ) {
1050 - $imgsrc = wp_get_attachment_image_src( $id, $key );
1051 - $value = $imgsrc[0];
1052 - };
1053 - array_walk( $image['meta']['sizes'], $callback );
1054 - return array_merge(
1055 - $image,
1056 - [
1057 - 'id' => $id,
1058 - 'file' => $image['meta']['file'],
1059 - 'file_full' => wp_get_attachment_url( $id ),
1060 - 'file_srcset' => wp_get_attachment_image_srcset( $id, 'full' ),
1061 - 'file_sizes' => wp_get_attachment_image_sizes( $id, 'full' ),
1062 - 'dimension' => [
1063 - 'width' => $image['meta']['width'],
1064 - 'height' => $image['meta']['height'],
1065 - ],
1066 - 'sizes' => $image['meta']['sizes'],
1067 - 'data' => [
1068 - 'caption' => $image['meta']['image_meta']['caption'],
1069 - 'gps' => $geo_coordinates,
1070 - ],
1071 - 'link' => $link_attr,
1072 - ]
1073 - );
1074 - }, $ids );
1075 - return array_values( array_filter( $map_images ) );
1076 - }
1077 -
1078 - public function get_mgl_root_class( $atts, $classes = ['mgl-root'] ) {
1079 - $classes[] = isset( $atts['align'] ) ? 'align' . $atts['align'] : '';
1080 - return trim( implode( ' ', $classes ) );
1081 - }
1082 -
1083 - public function get_data_as_json( $data ) {
1084 - return esc_attr( htmlspecialchars( wp_json_encode( $data ), ENT_QUOTES, 'UTF-8' ) );
1085 - }
1086 -
1087 - public function generate_uniqid($length = 13) {
1088 - // Use WordPress function
1089 - if ( function_exists( 'wp_unique_id' ) ) {
1090 - $prefix = uniqid();
1091 - return wp_unique_id( $prefix );
1092 - }
1093 - // Fall back
1094 - else {
1095 - if ( function_exists( "random_bytes" ) ) {
1096 - $bytes = random_bytes( ceil( $length / 2 ) );
1097 - }
1098 - elseif ( function_exists( "openssl_random_pseudo_bytes" ) ) {
1099 - $bytes = openssl_random_pseudo_bytes(ceil($length / 2));
1100 - }
1101 - else {
1102 - throw new Exception( "No cryptographically secure random function available." );
1103 - }
1104 - return substr( bin2hex( $bytes ), 0, $length );
1105 - }
1106 - }
1107 -
1108 -
1109 - public function get_gallery_by_id( $id ) {
1110 - global $wpdb;
1111 - $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1112 - $gallery = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $shortcodes_table WHERE id = %s", $id ), ARRAY_A );
1113 -
1114 - if ( !$gallery ) {
1115 - throw new Exception( __( 'Gallery not found.', MGL_DOMAIN ));
1116 - }
1117 - $gallery['medias'] = maybe_unserialize( $gallery['medias'] );
1118 - $gallery['posts'] = $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null;
1119 - $gallery['tags'] = $gallery['tags'] ? unserialize( $gallery['tags'] ) : null;
1120 -
1121 - return $gallery;
1122 - }
1123 -
1124 - public function get_galleries_by_ids( $ids ){
1125 - global $wpdb;
1126 - $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1127 - $galleries = [];
1128 - $ids_str = "'" . implode( "','", array_map( 'esc_sql', $ids )) . "'";
1129 - $query = "SELECT * FROM $shortcodes_table WHERE id IN ( $ids_str )";
1130 - $results = $wpdb->get_results( $query, ARRAY_A );
1131 - foreach ( $results as $gallery ) {
1132 - $galleries[$gallery['id']] = [
1133 - 'name' => $gallery['name'],
1134 - 'description' => $gallery['description'],
1135 - 'layout' => $gallery['layout'],
1136 - 'medias' => maybe_unserialize( $gallery['medias'] ),
1137 - 'lead_image_id' => $gallery['lead_image_id'],
1138 - 'order_by' => $gallery['order_by'],
1139 - 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1140 - 'dynamic_source' => $gallery['dynamic_source'],
1141 - 'hero' => ( bool )$gallery['is_hero_mode'],
1142 - 'posts' => $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null,
1143 - 'latest_posts' => $gallery['latest_posts'],
1144 - 'tags' => $gallery['tags'] ? unserialize( $gallery['tags'] ) : null,
1145 - 'updated' => strtotime( $gallery['updated_at'] )
1146 - ];
1147 - }
1148 - return $galleries;
1149 - }
1150 -
1151 - public function get_galleries( $offset = 0, $limit = 10, $order = 'DESC', $sort = null, $page = 1, $search = '' ) {
1152 - global $wpdb;
1153 - $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1154 - Meow_MGL_Migrations::check_db();
1155 -
1156 - // Get total count
1157 - $total = 0;
1158 - if ( empty( $search ) ) {
1159 - $total = $wpdb->get_var( "SELECT COUNT( * ) FROM $shortcodes_table" );
1160 - } else {
1161 - $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM $shortcodes_table WHERE name LIKE %s", '%' . $wpdb->esc_like( $search ) . '%' ) );
1162 - }
1163 -
1164 - // Calculate offset based on page if provided
1165 - if ($page > 1 && $offset === 0) {
1166 - $offset = ($page - 1) * $limit;
1167 - }
1168 -
1169 - // Get shortcodes with pagination and sorting
1170 - $sort_accessor_to_column = [
1171 - 'info' => 'name',
1172 - 'updated' => 'updated_at',
1173 - 'rank' => 'pref_rank',
1174 - ];
1175 -
1176 - $sort = $sort_accessor_to_column[$sort] ?? 'pref_rank';
1177 - $order = in_array( strtoupper( $order ), ['ASC', 'DESC'] ) ? strtoupper( $order ) : 'DESC';
1178 -
1179 - // Sort by rank DESC first, then by updated_at for equal ranks
1180 - $query = $wpdb->prepare(
1181 - "SELECT * FROM $shortcodes_table WHERE name LIKE %s ORDER BY $sort $order, updated_at DESC LIMIT %d, %d",
1182 - '%' . $wpdb->esc_like( $search ) . '%', $offset, $limit
1183 - );
1184 -
1185 - $results = $wpdb->get_results( $query, ARRAY_A );
1186 - $shortcodes = [];
1187 -
1188 - foreach ( $results as $gallery ) {
1189 - // Transform database format to match expected format
1190 - $shortcodes[$gallery['id']] = [
1191 - 'name' => $gallery['name'],
1192 - 'description' => $gallery['description'],
1193 - 'layout' => $gallery['layout'],
1194 - 'medias' => maybe_unserialize( $gallery['medias'] ),
1195 - 'lead_image_id' => $gallery['lead_image_id'],
1196 - 'order_by' => $gallery['order_by'],
1197 - 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1198 - 'hero' => ( bool )$gallery['is_hero_mode'],
1199 - 'posts' => $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null,
1200 - 'latest_posts' => $gallery['latest_posts'],
1201 - 'tags' => $gallery['tags'] ? unserialize( $gallery['tags'] ) : null,
1202 - 'dynamic_source' => $gallery['dynamic_source'],
1203 - 'rank' => intval( $gallery['pref_rank'] ?? 0 ),
1204 - 'updated' => strtotime( $gallery['updated_at'] )
1205 - ];
1206 - }
1207 -
1208 - return [
1209 - 'total' => $total,
1210 - 'galleries' => $shortcodes
1211 - ];
1212 - }
1213 -
1214 - public function get_collection_by_id( $id ) {
1215 - global $wpdb;
1216 - $collections_table = $wpdb->prefix . 'mgl_collections';
1217 - $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1218 -
1219 - // Get the collection
1220 - $collection = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $collections_table WHERE id = %d", $id ), ARRAY_A );
1221 - if ( !$collection ) {
1222 - throw new Exception( __( 'Collection not found.', MGL_DOMAIN ));
1223 - }
1224 - $collection['galleries_ids'] = unserialize( $collection['galleries_ids'] );
1225 -
1226 - // Get the associated galleries
1227 - $galleries = [];
1228 - if ( !empty( $collection['galleries_ids'] )) {
1229 - $galleries_ids_str = "'" . implode( "','", array_map( 'esc_sql', $collection['galleries_ids'] )) . "'";
1230 - $galleries_query = "SELECT * FROM $shortcodes_table WHERE id IN ( $galleries_ids_str )";
1231 - $galleries_data = $wpdb->get_results( $galleries_query, ARRAY_A );
1232 -
1233 - foreach ( $galleries_data as $gallery ) {
1234 - // Transform database format to match expected format
1235 - $galleries[] = [
1236 - 'id' => $gallery['id'],
1237 - 'name' => $gallery['name'],
1238 - 'description' => $gallery['description'],
1239 - 'layout' => $gallery['layout'],
1240 - 'medias' => unserialize( $gallery['medias'] ),
1241 - 'lead_image_id' => $gallery['lead_image_id'],
1242 - 'order_by' => $gallery['order_by'],
1243 - 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1244 - 'hero' => ( bool )$gallery['is_hero_mode'],
1245 - 'posts' => $gallery['posts'] ? unserialize( $gallery['posts'] ) : null,
1246 - 'latest_posts' => $gallery['latest_posts'],
1247 - 'updated' => strtotime( $gallery['updated_at'] )
1248 - ];
1249 - }
1250 -
1251 - // Format collection data
1252 - $collection['galleries'] = $galleries;
1253 - }
1254 -
1255 - return $collection;
1256 - }
1257 -
1258 - public function get_collections( $offset = 0, $limit = 10, $order = 'DESC', $page = 1, $search = '' ) {
1259 - global $wpdb;
1260 - $collections_table = $wpdb->prefix . 'mgl_collections';
1261 - $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1262 - Meow_MGL_Migrations::check_db();
1263 -
1264 - // Get total count
1265 - if( empty( $search ) ) {
1266 - $total = $wpdb->get_var( "SELECT COUNT( * ) FROM $collections_table" );
1267 - } else {
1268 - $total = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( * ) FROM $collections_table WHERE name LIKE %s", '%' . $wpdb->esc_like( $search ) . '%' ) );
1269 - }
1270 - // Calculate offset based on page if provided
1271 - if ($page > 1 && $offset === 0) {
1272 - $offset = ($page - 1) * $limit;
1273 - }
1274 -
1275 - // Get collections with pagination and sorting
1276 - $query = $wpdb->prepare(
1277 - "SELECT * FROM $collections_table WHERE name LIKE %s ORDER BY updated_at $order LIMIT %d, %d",
1278 - '%' . $wpdb->esc_like( $search ) . '%', $offset, $limit
1279 - );
1280 -
1281 - $collections = $wpdb->get_results( $query, ARRAY_A );
1282 - $result = [];
1283 -
1284 - foreach ( $collections as $collection ) {
1285 - $collection_id = $collection['id'];
1286 - $galleries_ids = unserialize( $collection['galleries_ids'] );
1287 -
1288 - // Get the associated galleries
1289 - $galleries = [];
1290 - if ( !empty( $galleries_ids )) {
1291 - $galleries_ids_str = "'" . implode( "','", array_map( 'esc_sql', $galleries_ids )) . "'";
1292 - $galleries_query = "SELECT * FROM $shortcodes_table WHERE id IN ( $galleries_ids_str )";
1293 - $galleries_data = $wpdb->get_results( $galleries_query, ARRAY_A );
1294 -
1295 - foreach ( $galleries_data as $gallery ) {
1296 - // Transform database format to match expected format
1297 - $gallery_item = [
1298 - 'id' => $gallery['id'],
1299 - 'name' => $gallery['name'],
1300 - 'description' => $gallery['description'],
1301 - 'layout' => $gallery['layout'],
1302 - 'medias' => unserialize( $gallery['medias'] ),
1303 - 'lead_image_id' => $gallery['lead_image_id'],
1304 - 'order_by' => $gallery['order_by'],
1305 - 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1306 - 'hero' => ( bool )$gallery['is_hero_mode'],
1307 - 'posts' => $gallery['posts'] ? unserialize( $gallery['posts'] ) : null,
1308 - 'latest_posts' => $gallery['latest_posts'],
1309 - 'tags' => $gallery['tags'] ? unserialize( $gallery['tags'] ) : null,
1310 - 'dynamic_source' => $gallery['dynamic_source'],
1311 - 'updated' => strtotime( $gallery['updated_at'] )
1312 - ];
1313 - $galleries[] = $gallery_item;
1314 - }
1315 - }
1316 -
1317 - // Format collection data
1318 - $result[$collection_id] = [
1319 - 'name' => $collection['name'],
1320 - 'description' => $collection['description'],
1321 - 'layout' => $collection['layout'],
1322 - 'galleries_ids' => $galleries_ids,
1323 - 'galleries' => $galleries,
1324 - 'updated' => strtotime( $collection['updated_at'] )
1325 - ];
1326 - }
1327 -
1328 - return [
1329 - 'total' => $total,
1330 - 'collections' => $result
1331 - ];
1332 - }
1333 -
1334 129 }
1335 130
1336 131 ?>