PluginProbe
Image Source Control Lite – Show Image Credits and Captions / trunk
Image Source Control Lite – Show Image Credits and Captions vtrunk
3.12.0 3.11.0 trunk 1.1 1.1.1 1.1.2 1.1.2.1 1.1.3 1.10 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.2 1.2.0.1 1.2.0.2 1.2.0.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.4.1 1.3.5 All 110 releases
image-source-control-isc / includes / model.php

model.php in Image Source Control Lite – Show Image Credits and Captions trunk, at includes/model.php

697 lines 21.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use ISC\Image_Sources\Image_Sources;
4 use ISC\Image_Sources\Post_Meta;
5 use ISC\indexer;
6
7 /**
8 * Logic to get and store sources
9 */
10 class ISC_Model {
11 /**
12 * Instance of ISC_Model
13 *
14 * @var ISC_Model
15 */
16 protected static $instance;
17
18 /**
19 * Maximum number of entries in custom queries
20 */
21 const MAX_POSTS = 100;
22
23 /**
24 * Setup registers filters and actions.
25 */
26 public function __construct() {
27 add_filter( 'attachment_fields_to_save', [ $this, 'isc_fields_save' ], 10, 2 );
28 }
29
30 /**
31 * Get instance of ISC_Model
32 *
33 * @return ISC_Model
34 */
35 public static function get_instance() {
36 return self::$instance;
37 }
38
39 /**
40 * Update the isc_image_posts and isc_post_images indexes
41 *
42 * @depecated since October 2024
43 *
44 * @param integer $post_id ID of the target post.
45 * @param string $content content of the target post.
46 */
47 public static function update_indexes( $post_id, $content ) {
48
49 ISC_Log::log( 'function removed. Use \ISC\Indexer::update_indexes' );
50
51 ISC\Indexer::update_indexes( $content );
52 }
53
54 /**
55 * Update isc_image_posts meta field with includes IDs of all posts that have the image in its content
56 * the function should be used to push a post ID to the (maybe) existing meta field
57 *
58 * @removed since April 2025
59 *
60 * @param integer $post_id ID of the target post.
61 * @param array $image_ids IDs of the attachments in the content.
62 */
63 public static function update_image_posts_meta( $post_id, $image_ids ) {
64
65 ISC_Log::log( 'function removed. \ISC\Indexer::update_indexes covers most of it now' );
66 }
67
68 /**
69 * Save image source to post_meta
70 * Used as a filter function. See save_field() it you are looking for a direct method to store post meta values
71 *
72 * @updated 1.5 added field for url
73 *
74 * @param array $post post data.
75 * @param array $attachment attachment data.
76 * @return array $post updated post data
77 */
78 public function isc_fields_save( $post, $attachment ) {
79 if ( isset( $attachment['isc_image_source'] ) ) {
80 self::save_field( $post['ID'], 'isc_image_source', Image_Sources::sanitize_source_html( $attachment['isc_image_source'] ) );
81 }
82 if ( isset( $attachment['isc_image_source_url'] ) ) {
83 self::save_field( $post['ID'], 'isc_image_source_url', $this->sanitize_source_url( $attachment['isc_image_source_url'] ) );
84 }
85 $own = ( isset( $attachment['isc_image_source_own'] ) ) ? $attachment['isc_image_source_own'] : '';
86 self::save_field( $post['ID'], 'isc_image_source_own', $own );
87
88 if ( isset( $attachment['isc_ai_label'] ) ) {
89 self::save_field( $post['ID'], \ISC\Image_Sources\Ai_Labels::META_KEY, \ISC\Image_Sources\Ai_Labels::sanitize_label( $attachment['isc_ai_label'] ) );
90 }
91
92 if ( isset( $attachment['isc_image_licence'] ) ) {
93 self::save_field( $post['ID'], 'isc_image_licence', sanitize_text_field( $attachment['isc_image_licence'] ) );
94 }
95
96 return $post;
97 }
98
99 /**
100 * Sanitize source URL string by removing any HTML tags
101 *
102 * @param string $source_url source URL string.
103 *
104 * @return string sanitized source URL
105 */
106 public function sanitize_source_url( string $source_url ): string {
107 return wp_kses( $source_url, [] );
108 }
109
110 /**
111 * Store attachment-related post meta values
112 *
113 * @param int $att_id WP_Post ID of the attachment.
114 * @param string $key post meta key.
115 * @param mixed $value post meta value.
116 */
117 public static function save_field( $att_id, $key, $value ) {
118 if ( is_string( $value ) ) {
119 $value = trim( $value );
120 }
121
122 self::update_post_meta( $att_id, $key, $value );
123 }
124
125 /**
126 * Update image-post index attached to attachments when a post is updated
127 *
128 * @removed since April 2025
129 *
130 * @param int $post_ID Post ID.
131 * @param WP_Post $post_after Post object following the update.
132 * @param WP_Post $post_before Post object before the update.
133 *
134 * @return void
135 */
136 public static function update_image_post_meta( $post_ID, $post_after, $post_before ) {
137 ISC_Log::log( 'function removed without a replacement.' );
138 }
139
140 /**
141 * Remove the post_images index from a single post
142 * namely the post meta field `isc_post_images`
143 *
144 * @moved since April 2025
145 *
146 * @param int $post_id Post ID.
147 */
148 public static function clear_single_post_images_index( $post_id ) {
149 ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Meta\Post_Images_Meta::delete' );
150
151 Post_Meta\Post_Images_Meta::delete( $post_id );
152 }
153
154 /**
155 * Remove post_images index
156 * namely the post meta field `isc_post_images`
157 *
158 * @moved since April 2025
159 *
160 * @return int|false The number of rows updated, or false on error.
161 */
162 public static function clear_post_images_index() {
163 ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Images_Meta::delete_all' );
164
165 return Post_Meta\Post_Images_Meta::delete_all();
166 }
167
168 /**
169 * Remove image_posts index
170 * namely the post meta field `isc_image_posts`
171 *
172 * @moved since April 2025
173 *
174 * @return int|false The number of rows updated, or false on error.
175 */
176 public static function clear_image_posts_index() {
177 ISC_Log::log( 'function moved. \ISC\Image_Sources\Image_Posts_Meta::delete_all' );
178
179 return Post_Meta\Image_Posts_Meta::delete_all();
180 }
181
182 /**
183 * Remove all image-post relations
184 * this concerns the post meta fields `isc_image_posts` and `isc_post_images`
185 *
186 * @deprecated since April 2025
187 *
188 * @return bool True if the option was removed
189 */
190 public static function clear_index(): bool {
191 ISC_Log::log( 'function moved. \ISC\Indexer::clear_index' );
192
193 return Indexer::clear_index();
194 }
195
196 /**
197 * Checks if there are image with missing sources
198 * this includes attachments that were not indexed yet (don’t have the appropriate meta values)
199 *
200 * @return int number of images with missing sources
201 */
202 public static function count_missing_sources(): int {
203
204 // get known and used attachments without sources
205 return count( self::get_attachments_with_empty_sources() );
206 }
207
208 /**
209 * Get attachments.
210 * Allows to be called with custom arguments.
211 *
212 * @param array $args arguments for the query.
213 * @return array with attachments. Returns all attachments in the Media Library if called without additional arguments.
214 */
215 public static function get_attachments( $args ) {
216 $args = wp_parse_args(
217 $args,
218 [
219 'post_type' => 'attachment',
220 'numberposts' => -1,
221 'post_status' => null,
222 'post_parent' => null,
223 ]
224 );
225
226 return get_posts( $args );
227 }
228
229 /**
230 * Get all attachments with empty sources options.
231 *
232 * @return array with attachments.
233 */
234 public static function get_attachments_with_empty_sources() {
235 global $wpdb;
236
237 $where_clause = "wp_posts.post_type = 'attachment' AND wp_posts.post_status = 'inherit'";
238
239 // Add image type check if images_only is enabled
240 if ( \ISC\Media_Type_Checker::enabled_images_only_option() ) {
241 $where_clause .= " AND wp_posts.post_mime_type LIKE 'image/%'";
242 }
243
244 /**
245 * Filters the WHERE clause for the query that finds attachments with empty sources.
246 *
247 * @param string $where_clause The WHERE clause conditions for the attachments query.
248 * @return string The filtered WHERE clause.
249 */
250 $where_clause = apply_filters( 'isc_image_sources_attachments_with_empty_sources_where_clause', $where_clause );
251
252 /**
253 * Using EXISTS instead of LEFT JOINs resulted in much faster queries and helped caching the results.
254 */
255 $query = "SELECT wp_posts.ID, wp_posts.post_title, wp_posts.post_parent
256 FROM {$wpdb->posts} AS wp_posts
257 WHERE {$where_clause}
258 AND NOT EXISTS (
259 SELECT 1
260 FROM {$wpdb->postmeta} AS mt1
261 WHERE mt1.post_id = wp_posts.ID
262 AND mt1.meta_key = 'isc_image_source_own'
263 AND mt1.meta_value = '1'
264 )
265 AND NOT EXISTS (
266 SELECT 1
267 FROM {$wpdb->postmeta} AS mt_ai
268 WHERE mt_ai.post_id = wp_posts.ID
269 AND mt_ai.meta_key = 'isc_ai_label'
270 AND mt_ai.meta_value IN ('ai', 'ai-modified', 'ai-generated')
271 )
272 AND (
273 EXISTS (
274 SELECT 1
275 FROM {$wpdb->postmeta} AS wp_postmeta
276 WHERE wp_postmeta.post_id = wp_posts.ID
277 AND wp_postmeta.meta_key = 'isc_image_source'
278 AND wp_postmeta.meta_value = ''
279 )
280 OR NOT EXISTS (
281 SELECT 1
282 FROM {$wpdb->postmeta} AS mt2
283 WHERE mt2.post_id = wp_posts.ID
284 AND mt2.meta_key = 'isc_image_source'
285 )
286 )
287 ORDER BY wp_posts.ID DESC
288 LIMIT %d, %d
289 ";
290
291 // The result of the query is already cached for a day, or until an image is added or removed.
292 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
293 $attachments = $wpdb->get_results( $wpdb->prepare( $query, 0, self::MAX_POSTS ) );
294
295 $count = count( $attachments );
296
297 // update the transient if the number of results differ
298 if ( $count !== (int) get_transient( 'isc-show-missing-sources-warning' ) ) {
299 set_transient( 'isc-show-missing-sources-warning', $count, DAY_IN_SECONDS );
300 }
301
302 return $attachments;
303 }
304
305 /**
306 * Update the transient we set for missing sources to not check them for another day
307 * running each time we are writing the `isc_image_source` post meta key
308 *
309 * @return int number of missing sources.
310 */
311 public static function update_missing_sources_transient() {
312 $missing_sources = self::count_missing_sources();
313 set_transient( 'isc-show-missing-sources-warning', $missing_sources, DAY_IN_SECONDS );
314 return $missing_sources;
315 }
316
317 /**
318 * Filter image ids from content
319 *
320 * @param string $content post content.
321 * @return string[] with image ids => image src uri-s
322 */
323 public static function filter_image_ids( $content = '' ) {
324 $srcs = [];
325
326 ISC_Log::log( 'enter looking for image IDs within the content' );
327
328 if ( empty( $content ) ) {
329 ISC_Log::log( 'exit due to missing content' );
330 return $srcs;
331 }
332
333 // parse HTML with DOM
334 $dom = new DOMDocument();
335
336 libxml_use_internal_errors( true );
337 if ( version_compare( PHP_VERSION, '8.2', '<' ) && function_exists( 'mb_convert_encoding' ) ) {
338 $content = mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' );
339 }
340 $dom->loadHTML( $content );
341
342 // Prevents from sending E_WARNINGs notice (Outputs are forbidden during activation)
343 libxml_clear_errors();
344
345 /**
346 * Handle multiple tags at once
347 * the original use case is checking AMP pages generated in reader mode in the AMP plugin and in AMPforWP
348 * for IMG and AMP-IMG tags
349 */
350 $tags = apply_filters( 'isc_filter_image_ids_tags', [ 'img' ] );
351
352 if ( ! is_array( $tags ) ) {
353 return [];
354 }
355
356 // I am keeping the original $dom->getElementsByTagName as well as the new DOMXpath solution for multiple elements for now
357 // since I am not 100% sure about the implications of the latter on existing features
358 if ( count( $tags ) === 1 ) {
359 $nodes = $dom->getElementsByTagName( 'img' );
360 } else {
361 $xpath = new DOMXpath( $dom );
362 $tags_string = '//' . implode( '|//', $tags );
363 $nodes = $xpath->query( $tags_string );
364 }
365
366 $nodes_count = count( $nodes );
367
368 ISC_Log::log( sprintf( 'found %d img tags', $nodes_count ) );
369
370 if ( ISC_Log::is_type( 'content' ) ) {
371 ISC_Log::log( sprintf( 'looked in content: %s', $content ) );
372 }
373
374 foreach ( $nodes as $node ) {
375 if ( isset( $node->attributes ) ) {
376 $matched = false;
377 if ( $node->attributes->getNamedItem( 'class' ) !== null ) {
378 ISC_Log::log( sprintf( 'found class attribute "%s"', $node->attributes->getNamedItem( 'class' )->textContent ) );
379
380 if ( preg_match( '#.*wp-image-(\d+?).*#U', $node->attributes->getNamedItem( 'class' )->textContent, $matches ) ) {
381 // check if a src attribute exists
382 if ( $node->attributes->getNamedItem( 'src' ) !== null ) {
383 $srcs[ intval( $matches[1] ) ] = $node->attributes->getNamedItem( 'src' )->textContent;
384 $matched = true;
385
386 ISC_Log::log( sprintf( 'found image ID "%d" with src "%s"', intval( $matches[1] ), $srcs[ intval( $matches[1] ) ] ) );
387 } else {
388 ISC_Log::log( sprintf( 'no src attribute found for image ID "%d"', intval( $matches[1] ) ) );
389 }
390 }
391 }
392 if ( ! $matched ) {
393 if ( $node->attributes->getNamedItem( 'src' ) !== null ) {
394 $url = $node->attributes->getNamedItem( 'src' )->textContent;
395 ISC_Log::log( sprintf( 'found src "%s"', $url ) );
396 // get ID of images by url
397 $id = self::get_image_by_url( $url );
398 if ( $id ) {
399 $srcs[ $id ] = $url;
400 }
401 }
402 }
403 }
404 }
405
406 /**
407 * Filter image IDs found in HTML content, or add new ones based on other rules.
408 *
409 * @since 2.9.0
410 *
411 * @param string[] $srcs image sources with image ids => image src uri
412 * @param string $content any HTML document
413 */
414 return apply_filters( 'isc_filter_image_ids_from_content', $srcs, $content );
415 }
416
417 /**
418 * Get image ID by URL accessing the database directly
419 *
420 * @param string $url URL of the image.
421 *
422 * @return integer ID of the image.
423 */
424 public static function get_image_by_url( string $url = '' ): ?int {
425 global $wpdb;
426
427 ISC_Log::log( 'enter get_image_by_url() to look for URL ' . $url );
428
429 $original_url = $url;
430 $url = apply_filters( 'isc_filter_url_pre_get_image_by_url', $url );
431
432 // replace certain characters that WordPress accepts in file names only to test if the URL is valid
433 $url_to_check = esc_url( $url, [ 'http', 'https' ] );
434
435 if ( empty( $url ) || $url_to_check !== $url ) {
436 return 0;
437 }
438
439 // ignore src strings with more than 1000 characters; these could be base24 images not hosted in WordPress
440 if ( strlen( $url ) > 1000 ) {
441 ISC_Log::log( 'exit due to URL length' );
442 return 0;
443 }
444
445 // get the file extension, e.g. "jpg"
446 $ext = pathinfo( $url, PATHINFO_EXTENSION );
447 if ( ! $ext ) {
448 ISC_Log::log( 'exit get_image_by_url() no extension found' );
449 if ( apply_filters( 'isc_allow_empty_file_extension', __return_false() ) ) {
450 ISC_Log::log( "get_image_by_url() didn’t find an extension for $url but continues." );
451 } else {
452 return 0;
453 }
454 } elseif ( ! in_array( $ext, Image_Sources::get_allowed_extensions(), true ) ) {
455 // a valid image extension is required, if an extension is given
456 ISC_Log::log( 'exit get_image_by_url() due to invalid image extension' );
457 return 0;
458 }
459
460 /**
461 * Check for the format 'image-title-(e12452112-)300x200.jpg(?query…)' and removes
462 * - the image size
463 * - edit marks
464 * - "scaled" or "rotated"
465 * - additional query vars
466 */
467 // this is how WordPress core is detecting changed image URLs
468 $newurl = esc_url( preg_replace( "/-(?:\d+x\d+|scaled|rotated)\.{$ext}(.*)/i", '.' . $ext, $url ) );
469 $storage = new ISC_Storage_Model();
470
471 // check if the URL is already in storage and if so, take it from there
472 if ( $storage->is_image_url_in_storage( $newurl ) ) {
473 $id_from_storage = absint( $storage->get_image_id_from_storage( $newurl ) );
474 ISC_Log::log( "found $newurl in storage with attachment ID $id_from_storage" );
475 return $id_from_storage;
476 }
477
478 /**
479 * Attachment_url_to_postid needs the URL including protocol, but cannot handle sizes, so it needs to be at exactly this position
480 * this function finds images based on the _wp_attached_file post meta value that includes the image path followed after the upload dir
481 * it therefore also works when the domain changed
482 * since _wp_attached_file contains "scaled" or "rotated" as well, we keep them, but only remove sizes
483 */
484 $id = attachment_url_to_postid( esc_url( preg_replace( "/-\d+x\d+\.{$ext}(.*)/i", '.' . $ext, $url ) ) );
485 if ( $id ) {
486 // store attachment ID in storage
487 $storage->update_post_id( $newurl, $id );
488 ISC_Log::log( '_attachment_url_to_postid found image ID ' . $id );
489 return $id;
490 }
491
492 // remove protocol (http or https)
493 $url = str_ireplace( [ 'http:', 'https:' ], '', $url );
494 $newurl = str_ireplace( [ 'http:', 'https:' ], '', $newurl );
495
496 // gather different URLs formats
497 $urls = [
498 'http:' . $url,
499 'https:' . $url,
500 'http:' . $newurl,
501 'https:' . $newurl,
502 ];
503
504 // remove duplicates
505 $urls = array_unique( $urls );
506
507 $url_queries = [];
508 foreach ( $urls as $_url ) {
509 // return if any of the URLs is already in storage
510 if ( $storage->is_image_url_in_storage( $_url ) ) {
511 $id_from_storage = absint( $storage->get_image_id_from_storage( $_url ) );
512 ISC_Log::log( "found $newurl in storage with attachment ID $id_from_storage" );
513 return $id_from_storage;
514 }
515
516 $url_queries[] = 'guid = "' . esc_url( $_url ) . '"';
517 }
518 $url_query_string = implode( ' OR ', $url_queries );
519 ISC_Log::log( sprintf( 'SQL query looking for anything with %s', implode( ', ', array_unique( [ $url, $newurl ] ) ) ) );
520
521 // not escaped, because escaping already happened above
522 $raw_query = "SELECT ID, guid FROM `$wpdb->posts` WHERE post_type='attachment' AND {$url_query_string} LIMIT 1";
523
524 $query = apply_filters( 'isc_get_image_by_url_query', $raw_query, $newurl );
525 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
526 $results = $wpdb->get_results( $query );
527
528 $id = isset( $results[0]->ID ) ? absint( $results[0]->ID ) : 0;
529 $guid = $results[0]->guid ?? null;
530
531 /**
532 * Filter the image ID found by ISC
533 *
534 * Especially useful for compatibility with plugins that manipulate the image URL or meta data so that our checks don’t work
535 *
536 * @param int $id attachment ID or null
537 * @param array $params array with additional, optional parameters
538 */
539 $id = apply_filters(
540 'isc_filter_final_id_get_image_by_url',
541 $id,
542 [
543 'original_url' => $original_url,
544 'newurl' => $newurl,
545 'url' => $url,
546 ]
547 );
548
549 if ( $id ) {
550 // if no $guid is found, we store one of the earlier URLs
551 $storage->update_post_id( $guid ?? $url, $id );
552 ISC_Log::log( 'found image ID ' . $id );
553 } else {
554 // this should ideally only apply to image URLs that are not in the media library
555 // ISC also stores the URL to prevent too many database requests
556 // using $newurl, because it is already stripped by potential parameters and stuff
557 $storage->update( $newurl, [ 'post_id' => null ] );
558 ISC_Log::log( 'no image ID found' );
559 }
560
561 return $id;
562 }
563
564 /**
565 * Store post meta information.
566 * Use this function instead of core’s update_post_meta() to allow debugging
567 *
568 * @param int $post_id post ID of the attachment.
569 * @param string $key post meta key.
570 * @param mixed $value value of the post meta information.
571 *
572 * @return bool|int
573 */
574 public static function update_post_meta( int $post_id, string $key, $value ) {
575 /**
576 * Run when any post meta information is stored by ISC
577 *
578 * @since 2.9.0
579 *
580 * @param int $post_id post ID of the attachment.
581 * @param string $key post meta key.
582 * @param mixed $value value of the post meta information.
583 */
584 do_action( 'isc_update_post_meta', $post_id, $key, $value );
585
586 return update_post_meta( $post_id, $key, $value );
587 }
588
589 /**
590 * Get an array of all posts that have the isc_post_images meta field set
591 * and the number of them not having it
592 * list each post type separately
593 *
594 * @return array
595 */
596 public static function get_posts_with_image_index(): array {
597 $post_types = get_post_types( [ 'public' => true ] );
598 // remove the attachment post type
599 $post_types = array_diff( $post_types, [ 'attachment' ] );
600 $results = [];
601
602 foreach ( $post_types as $post_type ) {
603 $count_posts = wp_count_posts( $post_type );
604 $total_posts = $count_posts->publish;
605
606 $args = [
607 'post_type' => $post_type,
608 'posts_per_page' => self::MAX_POSTS,
609 'post_status' => 'publish',
610 'fields' => 'ids',
611 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
612 'meta_query' => [
613 [
614 'key' => 'isc_post_images',
615 'compare' => 'NOT EXISTS',
616 ],
617 ],
618 ];
619 $query_without = new WP_Query( $args );
620 $posts_without_meta_field = $query_without->post_count;
621
622 $results[ $post_type ] = [
623 'total_posts' => $total_posts,
624 'without_meta_field' => $posts_without_meta_field,
625 'with_meta_field' => $total_posts - $posts_without_meta_field,
626 ];
627 }
628
629 return $results;
630 }
631
632 /**
633 * Get the attachment’s file URL from the database
634 *
635 * @param int $image_id Attachment ID.
636 *
637 * @return string
638 */
639 public function get_base_file_url( int $image_id ): string {
640 // load the attachment post
641 $file = get_post_meta( $image_id, '_wp_attached_file', true );
642 // get guid as fallback, e.g., _wp_attached_file can be empty for external images
643 if ( empty( $file ) ) {
644 $attachment = get_post( $image_id );
645 $file = $attachment->guid;
646 }
647
648 return $file;
649 }
650
651 /**
652 * Remove all plugin meta from non-image attachments
653 *
654 * @return void
655 */
656 public static function remove_plugin_meta_from_non_images() {
657 // The meta keys used by ISC that should be removed from non-images
658 $isc_meta_keys = [
659 'isc_image_source',
660 'isc_image_source_url',
661 'isc_image_license',
662 'isc_image_source_own',
663 \ISC\Image_Sources\Ai_Labels::META_KEY,
664 'isc_image_posts',
665 'isc_possible_usages',
666 'isc_possible_usages_last_check',
667 ];
668
669 /**
670 * Filter the meta keys to be removed from non-image attachments
671 *
672 * @param array $isc_meta_keys The meta keys to be removed from non-image attachments.
673 */
674 apply_filters( 'isc_cleanup_non_image_meta_keys', $isc_meta_keys );
675
676 $attachments = get_posts(
677 [
678 'post_type' => 'attachment',
679 'post_status' => 'inherit',
680 'posts_per_page' => -1,
681 ]
682 );
683
684 if ( empty( $attachments ) ) {
685 return;
686 }
687
688 foreach ( $attachments as $attachment ) {
689 if ( ! \ISC\Media_Type_Checker::is_image( $attachment ) ) {
690 foreach ( $isc_meta_keys as $meta_key ) {
691 delete_post_meta( $attachment->ID, $meta_key );
692 }
693 }
694 }
695 }
696 }
697