PluginProbe
Meow Gallery / 5.5.6
Meow Gallery v5.5.6
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 +226 -20 5.5.1 → 5.5.6 View file →
@@ -7,8 +7,10 @@
7 7 private $is_gallery_used = true; // TODO: Would be nice to detect if the gallery is actually used on the current page.
8 8 private $skeleton_handler;
9 9 private $pro_module = false;
10 10
11 + public $preview_cutoff = 12; // Limit the number of images to show in the preview (for performance reasons)
12 +
11 13 private static $plugin_option_name = 'mgl_options';
12 14 private $pro;
13 15 private $option_name = 'mgl_options';
14 16 private $infinite_layouts = [
@@ -21,8 +23,11 @@
21 23 ];
22 24
23 25 private $rewrittenMwlData = [];
24 26
27 + // Holds the image counts of the last preview render (used by the block editor).
28 + public $last_preview_counts = [ 'total' => 0, 'shown' => 0 ];
29 +
25 30 public function __construct() {
26 31 load_plugin_textdomain( MGL_DOMAIN, false, MGL_PATH . '/languages' );
27 32
28 33 //TODO: Move Skeleton into PRO
@@ -33,16 +38,19 @@
33 38 // Initializes the classes needed
34 39 MeowKit_MGL_Helpers::is_rest() && new Meow_MGL_Rest( $this );
35 40
36 41 // The gallery build process should only be enabled if the request is non-asynchronous
42 + add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 25, 3 );
43 +
37 44 if ( !MeowKit_MGL_Helpers::is_asynchronous_request() ) {
38 - add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 25, 3 );
45 +
39 46 if ( is_admin() || $this->is_gallery_used ) {
40 47 new Meow_MGL_Run( $this );
41 48 }
42 49 }
43 50
44 - // Load the Pro version *after* loading the Run class due to the JS file was gatherd into one file.
51 + // Load the Pro version *after* the Run class: both share the same JS bundle, and Run is
52 + // the one registering it (the Pro class only localizes extra data on it).
45 53
46 54 $this->pro_module = class_exists( 'MeowPro_MGL_Core' );
47 55 if ( $this->pro_module ) {
48 56 $this->pro = new MeowPro_MGL_Core( $this );
@@ -69,8 +77,34 @@
69 77 function collection() {
70 78 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.";
71 79 }
72 80
81 + // Gallery and collection IDs are plain identifiers (generate_uniqid(), stored as varchar).
82 + // Anything else is rejected, and rejected rather than stripped: stripping could turn a crafted
83 + // ID into a different existing one. Security: esc_attr() does NOT escape "]", so an ID coming
84 + // from a request and concatenated into a shortcode string could close the tag and run
85 + // arbitrary shortcodes. Keep IDs on this charset and never build a shortcode string from them.
86 + public static function sanitize_id( $id ) {
87 + if ( !is_scalar( $id ) ) {
88 + return '';
89 + }
90 + $id = (string) $id;
91 + return preg_match( '/^[A-Za-z0-9_-]+$/', $id ) ? $id : '';
92 + }
93 +
94 + // Renders a collection from its ID. Deliberately calls the handler directly instead of going
95 + // through do_shortcode(): there is no shortcode string to inject into that way.
96 + public function render_collection( $id, $is_preview = false ) {
97 + $id = self::sanitize_id( $id );
98 + if ( $id === '' ) {
99 + return "<p class='meow-error'><b>Meow Gallery:</b> This collection ID is not valid.</p>";
100 + }
101 + if ( $this->pro_module && $this->pro ) {
102 + return $this->pro->collection( array( 'id' => $id ), $is_preview );
103 + }
104 + return $this->collection();
105 + }
106 +
73 107 public function can_access_settings() {
74 108 return apply_filters( 'mgl_allow_setup', current_user_can( 'manage_options' ) );
75 109 }
76 110
@@ -95,11 +129,14 @@
95 129 else if ( $this->gallery_layout === 'cascade' )
96 130 $sizes = '80vw';
97 131 else if ( $this->gallery_layout === 'justified' )
98 132 $sizes = '(max-width: 800px) 80vw, 50vw';
133 +
99 134 $sizes = apply_filters( 'mgl_sizes', $sizes, $this->gallery_layout, $attachment, $attr );
135 +
100 136 if ( !empty( $sizes ) )
101 137 $attr['sizes'] = $sizes;
138 +
102 139 return $attr;
103 140 }
104 141
105 142 function get_rewritten_mwl_data() {
@@ -105,8 +142,14 @@
105 142 function get_rewritten_mwl_data() {
106 143 return $this->rewrittenMwlData;
107 144 }
108 145
146 + // Get the IDs of the image attachments attached to the current post.
147 + private function get_attached_image_ids() {
148 + $attachments = get_attached_media( 'image' );
149 + return array_map( function( $x ) { return $x->ID; }, $attachments );
150 + }
151 +
109 152 function gallery( $atts, $options = [] ) {
110 153 $atts = apply_filters( 'shortcode_atts_gallery', $atts, null, $atts, 'gallery' );
111 154
112 155 $isPreview = isset( $options['isPreview'] ) ? $options['isPreview'] : false;
@@ -126,11 +169,11 @@
126 169 if ( isset( $atts['meow'] ) && $atts['meow'] === 'false' ) {
127 170 return gallery_shortcode( $atts );
128 171 }
129 172
130 - // If the attributes contain "collection" then use the collection shortcode instead
173 + // If the attributes contain "collection" then render that collection instead
131 174 if ( isset( $atts['collection'] ) && !empty( $atts['collection'] ) ) {
132 - return do_shortcode( '[meow-collection id="' . $atts['collection'] . '"]' );
175 + return $this->render_collection( $atts['collection'] );
133 176 }
134 177
135 178 $image_ids = array();
136 179 $layout = '';
@@ -141,8 +184,9 @@
141 184 $has_include = isset( $atts['include'] ) && !empty( $atts['include'] );
142 185 $has_tags = isset( $atts['tags'] ) && !empty( $atts['tags'] );
143 186 $has_posts = isset( $atts['posts'] ) && !empty( $atts['posts'] );
144 187 $has_latest_posts = isset( $atts['latest_posts'] ) && !empty( $atts['latest_posts'] );
188 + $has_attachments = isset( $atts['attachments'] ) && ( $atts['attachments'] === 'true' || $atts['attachments'] === true || $atts['attachments'] === 1 || $atts['attachments'] === '1' );
145 189
146 190 if ( $has_id && $has_ids ) {
147 191 unset( $atts['ids'] );
148 192 error_log( "⚠️ Meow Gallery: in gallery $atts[id] both 'id' and 'ids' attributes are used in the same shortcode. 'id' will be ignored." );
@@ -156,9 +200,10 @@
156 200 try {
157 201 $shortcode = $this->get_gallery_by_id( $shortcode_id );
158 202 }
159 203 catch ( Exception $e ) {
160 - return "<p class='meow-error'><b>Meow Gallery:</b> This ID wasn't found in the Gallery Manager. (ID: $shortcode_id). " . $e->getMessage() . "</p>";
204 + $safe_id = esc_html( is_scalar( $shortcode_id ) ? $shortcode_id : '' );
205 + return "<p class='meow-error'><b>Meow Gallery:</b> This ID wasn't found in the Gallery Manager. (ID: $safe_id). " . esc_html( $e->getMessage() ) . "</p>";
161 206 }
162 207
163 208 if ( !isset( $shortcode['medias'] ) || !isset( $shortcode['medias']['thumbnail_ids'])) {
164 209 return "<p class='meow-error'><b>Meow Gallery:</b> Thumbnail IDs not found.</p>";
@@ -176,8 +221,21 @@
176 221
177 222 $atts = array_merge( $shortcode, $atts );
178 223 }
179 224
225 + // Real Media Library folder path (e.g. rml="2025/France/Tours").
226 + // Re-evaluated here (not only from the top) so it also works when the value
227 + // comes from a saved gallery loaded via [gallery id="..."]. The logic lives
228 + // in Meow_MGL_RML::get_image_ids() to keep this method clean.
229 + $has_rml = isset( $atts['rml'] ) && !empty( $atts['rml'] );
230 + if ( $has_rml ) {
231 + $rml_ids = Meow_MGL_RML::get_image_ids( $atts['rml'] );
232 + if ( is_wp_error( $rml_ids ) ) {
233 + return "<p class='meow-error'><b>Meow Gallery:</b> " . esc_html( $rml_ids->get_error_message() ) . "</p>";
234 + }
235 + $image_ids = implode( ',', $rml_ids );
236 + }
237 +
180 238 if ( $has_ids ) {
181 239 $image_ids = $atts['ids'];
182 240 }
183 241
@@ -252,8 +310,15 @@
252 310 }
253 311
254 312 }
255 313
314 + if( $has_attachments ) {
315 + $attachmentIds = $this->get_attached_image_ids();
316 + if ( !empty( $attachmentIds ) ) {
317 + $image_ids = implode( ',', $attachmentIds );
318 + }
319 + }
320 +
256 321 $posts_ids = [];
257 322 if ( $has_posts ) {
258 323
259 324 $posts_ids = is_array( $atts['posts'] ) ? $atts['posts'] : explode( ',', $atts['posts'] );
@@ -283,23 +348,32 @@
283 348 $image_ids = implode( ',', $ids );
284 349
285 350 #endregion
286 351
287 - // Use attached images if still empty
288 - if ( empty( $image_ids ) ) {
289 - $attachments = get_attached_media( 'image' );
290 - $attachmentIds = array_map( function($x) { return $x->ID; }, $attachments );
352 + // Fall back to the attachments of the current post if the gallery is empty and the option is enabled.
353 + if ( empty( $image_ids ) && !$has_attachments && $this->get_option( 'use_attachments_on_empty', false ) ) {
354 + $attachmentIds = $this->get_attached_image_ids();
291 355 if ( !empty( $attachmentIds ) ) {
292 356 $image_ids = implode( ',', $attachmentIds );
357 + $has_attachments = true;
293 358 }
294 - else {
295 - return "<p class='meow-error'><b>Meow Gallery:</b> The gallery is empty.</p>";
359 + }
360 +
361 + // Use attached images if still empty
362 + if ( empty( $image_ids ) ) {
363 +
364 + if( $has_attachments ) {
365 + return "<p class='meow-error'><b>Meow Gallery:</b> No attached medias were found in the current post.</p>";
296 366 }
367 +
368 + return "<p class='meow-error'><b>Meow Gallery:</b> The gallery is empty.</p>";
297 369 }
298 370
299 371 if ( $isPreview ) {
300 372 $check = explode( ',', $image_ids );
301 - $check = array_slice( $check, 0, 40 );
373 + $total = count( $check );
374 + $check = array_slice( $check, 0, $this->preview_cutoff );
375 + $this->last_preview_counts = [ 'total' => $total, 'shown' => count( $check ) ];
302 376 $image_ids = implode( ',', $check );
303 377 }
304 378
305 379 // Limit images on archive/listing pages (not viewing the full single post)
@@ -475,9 +549,11 @@
475 549
476 550 foreach ( $gallery_images as $image ) {
477 551 if ( !empty( $image['link_href'] ) ) {
478 552 // If there is a link, we will get the alt from the image id so we have a proper aria-label
479 - $aria = get_post_meta( $image['id'], '_wp_attachment_image_alt', true );
553 + $aria_label = __('Open image', MGL_DOMAIN);
554 + $aria_value = esc_attr( get_post_meta( $image['id'], '_wp_attachment_image_alt', true ) );
555 + $aria = !empty( $aria_value ) ? $aria_label . ': ' . $aria_value : $aria_label;
480 556
481 557 $custom_link_classes = apply_filters( 'mgl_custom_link_classes', '', $image );
482 558 $html .= '<a class="' . $custom_link_classes . '" href="' . $image['link_href'] . '" target="' . $image['link_target'] . '" rel="' . $image['link_rel'] .
483 559 '" aria-label="' . $aria . '">';
@@ -635,8 +711,26 @@
635 711 static function get_plugin_option_name() {
636 712 return self::$plugin_option_name;
637 713 }
638 714
715 + // Tiles density, from the shortcode attributes when they set one, from the options otherwise.
716 + // Shared by the front-end settings (Meow_MGL_Run) and the tiles CSS (Meow_MGL_Builders_Tiles).
717 + static function get_tiles_density( $atts = [] ) {
718 + if ( isset( $atts['density'] ) ) {
719 + return array(
720 + 'desktop' => $atts['density'],
721 + 'tablet' => $atts['density'],
722 + 'mobile' => $atts['density'],
723 + );
724 + }
725 + $options = get_option( self::$plugin_option_name, [] );
726 + return array(
727 + 'desktop' => $options['tiles_density'] ?? 'high',
728 + 'tablet' => $options['tiles_density_tablet'] ?? 'medium',
729 + 'mobile' => $options['tiles_density_mobile'] ?? 'low',
730 + );
731 + }
732 +
639 733 static function get_plugin_option( $option_name, $default = null ) {
640 734 $options = get_option( self::$plugin_option_name, null );
641 735 if ( !empty( $options ) && array_key_exists( $option_name, $options ) ) {
642 736 return $options[$option_name];
@@ -668,8 +762,10 @@
668 762 'animation' => false,
669 763 'image_size' => 'srcset',
670 764 'truncate_on_listing' => true,
671 765 'truncate_count' => 4,
766 + 'use_attachments_on_empty' => false,
767 + 'debug_logs' => false,
672 768
673 769 'rendering_mode' => 'dom', // Can be 'dom' or 'js'
674 770 'tiles_gutter' => 10,
675 771 'tiles_gutter_tablet' => 10,
@@ -710,10 +806,13 @@
710 806
711 807 // Stylish effect options
712 808 'stylish_enabled' => false,
713 809 'stylish_border_radius' => 6,
810 + 'stylish_border_width' => 0,
811 + 'stylish_border_color' => '#ffffff',
714 812 'stylish_shadow_opacity' => 0.08,
715 813 'stylish_shadow_opacity_hover' => 0.12,
814 + 'stylish_hover_lift' => 2,
716 815 'stylish_transition_speed' => 250,
717 816
718 817 //PRO OPTIONS
719 818 'infinite' => false,
@@ -821,8 +920,16 @@
821 920
822 921 function get_gallery_images( array $image_ids, array $atts, string $layout, string $size, array $posts_ids = []) {
823 922 global $wpdb;
824 923
924 + // Enable the image-attribute rewriting (and the 'mgl_sizes' filter) for the duration of this method.
925 + // This matters when get_gallery_images() is called directly (e.g. infinite scroll via REST) without
926 + // going through gallery(), which is what normally sets these on the initial page load.
927 + $previous_gallery_process = $this->gallery_process;
928 + $previous_gallery_layout = $this->gallery_layout;
929 + $this->gallery_process = true;
930 + $this->gallery_layout = $layout;
931 +
825 932 // Escape the array of IDs for SQL
826 933 $ids = array_map( 'intval', $image_ids );
827 934 $ids_str = implode( ',', $ids );
828 935
@@ -860,9 +967,12 @@
860 967 }
861 968 $ids = apply_filters( 'mgl_sort', $cleanIds, $images, $layout, $atts );
862 969
863 970 if ($layout === 'map') {
864 - return $this->get_map_images( $ids, $images, $atts );
971 + $map_result = $this->get_map_images( $ids, $images, $atts );
972 + $this->gallery_process = $previous_gallery_process;
973 + $this->gallery_layout = $previous_gallery_layout;
974 + return $map_result;
865 975 }
866 976
867 977 $result = [];
868 978 foreach ($ids as $index => $id) {
@@ -912,11 +1022,21 @@
912 1022 $mergedArray['featured_post_url'] = get_permalink( $post_id );
913 1023
914 1024 }
915 1025
916 - $result[] = array_merge( $image, $mergedArray, $orientation );
1026 + // Everything above can read the full attachment metadata, but it ends up in the page as
1027 + // data-gallery-images, and the front-end only needs the dimensions. Every size and the EXIF
1028 + // were bloating the HTML of big galleries (wp.org forum, 2026-09). The map layout returns
1029 + // earlier with its own data.
1030 + $item = array_merge( $image, $mergedArray, $orientation );
1031 + $meta = is_array( $image['meta'] ) ? $image['meta'] : [];
1032 + $item['meta'] = [ 'width' => $meta['width'] ?? null, 'height' => $meta['height'] ?? null ];
1033 + $result[] = $item;
917 1034 }
918 1035
1036 + $this->gallery_process = $previous_gallery_process;
1037 + $this->gallery_layout = $previous_gallery_layout;
1038 +
919 1039 return $result;
920 1040 }
921 1041
922 1042 private function get_image_class( $id, $layout, $noLightbox ) {
@@ -1125,8 +1245,60 @@
1125 1245 }
1126 1246 }
1127 1247
1128 1248
1249 + /**
1250 + * Reads the "medias" of a gallery. Only the ordered attachment IDs are stored (older versions
1251 + * also stored their URLs and mime types, which are ignored: they were a copy of the Media
1252 + * Library that went stale). This is also the shape that gets written back, and all the
1253 + * front-end needs. The Admin uses hydrate_medias() to get the URLs to display.
1254 + */
1255 + public static function normalize_medias( $medias ) {
1256 + $ids = ( is_array( $medias ) && isset( $medias['thumbnail_ids'] ) && is_array( $medias['thumbnail_ids'] ) )
1257 + ? array_values( $medias['thumbnail_ids'] ) : [];
1258 +
1259 + return [ 'thumbnail_ids' => $ids ];
1260 + }
1261 +
1262 + /**
1263 + * Adds the 'thumbnails' the Admin displays: one entry per ID, with its URLs and mime type
1264 + * resolved from the Media Library. Never stale, and a deleted attachment simply gets empty
1265 + * URLs (the Admin then shows a placeholder). Not used on the front-end, which only needs
1266 + * the IDs.
1267 + */
1268 + public static function hydrate_medias( $medias ) {
1269 + $ids = self::normalize_medias( $medias )['thumbnail_ids'];
1270 +
1271 + // One query for all the attachments instead of one per thumbnail. _prime_post_caches()
1272 + // only caches the attachments which exist, so anything still absent from the cache
1273 + // afterwards is gone: it's skipped instead of being queried on every request.
1274 + if ( !empty( $ids ) ) {
1275 + _prime_post_caches( array_values( array_unique( array_map( 'intval', $ids ) ) ), false, true );
1276 + }
1277 +
1278 + $thumbnails = [];
1279 + foreach ( $ids as $id ) {
1280 + $thumbnail = [ 'id' => $id, 'url' => '', 'zoom_url' => '', 'mime' => '' ];
1281 +
1282 + if ( !empty( $id ) && wp_cache_get( (int)$id, 'posts' ) ) {
1283 + $mime = get_post_mime_type( $id ) ?: '';
1284 + // Same rule as the latest_photos endpoint: videos have no image sizes, so their
1285 + // own URL is used for both the thumbnail and the zoom.
1286 + $is_video = strpos( $mime, 'video' ) !== false;
1287 + $url = $is_video ? wp_get_attachment_url( $id ) : wp_get_attachment_image_url( $id, 'thumbnail' );
1288 + $zoom = $is_video ? $url : wp_get_attachment_image_url( $id, 'large' );
1289 +
1290 + $thumbnail['url'] = $url ?: '';
1291 + $thumbnail['zoom_url'] = $zoom ?: $thumbnail['url'];
1292 + $thumbnail['mime'] = $mime;
1293 + }
1294 +
1295 + $thumbnails[] = $thumbnail;
1296 + }
1297 +
1298 + return [ 'thumbnail_ids' => $ids, 'thumbnails' => $thumbnails ];
1299 + }
1300 +
1129 1301 public function get_gallery_by_id( $id ) {
1130 1302 global $wpdb;
1131 1303 $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1132 1304 $gallery = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $shortcodes_table WHERE id = %s", $id ), ARRAY_A );
@@ -1133,9 +1305,9 @@
1133 1305
1134 1306 if ( !$gallery ) {
1135 1307 throw new Exception( __( 'Gallery not found.', MGL_DOMAIN ));
1136 1308 }
1137 - $gallery['medias'] = maybe_unserialize( $gallery['medias'] );
1309 + $gallery['medias'] = self::normalize_medias( maybe_unserialize( $gallery['medias'] ) );
1138 1310 $gallery['posts'] = $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null;
1139 1311 $gallery['tags'] = $gallery['tags'] ? unserialize( $gallery['tags'] ) : null;
1140 1312
1141 1313 return $gallery;
@@ -1152,9 +1324,9 @@
1152 1324 $galleries[$gallery['id']] = [
1153 1325 'name' => $gallery['name'],
1154 1326 'description' => $gallery['description'],
1155 1327 'layout' => $gallery['layout'],
1156 - 'medias' => maybe_unserialize( $gallery['medias'] ),
1328 + 'medias' => self::normalize_medias( maybe_unserialize( $gallery['medias'] ) ),
1157 1329 'lead_image_id' => $gallery['lead_image_id'],
1158 1330 'order_by' => $gallery['order_by'],
1159 1331 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1160 1332 'dynamic_source' => $gallery['dynamic_source'],
@@ -1161,8 +1333,10 @@
1161 1333 'hero' => ( bool )$gallery['is_hero_mode'],
1162 1334 'posts' => $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null,
1163 1335 'latest_posts' => $gallery['latest_posts'],
1164 1336 'tags' => $gallery['tags'] ? unserialize( $gallery['tags'] ) : null,
1337 + 'dynamic_source' => $gallery['dynamic_source'],
1338 + 'rml' => $gallery['rml'] ?? null,
1165 1339 'updated' => strtotime( $gallery['updated_at'] )
1166 1340 ];
1167 1341 }
1168 1342 return $galleries;
@@ -1210,9 +1384,9 @@
1210 1384 $shortcodes[$gallery['id']] = [
1211 1385 'name' => $gallery['name'],
1212 1386 'description' => $gallery['description'],
1213 1387 'layout' => $gallery['layout'],
1214 - 'medias' => maybe_unserialize( $gallery['medias'] ),
1388 + 'medias' => self::hydrate_medias( maybe_unserialize( $gallery['medias'] ) ),
1215 1389 'lead_image_id' => $gallery['lead_image_id'],
1216 1390 'order_by' => $gallery['order_by'],
1217 1391 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1218 1392 'hero' => ( bool )$gallery['is_hero_mode'],
@@ -1219,8 +1393,9 @@
1219 1393 'posts' => $gallery['posts'] ? maybe_unserialize( $gallery['posts'] ) : null,
1220 1394 'latest_posts' => $gallery['latest_posts'],
1221 1395 'tags' => $gallery['tags'] ? unserialize( $gallery['tags'] ) : null,
1222 1396 'dynamic_source' => $gallery['dynamic_source'],
1397 + 'rml' => $gallery['rml'] ?? null,
1223 1398 'rank' => intval( $gallery['pref_rank'] ?? 0 ),
1224 1399 'updated' => strtotime( $gallery['updated_at'] )
1225 1400 ];
1226 1401 }
@@ -1230,8 +1405,39 @@
1230 1405 'galleries' => $shortcodes
1231 1406 ];
1232 1407 }
1233 1408
1409 + /**
1410 + * Just the id => name pairs, for the selectors (the Gutenberg block). They only need the
1411 + * names, so this avoids shipping every gallery's medias in the page and, unlike
1412 + * get_galleries(), it isn't paginated: the selectors used to be capped at 10 entries.
1413 + */
1414 + public function get_gallery_names() {
1415 + global $wpdb;
1416 + $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
1417 + Meow_MGL_Migrations::check_db();
1418 +
1419 + $names = [];
1420 + $results = $wpdb->get_results( "SELECT id, name FROM $shortcodes_table ORDER BY name ASC", ARRAY_A );
1421 + foreach ( $results as $gallery ) {
1422 + $names[$gallery['id']] = [ 'name' => $gallery['name'] ];
1423 + }
1424 + return [ 'galleries' => $names ];
1425 + }
1426 +
1427 + public function get_collection_names() {
1428 + global $wpdb;
1429 + $collections_table = $wpdb->prefix . 'mgl_collections';
1430 + Meow_MGL_Migrations::check_db();
1431 +
1432 + $names = [];
1433 + $results = $wpdb->get_results( "SELECT id, name FROM $collections_table ORDER BY name ASC", ARRAY_A );
1434 + foreach ( $results as $collection ) {
1435 + $names[$collection['id']] = [ 'name' => $collection['name'] ];
1436 + }
1437 + return [ 'collections' => $names ];
1438 + }
1439 +
1234 1440 public function get_collection_by_id( $id ) {
1235 1441 global $wpdb;
1236 1442 $collections_table = $wpdb->prefix . 'mgl_collections';
1237 1443 $shortcodes_table = $wpdb->prefix . 'mgl_gallery_shortcodes';
@@ -1256,9 +1462,9 @@
1256 1462 'id' => $gallery['id'],
1257 1463 'name' => $gallery['name'],
1258 1464 'description' => $gallery['description'],
1259 1465 'layout' => $gallery['layout'],
1260 - 'medias' => unserialize( $gallery['medias'] ),
1466 + 'medias' => self::normalize_medias( maybe_unserialize( $gallery['medias'] ) ),
1261 1467 'lead_image_id' => $gallery['lead_image_id'],
1262 1468 'order_by' => $gallery['order_by'],
1263 1469 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1264 1470 'hero' => ( bool )$gallery['is_hero_mode'],
@@ -1318,9 +1524,9 @@
1318 1524 'id' => $gallery['id'],
1319 1525 'name' => $gallery['name'],
1320 1526 'description' => $gallery['description'],
1321 1527 'layout' => $gallery['layout'],
1322 - 'medias' => unserialize( $gallery['medias'] ),
1528 + 'medias' => self::hydrate_medias( maybe_unserialize( $gallery['medias'] ) ),
1323 1529 'lead_image_id' => $gallery['lead_image_id'],
1324 1530 'order_by' => $gallery['order_by'],
1325 1531 'is_post_mode' => ( bool )$gallery['is_post_mode'],
1326 1532 'hero' => ( bool )$gallery['is_hero_mode'],