Plugin Directory

Changeset 3283881


Ignore:
Timestamp:
04/29/2025 07:56:28 AM (10 months ago)
Author:
webzunft
Message:

version 3.2.0

Location:
image-source-control-isc
Files:
12 added
2 deleted
34 edited
8 copied

Legend:

Unmodified
Added
Removed
  • image-source-control-isc/tags/3.2.0/admin/assets/js/settings.js

    r3253182 r3283881  
    1818        $( '#isc-settings-caption-style input' ).on( 'change', function(){ isc_toggle_caption_position(); } );
    1919        $( '#isc-settings-global-list-indexed-images' ).on( 'change', function(){ isc_show_reindex_warning(); } );
    20         $('#isc-settings-plugin-modules input[type="checkbox"]').on('change', function() { isc_toggle_module_sections(); });
     20        $( '#isc-settings-plugin-modules input[type="checkbox"]').on('change', function() { isc_toggle_module_sections(); });
     21        $( '#isc-settings-plugin-images-only' ).on( 'change', function() {
     22            const enabled = this.checked;
     23            $( '#isc-settings-plugin-images-only-indexer' ).toggleClass( 'hidden' );
     24            $( '#isc-settings-plugin-images-only-cleanup-wrapper' ).toggleClass( 'hidden', !enabled );
     25        });
    2126
    2227        // Show and update preview when a position option is clicked
  • image-source-control-isc/tags/3.2.0/admin/assets/js/sources.js

    r3101157 r3283881  
    102102            }
    103103        );
     104        // show frontend storage
     105        $( '#isc-show-storage' ).on(
     106            'click',
     107            function(){
     108                // disable the button
     109                var button      = this;
     110                button.disabled = true;
     111
     112                $.ajax(
     113                    {
     114                        type: 'POST',
     115                        url: ajaxurl,
     116                        data: {
     117                            action: 'isc-show-storage',
     118                            nonce: isc.ajaxNonce,
     119                        },
     120                        success:function(data, textStatus, XMLHttpRequest){
     121                            // display return messages
     122                            $( '#isc-show-storage-output' ).html( data );
     123                            button.disabled = false;
     124                        },
     125                        error: function(MLHttpRequest, textStatus, errorThrown){
     126                            $( '#isc-show-storage-output' ).html( errorThrown );
     127                            button.disabled = false;
     128                        }
     129                    }
     130                );
     131            }
     132        );
    104133        // clear frontend storage
    105134        $( '#isc-clear-storage' ).on(
  • image-source-control-isc/tags/3.2.0/admin/templates/sources/storage.php

    r3253182 r3283881  
    2222<button id="isc-clear-storage" class="button button-secondary"><?php esc_html_e( 'clear storage', 'image-source-control-isc' ); ?></button>
    2323<div id="isc-clear-storage-feedback"></div>
     24<?php
     25// if the WordPress debug mode is enabled, show the button to dump the storage
     26if ( $storage_size > 0 && defined( 'WP_DEBUG' ) && WP_DEBUG ) :
     27    ?>
     28    <br/>
     29    <button id="isc-show-storage" class="button button-secondary"><?php esc_html_e( 'show storage', 'image-source-control-isc' ); ?></button>
     30    <pre id="isc-show-storage-output"></pre>
     31    <?php
     32endif; ?>
  • image-source-control-isc/tags/3.2.0/includes/image-sources/admin.php

    r3254673 r3283881  
    66 * Admin features for image sources
    77 */
    8 class Admin {
    9     use \ISC\Options;
    10 
     8class Admin extends Image_Sources {
    119    /**
    1210     * Initiate admin functions
    1311     */
    1412    public function __construct() {
     13        parent::__construct();
     14
    1515        // load components
    1616        new Image_Sources_Admin_Scripts();
  • image-source-control-isc/tags/3.2.0/includes/image-sources/admin/ajax.php

    r3253182 r3283881  
    33namespace ISC\Image_Sources;
    44
    5 use ISC_Model;
     5use ISC\Image_Sources\Post_Meta\Image_Posts_Meta;
     6use ISC\Image_Sources\Post_Meta\Post_Images_Meta;
    67
    78/**
     
    1819        add_action( 'wp_ajax_isc-image-post-relations', [ $this, 'list_image_post_relations' ] );
    1920        add_action( 'wp_ajax_isc-clear-index', [ $this, 'clear_index' ] );
     21        add_action( 'wp_ajax_isc-show-storage', [ $this, 'show_storage' ] );
    2022        add_action( 'wp_ajax_isc-clear-storage', [ $this, 'clear_storage' ] );
    2123        add_action( 'wp_ajax_isc-clear-image-posts-index', [ $this, 'clear_image_posts_index' ] );
     
    98100        }
    99101
    100         die(
    101             sprintf(
    102             // translators: %d is the number of deleted entries
    103                 esc_html__( '%d entries deleted', 'image-source-control-isc' ),
    104                 (int) ISC_Model::clear_index()
    105             )
    106         );
     102        if ( \ISC\Indexer::clear_index() ) {
     103            wp_send_json_success( esc_html__( 'Index cleared', 'image-source-control-isc' ) );
     104        } else {
     105            wp_send_json_error( 'Error' );
     106        }
     107    }
     108
     109    /**
     110     * Show the storage array for debugging
     111     */
     112    public function show_storage() {
     113        check_ajax_referer( 'isc-admin-ajax-nonce', 'nonce' );
     114
     115        if ( ! current_user_can( 'manage_options' ) ) {
     116            die( 'Wrong capabilities' );
     117        }
     118
     119        $storage_model = new \ISC_Storage_Model();
     120        $images        = $storage_model->get_storage();
     121
     122        // We are in debug mode, so it is fine to just output the content
     123        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     124        print_r( $images );
     125
     126        die();
    107127    }
    108128
     
    137157
    138158        $image_id = (int) $_POST['image_id'];
    139         delete_post_meta( $image_id, 'isc_image_posts' );
     159        $deleted  = Image_Posts_Meta::delete( $image_id );
    140160
    141         die( 'Image-Posts index cleared' );
     161        if ( $deleted ) {
     162            wp_send_json_success( 'Image-Posts index cleared' );
     163        } else {
     164            wp_send_json_success( 'Image-Posts index cleared (or did not exist)' );
     165        }
    142166    }
    143167
     
    157181
    158182        $post_id = (int) $_POST['post_id'];
    159         delete_post_meta( $post_id, 'isc_post_images' );
     183        $deleted = Post_Images_Meta::delete( $post_id );
    160184
    161         die( 'Post-Images index cleared' );
     185        if ( $deleted ) {
     186            wp_send_json_success( 'Post-Images index cleared' );
     187        } else {
     188            wp_send_json_success( 'Post-Images index cleared (or did not exist)' );
     189        }
    162190    }
    163191}
  • image-source-control-isc/tags/3.2.0/includes/image-sources/admin/fields.php

    r3253281 r3283881  
    2323     * Add custom field to attachment
    2424     *
    25      * @param array  $form_fields field fields.
    26      * @param object $post        post object.
     25     * @param array    $form_fields field fields.
     26     * @param \WP_Post $post        post object.
    2727     *
    2828     * @return array with form fields
    2929     */
    3030    public function add_isc_fields( $form_fields, $post ) {
     31        // Check if we should process this attachment based on settings
     32        if ( ! \ISC\Media_Type_Checker::should_process_attachment( $post ) ) {
     33            return $form_fields;
     34        }
     35
    3136        /**
    3237         * Return, when the ISC fields are enabled for blocks, and we are not using the block editor.
  • image-source-control-isc/tags/3.2.0/includes/image-sources/admin/media-library-filters.php

    r3256706 r3283881  
    4545     */
    4646    public function filter_media_library( \WP_Query $query ) {
    47         Admin_Utils::is_media_library_list_view_page();
     47        if ( ! Admin_Utils::is_media_library_list_view_page() ) {
     48            return;
     49        }
    4850
    4951        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    5052        $filter = isset( $_GET['isc_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['isc_filter'] ) ) : '';
     53
     54        // Add image type check if images_only is enabled
     55        if ( \ISC\Media_Type_Checker::enabled_images_only_option() ) {
     56            $query->set( 'post_mime_type', 'image%' );
     57        }
    5158
    5259        if ( $filter === 'with_source' ) {
  • image-source-control-isc/tags/3.2.0/includes/image-sources/analyze-html.php

    r3253281 r3283881  
    120120    /**
    121121     * Extract image URLs from HTML.
    122      * One image URL per HTML tag will be found.
     122     * One image URL per HTML attribute will be found.
     123     *
     124     * Limitations:
     125     * - retrieves the first valid image URL from any HTML tag
     126     * - if an IMG tag has a SRC attribute with a valid image URL, all other tags with valid URLs are ignored
     127     * - technically, one could generate tags with different images from the Media Library, e.g., having a differently cut URL in a data-attribute
     128     *  that then shows dynamically using JavaScript
     129     *  this would cause one of the images not being listed with a source or a used image
     130     *  since I haven’t seen that in the wild, this case is deliberately ignored
    123131     *
    124132     * @param string $html Any HTML code.
  • image-source-control-isc/tags/3.2.0/includes/image-sources/image-sources.php

    r3253182 r3283881  
    1010 */
    1111class Image_Sources {
    12     /**
    13      * Define default meta fields
    14      *
    15      * @var array option fields.
    16      */
    17     protected $fields = [
    18         'image_source'     => [
    19             'id'      => 'isc_image_source',
    20             'default' => '',
    21         ],
    22         'image_source_url' => [
    23             'id'      => 'isc_image_source_url',
    24             'default' => '',
    25         ],
    26         'image_source_own' => [
    27             'id'      => 'isc_image_source_own',
    28             'default' => '',
    29         ],
    30         'image_posts'      => [
    31             'id'      => 'isc_image_posts',
    32             'default' => [],
    33         ],
    34         'image_licence'    => [
    35             'id'      => 'isc_image_licence',
    36             'default' => '',
    37         ],
    38     ];
    39 
    4012    /**
    4113     * Allowed image file types/extensions
     
    129101
    130102        /**
    131          * Clear post-image index whenever the content of a single post is updated
     103         * Update index when a post is deleted or moved into trash
     104         */
     105        add_action( 'before_delete_post', [ '\ISC\Indexer', 'handle_post_deletion' ] );
     106        add_action( 'wp_trash_post', [ '\ISC\Indexer', 'handle_post_deletion' ] );
     107
     108        /**
     109         * Clear post-image index whenever the content of a single post is updated and move the content to a temporary post meta
    132110         * this could force reindexing the post after adding or removing image sources
    133111         */
    134         add_action( 'wp_insert_post', [ 'ISC_Model', 'clear_single_post_images_index' ] );
    135 
    136         /**
    137          * Fire when a post or page was updated
    138          */
    139         add_action( 'post_updated', [ 'ISC_Model', 'update_image_post_meta' ], 10, 3 );
     112        add_action( 'wp_insert_post', [ '\ISC\Indexer', 'prepare_for_reindex' ] );
    140113    }
    141114
  • image-source-control-isc/tags/3.2.0/includes/indexer.php

    r3253182 r3283881  
    33namespace ISC;
    44
     5use ISC\Image_Sources\Post_Meta\Image_Posts_Meta;
     6use ISC\Image_Sources\Post_Meta\Post_Images_Meta;
    57use ISC_Log, ISC_Model;
    68
     
    1012class Indexer {
    1113
    12     /**
    13      * Handle index updates
    14      *
    15      * @param string $content content of the target post.
     14    const BEFORE_UPDATE_META_KEY = 'isc_post_images_before_update';
     15
     16    /**
     17     * Handle index updates on frontend visit after a post save.
     18     * Compares pre-save state with current render and updates indexes.
     19     *
     20     * @param string $content Rendered content of the target post.
    1621     *
    1722     * @return void
     
    2631
    2732        // Skip indexing if this is a page with a Global list.
    28         if ( has_shortcode( $content, '[isc_list_all]' ) || false !== strpos( $content, 'isc_all_image_list_box' ) ) {
    29             // an empty isc_post_images meta value is used to indicate that the post was already indexed
    30             ISC_Model::update_post_images_meta( $post->ID, [] );
     33        if ( self::is_global_list_page( $content ) ) {
     34            // Ensure no temporary meta is left behind if user adds shortcode later.
     35            self::cleanup_after_reindex( $post->ID ); // Use the cleanup method
     36            // An empty isc_post_images meta value indicates the post was indexed (or intentionally skipped).
     37            if ( Post_Images_Meta::get( $post->ID ) === '' ) {
     38                Post_Images_Meta::update_images_in_posts( $post->ID, [] );
     39            }
     40            ISC_Log::log( sprintf( 'Exiting update_indexes for post %d: Global list page.', $post->ID ) );
    3141            return;
    3242        }
     
    5060             */
    5161            if ( $attachments !== '' ) {
     62                // Remove the temporary data since we are not updating the index
     63                self::cleanup_after_reindex( $post->ID );
    5264                return;
    5365            }
    5466        }
    5567
    56         ISC_Log::log( 'start updating index for post ID ' . $post->ID );
    57 
    58         // check if we can even save the image information
    59         // abort on archive pages since some output from other plugins might be disabled here
    60         if (
    61             is_archive()
    62             || is_home()
    63             || ! self::can_save_image_information( $post->ID ) ) {
     68        ISC_Log::log( 'Start updating index for post ID ' . $post->ID );
     69
     70        // Check if we can even save the image information
     71        // Abort on archive pages, home, or unsupported post types
     72        if ( is_archive() || is_home() || ! self::can_save_image_information( $post->ID ) ) {
     73            ISC_Log::log( sprintf( 'Exiting update_indexes for post %d: Cannot save image information (archive/home/post type).', $post->ID ) );
     74            // Clean up temporary meta if we abort here
     75            self::cleanup_after_reindex( $post->ID );
    6476            return;
    6577        }
    6678
    67         $image_ids = ISC_Model::filter_image_ids( $content );
    68 
    69         ISC_Log::log( 'updating index with image IDs ' . implode( ', ', $image_ids ) );
    70 
    71         // retrieve images added to a post or page and save all information as a post meta value for the post
    72         ISC_Model::update_post_images_meta( $post->ID, $image_ids );
    73 
    74         // add the post ID to the list of posts associated with a given image
    75         ISC_Model::update_image_posts_meta( $post->ID, $image_ids );
     79        // 1. Get the state before the last update(s).
     80        $old_indexed_data = self::get_pre_update_state( $post->ID );
     81
     82        // 2. Get the image IDs from the currently rendered content.
     83        // Call filter_image_ids only ONCE here.
     84        $new_rendered_ids = ISC_Model::filter_image_ids( $content );
     85
     86        $thumb_id = get_post_thumbnail_id( $post->ID );
     87        if ( ! empty( $thumb_id ) && ! isset( $new_rendered_ids[ $thumb_id ] ) ) {
     88            // Add thumbnail to the list if it's not already there from content parsing.
     89            // The value structure should match what filter_image_ids returns,
     90            // though sync_image_post_associations only cares about the keys.
     91            // The 'thumbnail' flag itself is added later in Post_Meta\Post_Images_Meta::update_images_in_posts.
     92            $thumb_url = wp_get_attachment_url( $thumb_id );
     93            if ( $thumb_url ) {
     94                $new_rendered_ids[ $thumb_id ] = [ $thumb_url ];
     95                ISC_Log::log( sprintf( 'Added thumbnail ID %d to new_rendered_ids for post %d.', $thumb_id, $post->ID ) );
     96            }
     97        }
     98
     99        /**
     100         * Allows developers to modify the list before synchronization.
     101         *
     102         * @return array $new_rendered_ids Image IDs found in the content ([id => data]).
     103         *               id is expected to be numeric and the attachment post type
     104         */
     105        $new_rendered_ids = apply_filters( 'isc_images_in_posts_simple', $new_rendered_ids, $post->ID );
     106        if ( has_filter( 'isc_images_in_posts_simple' ) ) {
     107            ISC_Log::log( sprintf( 'Post %d - new_rendered_ids after isc_images_in_posts_simple filter ran: %s', $post->ID, ! empty( $new_rendered_ids ) ? implode( ', ', array_keys( $new_rendered_ids ) ) : 'Empty' ) );
     108        }
     109
     110        // Check if image IDs refer to a valid post type (default: 'attachment').
     111        $valid_image_post_types = apply_filters( 'isc_valid_post_types', [ 'attachment' ] );
     112        if ( ! empty( $new_rendered_ids ) ) { // Avoid errors if array is empty
     113            foreach ( $new_rendered_ids as $_id => $_data ) {
     114                // Ensure ID is numeric before checking post type
     115                if ( ! is_numeric( $_id ) || $_id <= 0 ) {
     116                    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     117                    ISC_Log::log( sprintf( 'Removing invalid image ID %s from index for post %d.', print_r( $_id, true ), $post->ID ) );
     118                    unset( $new_rendered_ids[ $_id ] );
     119                    continue;
     120                }
     121                $post_type = get_post_type( (int) $_id );
     122                if ( ! $post_type || ! in_array( $post_type, $valid_image_post_types, true ) ) {
     123                    ISC_Log::log( sprintf( 'Removing image ID %d (type: %s) due to invalid post type for post %d.', $_id, $post_type ? $post_type : 'unknown', $post->ID ) );
     124                    unset( $new_rendered_ids[ $_id ] );
     125                }
     126            }
     127        }
     128
     129        // 3. Sync the image->post associations based on comparison.
     130        // This handles adding/removing $post->ID from individual image's isc_image_posts meta.
     131        self::sync_image_post_associations( $post->ID, $old_indexed_data, $new_rendered_ids );
     132
     133        // 4. Update the main post->image index ('isc_post_images') with the current state.
     134        // This saves the $new_rendered_ids to the post's meta.
     135        Post_Images_Meta::update_images_in_posts( $post->ID, $new_rendered_ids );
     136
     137        // 5. Clean up the temporary meta key.
     138        self::cleanup_after_reindex( $post->ID );
    76139
    77140        /**
    78141         * Triggered after updating the indexes.
    79142         *
    80          * @param int $post->ID Post ID.
    81          * @param string $content Post content.
    82          * @param array $image_ids Image IDs found in the content.
     143         * @param int    $post_id          Post ID.
     144         * @param string $content          Post content.
     145         * @param array  $new_rendered_ids Image IDs found in the content ([id => data]).
    83146         */
    84         do_action( 'isc_after_update_indexes', $post->ID, $content, $image_ids );
    85     }
    86 
     147        do_action( 'isc_after_update_indexes', $post->ID, $content, $new_rendered_ids );
     148    }
    87149
    88150    /**
     
    101163        } else {
    102164            // check if a post-images index exists
    103             $attachments = get_post_meta( $post_id, 'isc_post_images', true );
     165            $attachments = Post_Images_Meta::get( $post_id );
    104166            if ( $attachments === '' ) {
    105167                ISC_Log::log( 'no post-images index found' );
     
    173235    public static function is_index_bot(): bool {
    174236        $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
    175         return strpos( $user_agent, 'ISC Index Bot' ) !== false;
     237        // Check the user agent first
     238        $is_bot = strpos( $user_agent, 'ISC Index Bot' ) !== false;
     239
     240        /**
     241         * Apply a filter to allow overriding the result, passing the original check result
     242         *
     243         * @param bool $is_bot The result of the initial check.
     244         * @return bool The final result after applying the filter.
     245         */
     246        return apply_filters( 'isc_is_index_bot', $is_bot );
     247    }
     248
     249    /**
     250     * Prepares a post for re-indexing on the next frontend visit.
     251     * Moves the current post-image index to a temporary key if it exists.
     252     *
     253     * Hooked to 'wp_insert_post'.
     254     *
     255     * @param int $post_id Post ID.
     256     *
     257     * @return bool True if preparation was done (index moved), false otherwise.
     258     */
     259    public static function prepare_for_reindex( int $post_id ): bool {
     260        if ( ! self::can_save_image_information( $post_id ) ) {
     261            ISC_Log::log( sprintf( 'Skipping prepare_for_reindex for post %d: Cannot save image information.', $post_id ) );
     262            return false;
     263        }
     264
     265        $old_value = Post_Images_Meta::get( $post_id );
     266
     267        if ( is_array( $old_value ) ) {
     268            ISC_Log::log( sprintf( 'Preparing post %d for frontend re-index. Moving existing index.', $post_id ) );
     269            update_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY, $old_value );
     270            Post_Images_Meta::delete( $post_id );
     271            return true;
     272        } else {
     273            ISC_Log::log( sprintf( 'Skipping prepare_for_reindex for post %d: No existing index found or already prepared.', $post_id ) );
     274            return false;
     275        }
     276    }
     277
     278    /**
     279     * Retrieves the pre-update state of the post-image index.
     280     *
     281     * @param int $post_id Post ID.
     282     *
     283     * @return array The old index map, or an empty array if none found.
     284     */
     285    public static function get_pre_update_state( int $post_id ): array {
     286        $state = get_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY, true );
     287        return is_array( $state ) ? $state : [];
     288    }
     289
     290    /**
     291     * Compares old and new image associations for a post and updates
     292     * the 'isc_image_posts' meta field on individual images accordingly.
     293     *
     294     * @param int   $post_id       The ID of the post being updated.
     295     * @param array $old_image_map Map of images previously associated [id => data].
     296     * @param array $new_image_map Map of images currently associated [id => data].
     297     */
     298    public static function sync_image_post_associations( int $post_id, array $old_image_map, array $new_image_map ) {
     299        ISC_Log::log( sprintf( 'Entering for post %d.', $post_id ) );
     300
     301        // 1. Calculate differences based on image IDs (keys).
     302        $old_ids = array_keys( $old_image_map );
     303        $new_ids = array_keys( $new_image_map );
     304
     305        // Find IDs present in the new map but not in the old map.
     306        $added_ids = array_diff( $new_ids, $old_ids );
     307        // Find IDs present in the old map but not in the new map.
     308        $removed_ids = array_diff( $old_ids, $new_ids );
     309
     310        ISC_Log::log( sprintf( 'Post %d - Sync Calculated Added IDs: %s', $post_id, ! empty( $added_ids ) ? implode( ', ', $added_ids ) : 'None' ) );
     311        ISC_Log::log( sprintf( 'Post %d - Sync Calculated Removed IDs: %s', $post_id, ! empty( $removed_ids ) ? implode( ', ', $removed_ids ) : 'None' ) );
     312
     313        // 2. Update added associations
     314        foreach ( $added_ids as $id ) {
     315            // Basic validation for ID
     316            if ( ! empty( $id ) && is_numeric( $id ) ) {
     317                Image_Posts_Meta::add_image_post_association( (int) $id, $post_id );
     318            } else {
     319                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     320                ISC_Log::log( sprintf( 'Skipping add association for invalid ID: %s', print_r( $id, true ) ) );
     321            }
     322        }
     323
     324        // 3. Update removed associations
     325        foreach ( $removed_ids as $id ) {
     326            // Basic validation for ID
     327            if ( ! empty( $id ) && is_numeric( $id ) ) {
     328                Image_Posts_Meta::remove_image_post_association( (int) $id, $post_id );
     329            } else {
     330                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     331                ISC_Log::log( sprintf( 'Skipping remove association for invalid ID: %s', print_r( $id, true ) ) );
     332            }
     333        }
     334        ISC_Log::log( sprintf( 'Exiting for post %d.', $post_id ) );
     335    }
     336
     337    /**
     338     * Cleans up the temporary meta key after re-indexing.
     339     *
     340     * @param int $post_id Post ID.
     341     */
     342    public static function cleanup_after_reindex( int $post_id ) {
     343        ISC_Log::log( sprintf( 'Deleting temporary index key %s for post %d.', self::BEFORE_UPDATE_META_KEY, $post_id ) );
     344        delete_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY );
     345    }
     346
     347    /**
     348     * Remove all image-post relations
     349     * this concerns the post meta fields `isc_image_posts` and `isc_post_images`
     350     *
     351     * @return bool True on success, false on failure.
     352     */
     353    public static function clear_index(): bool {
     354        return Post_Images_Meta::delete_all() && Image_Posts_Meta::delete_all();
     355    }
     356
     357    /**
     358     * Handle post deletion and clean up image-post associations.
     359     *
     360     * @param int $post_id Post ID.
     361     */
     362    public static function handle_post_deletion( int $post_id ): void {
     363        $images = Post_Images_Meta::get( $post_id );
     364        if ( is_array( $images ) ) {
     365            foreach ( array_keys( $images ) as $image_id ) {
     366                Image_Posts_Meta::remove_image_post_association( (int) $image_id, $post_id );
     367            }
     368        }
     369
     370        // Clean up the isc_post_images meta too
     371        Post_Images_Meta::delete( $post_id );
     372    }
     373
     374    /**
     375     * Return true if the content indicates that this is a page with the Global List on it
     376     *
     377     * @param string $content The content to check.
     378     * @return bool True if the content contains the Global List shortcode or class, false otherwise.
     379     */
     380    public static function is_global_list_page( $content ): bool {
     381        return has_shortcode( $content, '[isc_list_all]' ) || false !== strpos( $content, 'isc_all_image_list_box' );
    176382    }
    177383}
  • image-source-control-isc/tags/3.2.0/includes/model.php

    r3253182 r3283881  
    22
    33use ISC\Image_Sources\Image_Sources;
     4use ISC\Image_Sources\Post_Meta;
     5use ISC\indexer;
     6
    47/**
    58 * Logic to get and store sources
     
    2225     */
    2326    public function __construct() {
    24         // attachment field handling
    25         add_action( 'add_attachment', [ $this, 'attachment_added' ], 10, 2 );
    2627        add_filter( 'attachment_fields_to_save', [ $this, 'isc_fields_save' ], 10, 2 );
    2728    }
     
    5556     * the function should be used to push a post ID to the (maybe) existing meta field
    5657     *
     58     * @removed since April 2025
     59     *
    5760     * @param integer $post_id ID of the target post.
    5861     * @param array   $image_ids IDs of the attachments in the content.
    5962     */
    6063    public static function update_image_posts_meta( $post_id, $image_ids ) {
    61         ISC_Log::log( 'enter update_image_posts_meta()' );
    62 
    63         $added_images   = [];
    64         $removed_images = [];
    65 
    66         // add thumbnail information
    67         $thumb_id = get_post_thumbnail_id( $post_id );
    68         if ( ! empty( $thumb_id ) ) {
    69             $image_ids[ $thumb_id ] = wp_get_attachment_url( $thumb_id );
    70             ISC_Log::log( 'thumbnail found with ID ' . $thumb_id );
    71         }
    72 
    73         // apply filter to image array, so other developers can add their own logic
    74         $image_ids = apply_filters( 'isc_images_in_posts_simple', $image_ids, $post_id );
    75 
    76         ISC_Log::log( 'known image IDs: ' . implode( ",\n\t", $image_ids ) );
    77 
    78         // check if image IDs refer to an attachment post type
    79         $valid_image_post_types = apply_filters( 'isc_valid_post_types', [ 'attachment' ] );
    80         foreach ( $image_ids as $_id => $_url ) {
    81             if ( ! in_array( get_post_type( $_id ), $valid_image_post_types, true ) ) {
    82                 ISC_Log::log( 'remove image due to invalid post type: ' . $_id );
    83                 unset( $image_ids[ $_id ] );
    84             }
    85         }
    86 
    87         $isc_post_images = get_post_meta( $post_id, 'isc_post_images', true );
    88         // just needed in very rare cases, when updates comes from outside of isc and meta fields doesn’t exist yet
    89         if ( empty( $isc_post_images ) ) {
    90             $isc_post_images = [];
    91         }
    92 
    93         foreach ( $image_ids as $id => $url ) {
    94             if ( is_array( $isc_post_images ) && ! array_key_exists( $id, $isc_post_images ) ) {
    95                 ISC_Log::log( 'add new image: ' . $id );
    96                 $added_images[] = $id;
    97             }
    98         }
    99         if ( is_array( $isc_post_images ) ) {
    100             foreach ( $isc_post_images as $old_id => $value ) {
    101                 if ( ! array_key_exists( $old_id, $image_ids ) ) {
    102                     $removed_images[] = $old_id;
    103                     ISC_Log::log( 'remove image: ' . $old_id );
    104                 } elseif ( ! empty( $old_id ) ) {
    105                         $meta = get_post_meta( $old_id, 'isc_image_posts', true );
    106                     if ( empty( $meta ) ) {
    107                         ISC_Log::log( sprintf( 'adding isc_image_posts for image %d and post %d', $old_id, $post_id ) );
    108                         self::update_image_posts_meta_with_limit( $old_id, [ $post_id ] );
    109                     } elseif ( is_array( $meta ) && ! in_array( $post_id, $meta ) ) {
    110                         // In case the isc_image_posts is not up to date
    111                         $meta[] = $post_id;
    112                         $meta   = array_unique( $meta );
    113                         ISC_Log::log( sprintf( 'updating isc_image_posts for image %d and posts %s', $old_id, implode( ",\n\t", $meta ) ) );
    114                         self::update_image_posts_meta_with_limit( $old_id, $meta );
    115                     }
    116                 }
    117             }
    118         }
    119 
    120         foreach ( $added_images as $id ) {
    121             $meta = get_post_meta( $id, 'isc_image_posts', true );
    122             if ( ! is_array( $meta ) || $meta === [] ) {
    123                 ISC_Log::log( sprintf( 'adding isc_image_posts for NEW image %d and post %d', $id, $post_id ) );
    124                 self::update_image_posts_meta_with_limit( $id, [ $post_id ] );
    125             } else {
    126                 $meta[] = $post_id;
    127                 $meta   = array_unique( $meta );
    128                 ISC_Log::log( sprintf( 'adding isc_image_posts for NEW image %d and posts %s', $id, implode( ', ', $meta ) ) );
    129                 self::update_image_posts_meta_with_limit( $id, $meta );
    130             }
    131         }
    132 
    133         foreach ( $removed_images as $id ) {
    134             $image_meta = get_post_meta( $id, 'isc_image_posts', true );
    135             if ( is_array( $image_meta ) ) {
    136                 $offset = array_search( $post_id, $image_meta );
    137                 if ( $offset !== false ) {
    138                     array_splice( $image_meta, $offset, 1 );
    139                     $image_meta = array_unique( $image_meta );
    140                     ISC_Log::log( sprintf( 'updating isc_image_posts for REMOVED image %d and posts %s', $id, implode( ', ', $image_meta ) ) );
    141                     self::update_image_posts_meta_with_limit( $id, $image_meta );
    142                 }
    143             }
    144         }
    145     }
    146 
    147     /**
    148      * Update the isc_image_posts meta field with a filtered limit
    149      *
    150      * @param integer $post_id   ID of the target post.
    151      * @param array   $image_ids IDs of the attachments in the content.
    152      */
    153     public static function update_image_posts_meta_with_limit( int $post_id, array $image_ids ) {
    154         // limit the number of post IDs to 10
    155         $image_ids = array_slice( $image_ids, 0, apply_filters( 'isc_image_posts_meta_limit', 10 ) );
    156 
    157         self::update_post_meta( $post_id, 'isc_image_posts', $image_ids );
    158     }
    159 
    160     /**
    161      * Retrieve images added to a post or page and save all information as a post meta value for the post
    162      *
    163      * @param integer $post_id   ID of a post.
    164      * @param array   $image_ids IDs of the attachments in the content.
    165      */
    166     public static function update_post_images_meta( $post_id, $image_ids ) {
    167         // add thumbnail information
    168         $thumb_id = get_post_thumbnail_id( $post_id );
    169 
    170         /**
    171          * If an image is used both inside the post and as post thumbnail, the thumbnail entry overrides the regular image.
    172          */
    173         if ( ! empty( $thumb_id ) ) {
    174             $image_ids[ $thumb_id ] = [
    175                 'src'       => wp_get_attachment_url( $thumb_id ),
    176                 'thumbnail' => true,
    177             ];
    178             ISC_Log::log( 'thumbnail found with ID ' . $thumb_id );
    179         }
    180 
    181         // apply filter to image array, so other developers can add their own logic
    182         $image_ids = apply_filters( 'isc_images_in_posts', $image_ids, $post_id );
    183 
    184         if ( empty( $image_ids ) ) {
    185             $image_ids = [];
    186         }
    187 
    188         ISC_Log::log( 'save isc_post_images with size of ' . count( $image_ids ) );
    189 
    190         self::update_post_meta( $post_id, 'isc_post_images', $image_ids );
    191     }
    192 
    193     /**
    194      * Update attachment meta field
    195      *
    196      * @param integer $att_id attachment post ID.
    197      */
    198     public function attachment_added( $att_id ) {
    199         if ( ! isset( $this->fields ) ) {
    200             return;
    201         }
    202 
    203         foreach ( $this->fields as $field ) {
    204             self::update_post_meta( $att_id, $field['id'], $field['default'] );
    205         }
     64
     65        ISC_Log::log( 'function removed. \ISC\Indexer::update_indexes covers most of it now' );
    20666    }
    20767
     
    262122     * Update image-post index attached to attachments when a post is updated
    263123     *
     124     * @removed since April 2025
     125     *
    264126     * @param int     $post_ID      Post ID.
    265127     * @param WP_Post $post_after   Post object following the update.
     
    269131     */
    270132    public static function update_image_post_meta( $post_ID, $post_after, $post_before ) {
    271         if ( ! \ISC\Indexer::can_save_image_information( $post_ID ) ) {
    272             return;
    273         }
    274 
    275         $image_ids = self::filter_image_ids( $post_before->post_content );
    276         $thumb_id  = get_post_thumbnail_id( $post_ID );
    277         if ( ! empty( $thumb_id ) ) {
    278             $image_ids[ $thumb_id ] = '';
    279         }
    280 
    281         // iterate through all image ids and remove the post ID from their "image_posts" meta data
    282         foreach ( $image_ids as $image_id => $image_src ) {
    283             $meta = get_post_meta( $image_id, 'isc_image_posts', true );
    284             if ( is_array( $meta ) ) {
    285                 unset( $meta[ array_search( $post_ID, $meta ) ] );
    286                 self::update_post_meta( $image_id, 'isc_image_posts', $meta );
    287             }
    288         }
     133        ISC_Log::log( 'function removed without a replacement.' );
    289134    }
    290135
     
    293138     * namely the post meta field `isc_post_images`
    294139     *
     140     * @moved since April 2025
     141     *
    295142     * @param int $post_id Post ID.
    296143     */
    297144    public static function clear_single_post_images_index( $post_id ) {
    298         delete_post_meta( $post_id, 'isc_post_images' );
     145        ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Meta\Post_Images_Meta::delete' );
     146
     147        Post_Meta\Post_Images_Meta::delete( $post_id );
    299148    }
    300149
     
    303152     * namely the post meta field `isc_post_images`
    304153     *
     154     * @moved since April 2025
     155     *
    305156     * @return int|false The number of rows updated, or false on error.
    306157     */
    307158    public static function clear_post_images_index() {
    308         global $wpdb;
    309 
    310         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    311         return $wpdb->delete( $wpdb->postmeta, [ 'meta_key' => 'isc_post_images' ], [ '%s' ] );
     159        ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Images_Meta::delete_all' );
     160
     161        return Post_Meta\Post_Images_Meta::delete_all();
    312162    }
    313163
     
    316166     * namely the post meta field `isc_image_posts`
    317167     *
     168     * @moved since April 2025
     169     *
    318170     * @return int|false The number of rows updated, or false on error.
    319171     */
    320172    public static function clear_image_posts_index() {
    321         global $wpdb;
    322 
    323         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    324         return $wpdb->delete( $wpdb->postmeta, [ 'meta_key' => 'isc_image_posts' ], [ '%s' ] );
     173        ISC_Log::log( 'function moved. \ISC\Image_Sources\Image_Posts_Meta::delete_all' );
     174
     175        return Post_Meta\Image_Posts_Meta::delete_all();
    325176    }
    326177
     
    329180     * this concerns the post meta fields `isc_image_posts` and `isc_post_images`
    330181     *
    331      * @return int|false The number of rows updated, or false on error.
    332      */
    333     public static function clear_index() {
    334         $rows_deleted_1 = self::clear_post_images_index();
    335         $rows_deleted_2 = self::clear_image_posts_index();
    336 
    337         if ( $rows_deleted_1 !== false && $rows_deleted_2 !== false ) {
    338             return $rows_deleted_1 + $rows_deleted_2;
    339         }
    340 
    341         return false;
     182     * @deprecated since April 2025
     183     *
     184     * @return bool True if the option was removed
     185     */
     186    public static function clear_index(): bool {
     187        ISC_Log::log( 'function moved. \ISC\Indexer::clear_index' );
     188
     189        return Indexer::clear_index();
    342190    }
    343191
     
    383231        global $wpdb;
    384232
     233        $where_clause = "wp_posts.post_type = 'attachment' AND wp_posts.post_status = 'inherit'";
     234
     235        // Add image type check if images_only is enabled
     236        if ( \ISC\Media_Type_Checker::enabled_images_only_option() ) {
     237            $where_clause .= " AND wp_posts.post_mime_type LIKE 'image/%'";
     238        }
     239
    385240        /**
    386241         * Using EXISTS instead of LEFT JOINs resulted in much faster queries and helped caching the results.
     
    388243        $query = "SELECT wp_posts.ID, wp_posts.post_title, wp_posts.post_parent
    389244            FROM {$wpdb->posts} AS wp_posts
    390             WHERE wp_posts.post_type = 'attachment'
    391               AND wp_posts.post_status = 'inherit'
     245            WHERE {$where_clause}
    392246              AND NOT EXISTS (
    393247                    SELECT 1
     
    418272        // The result of the query is already cached for a day, or until an image is added or removed.
    419273        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
    420         return $wpdb->get_results( $wpdb->prepare( $query, 0, self::MAX_POSTS ) );
     274        $attachments = $wpdb->get_results( $wpdb->prepare( $query, 0, self::MAX_POSTS ) );
     275
     276        $count = count( $attachments );
     277
     278        // update the transient if the number of results differ
     279        if ( $count !== (int) get_transient( 'isc-show-missing-sources-warning' ) ) {
     280            set_transient( 'isc-show-missing-sources-warning', $count, DAY_IN_SECONDS );
     281        }
     282
     283        return $attachments;
    421284    }
    422285
     
    685548     * @param string $key     post meta key.
    686549     * @param mixed  $value   value of the post meta information.
     550     *
     551     * @return bool|int
    687552     */
    688553    public static function update_post_meta( int $post_id, string $key, $value ) {
    689 
    690554        /**
    691555         * Run when any post meta information is stored by ISC
     
    699563        do_action( 'isc_update_post_meta', $post_id, $key, $value );
    700564
    701         update_post_meta( $post_id, $key, $value );
     565        return update_post_meta( $post_id, $key, $value );
    702566    }
    703567
     
    763627        return $file;
    764628    }
     629
     630    /**
     631     * Remove all plugin meta from non-image attachments
     632     *
     633     * @return void
     634     */
     635    public static function remove_plugin_meta_from_non_images() {
     636        // The meta keys used by ISC that should be removed from non-images
     637        $isc_meta_keys = [
     638            'isc_image_source',
     639            'isc_image_source_url',
     640            'isc_image_license',
     641            'isc_image_source_own',
     642            'isc_image_posts',
     643            'isc_possible_usages',
     644            'isc_possible_usages_last_check',
     645        ];
     646
     647        /**
     648         * Filter the meta keys to be removed from non-image attachments
     649         *
     650         * @param array $isc_meta_keys The meta keys to be removed from non-image attachments.
     651         */
     652        apply_filters( 'isc_cleanup_non_image_meta_keys', $isc_meta_keys );
     653
     654        $attachments = get_posts(
     655            [
     656                'post_type'      => 'attachment',
     657                'post_status'    => 'inherit',
     658                'posts_per_page' => -1,
     659            ]
     660        );
     661
     662        if ( empty( $attachments ) ) {
     663            return;
     664        }
     665
     666        foreach ( $attachments as $attachment ) {
     667            if ( ! \ISC\Media_Type_Checker::is_image( $attachment ) ) {
     668                foreach ( $isc_meta_keys as $meta_key ) {
     669                    delete_post_meta( $attachment->ID, $meta_key );
     670                }
     671            }
     672        }
     673    }
    765674}
  • image-source-control-isc/tags/3.2.0/includes/options.php

    r3253182 r3283881  
    8484        $default['standard_source']           = 'custom_text';
    8585        $default['standard_source_text']      = '';
     86        $default['images_only']               = false;
    8687
    8788        /**
  • image-source-control-isc/tags/3.2.0/includes/settings/sections/plugin-options.php

    r3253182 r3283881  
    1717        add_settings_section( 'isc_settings_section_plugin', __( 'Plugin options', 'image-source-control-isc' ), '__return_false', 'isc_settings_page' );
    1818        add_settings_field( 'modules', __( 'Modules', 'image-source-control-isc' ), [ $this, 'render_field_modules' ], 'isc_settings_page', 'isc_settings_section_plugin' );
     19        add_settings_field( 'images_only', __( 'Images only', 'image-source-control-isc' ), [ $this, 'render_field_images_only' ], 'isc_settings_page', 'isc_settings_section_plugin' );
    1920        add_settings_field( 'remove_on_uninstall', __( 'Delete data on uninstall', 'image-source-control-isc' ), [ $this, 'render_field_remove_on_uninstall' ], 'isc_settings_page', 'isc_settings_section_plugin' );
    2021    }
     
    3738
    3839    /**
     40     * Render the option to restrict functionality to images only.
     41     */
     42    public function render_field_images_only() {
     43        $options = $this->get_options();
     44        $checked = ! empty( $options['images_only'] );
     45        require_once ISCPATH . '/admin/templates/settings/plugin/images-only.php';
     46    }
     47
     48    /**
    3949     * Render the option to remove all options and meta data when the plugin is deleted.
    4050     */
     
    5464     */
    5565    public function validate_settings( array $output, array $input ): array {
     66        $output['modules']             = ( isset( $input['modules'] ) && is_array( $input['modules'] ) ) ? $input['modules'] : [];
     67        $output['images_only']         = ! empty( $input['images_only'] );
     68        $output['remove_on_uninstall'] = ! empty( $input['remove_on_uninstall'] );
    5669
    57         $output['modules']             = ( isset( $input['modules'] ) && is_array( $input['modules'] ) ) ? $input['modules'] : [];
    58         $output['remove_on_uninstall'] = ! empty( $input['remove_on_uninstall'] );
     70        // Cleanup meta data for non-images
     71        if (
     72            ! empty( $input['images_only'] ) &&
     73            ! empty( $input['images_only_cleanup'] )
     74        ) {
     75            \ISC_Model::remove_plugin_meta_from_non_images();
     76        }
    5977
    6078        return $output;
  • image-source-control-isc/tags/3.2.0/isc.php

    r3256989 r3283881  
    22/**
    33 * Plugin Name: Image Source Control Lite
    4  * Version: 3.1.4
     4 * Version: 3.2.0
    55 * Plugin URI: https://imagesourcecontrol.com/
    66 * Description: Image Source Control saves the source of an image, lists them and warns if it is missing.
     
    3030}
    3131
    32 define( 'ISCVERSION', '3.1.4' );
     32define( 'ISCVERSION', '3.2.0' );
    3333define( 'ISCNAME', 'Image Source Control' );
    3434define( 'ISCDIR', basename( __DIR__ ) );
  • image-source-control-isc/tags/3.2.0/lib/composer/autoload_classmap.php

    r3253182 r3283881  
    2424    'ISC\\Image_Sources\\Image_Sources' => $baseDir . '/includes/image-sources/image-sources.php',
    2525    'ISC\\Image_Sources\\Image_Sources_Admin_Scripts' => $baseDir . '/includes/image-sources/admin/scripts.php',
     26    'ISC\\Image_Sources\\Post_Meta\\Image_Posts_Meta' => $baseDir . '/includes/image-sources/Post_Meta/Image_Posts_Meta.php',
     27    'ISC\\Image_Sources\\Post_Meta\\Post_Images_Meta' => $baseDir . '/includes/image-sources/Post_Meta/Post_Images_Meta.php',
    2628    'ISC\\Image_Sources\\Renderer' => $baseDir . '/includes/image-sources/renderer.php',
    2729    'ISC\\Image_Sources\\Renderer\\Caption' => $baseDir . '/includes/image-sources/renderer/caption.php',
     
    2931    'ISC\\Image_Sources\\Utils' => $baseDir . '/includes/image-sources/utils.php',
    3032    'ISC\\Indexer' => $baseDir . '/includes/indexer.php',
     33    'ISC\\Media_Type_Checker' => $baseDir . '/includes/media-type-checker.php',
    3134    'ISC\\Newsletter' => $baseDir . '/includes/newsletter.php',
    3235    'ISC\\Options' => $baseDir . '/includes/options.php',
  • image-source-control-isc/tags/3.2.0/lib/composer/autoload_static.php

    r3253182 r3283881  
    4444        'ISC\\Image_Sources\\Image_Sources' => __DIR__ . '/../..' . '/includes/image-sources/image-sources.php',
    4545        'ISC\\Image_Sources\\Image_Sources_Admin_Scripts' => __DIR__ . '/../..' . '/includes/image-sources/admin/scripts.php',
     46        'ISC\\Image_Sources\\Post_Meta\\Image_Posts_Meta' => __DIR__ . '/../..' . '/includes/image-sources/Post_Meta/Image_Posts_Meta.php',
     47        'ISC\\Image_Sources\\Post_Meta\\Post_Images_Meta' => __DIR__ . '/../..' . '/includes/image-sources/Post_Meta/Post_Images_Meta.php',
    4648        'ISC\\Image_Sources\\Renderer' => __DIR__ . '/../..' . '/includes/image-sources/renderer.php',
    4749        'ISC\\Image_Sources\\Renderer\\Caption' => __DIR__ . '/../..' . '/includes/image-sources/renderer/caption.php',
     
    4951        'ISC\\Image_Sources\\Utils' => __DIR__ . '/../..' . '/includes/image-sources/utils.php',
    5052        'ISC\\Indexer' => __DIR__ . '/../..' . '/includes/indexer.php',
     53        'ISC\\Media_Type_Checker' => __DIR__ . '/../..' . '/includes/media-type-checker.php',
    5154        'ISC\\Newsletter' => __DIR__ . '/../..' . '/includes/newsletter.php',
    5255        'ISC\\Options' => __DIR__ . '/../..' . '/includes/options.php',
  • image-source-control-isc/tags/3.2.0/public/public.php

    r3253182 r3283881  
    182182        }
    183183
     184        if ( ISC\Indexer::is_global_list_page( $content ) ) {
     185            ISC_Log::log( 'skipped adding sources because the content contains the Global List' );
     186            return $content;
     187        }
     188
    184189        // return if this is not the main query or within the loop
    185190        if ( ! self::is_main_loop() ) {
     
    629634
    630635        foreach ( $attachments as $_attachment ) {
     636            // skip non-images if option is selected
     637            if ( ! \ISC\Media_Type_Checker::should_process_attachment( $_attachment ) ) {
     638                ISC_Log::log( sprintf( 'skipped image %d because it is not an image', $_attachment->ID ) );
     639                continue;
     640            }
     641
    631642            $connected_atts[ $_attachment->ID ]['source']   = self::get_image_source_text_raw( $_attachment->ID );
    632643            $connected_atts[ $_attachment->ID ]['standard'] = Standard_Source::use_standard_source( $_attachment->ID );
  • image-source-control-isc/tags/3.2.0/readme.txt

    r3275604 r3283881  
    44Requires at least: 6.0
    55Tested up to: 6.8
    6 Stable tag: 3.1.4
     6Stable tag: 3.2.0
    77Requires PHP: 7.4
    88License: GPLv3 or later
     
    4848* Warn about missing image sources
    4949* Manage, display, and link available licenses
     50* Enable the features for any files in the media library or for images only
     51* Filter the media library list by images with or without sources
    5052
    5153**Featured Image Caption**
     
    6163[Check out all features of Image Source Control](https://imagesourcecontrol.com/?utm_source=wporg&utm_medium=link&utm_campaign=all-features).
    6264
     65* The Indexer looks for all images in all published content in one go
    6366* List credits for images outside the content
    6467* Add multiple links to the source string
     
    6770* Show image usage in the image details and the List view of the media library
    6871* Bulk-edit image copyright information in the media library
     72* Preview image credits in the media library
    6973* Show the standard picture credit for all images without a selected source
    7074* [Display IPTC copyright metadata](https://imagesourcecontrol.com/blog/iptc-copyright-information-image-caption-wordpress/) in the backend and automatically as a standard source in the frontend
     
    8084
    8185Extended compatibility with Elementor, Avada, WP Bakery, and other page builders
    82 as well as with plugins like Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
     86as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
    8387
    8488[See Pricing](https://imagesourcecontrol.com/pricing/?utm_source=wporg&utm_medium=link&utm_campaign=pricing).
     
    1511551. Customizing the global list of image sources
    1521561. Manage image usage licenses
     1571. The Indexer searches for all images in published content
    153158
    154159== Changelog ==
     160
     161= 3.2.0 =
     162
     163- Feature: You can use the option “Images only” to disable features for non-images in the media library, e.g., PDF files
     164- Improvement: When authors change content, ISC now looks for removed or new images at the next visit of that page in the frontend. This highly improved compatibility with page builders and dynamic content like shortcodes.
     165- Improvement: When WP_DEBUG is enabled, show a button to list the content of the internal storage on the Tools page
     166- Improvement: Removes image source output on pages with the Global List on it
     167- Improvement (Pro): Ignores image URLs in `href` attributes when looking for image sources
     168- Improvement (Pro): Clears the URL storage when the Indexer runs. This can help with issues when a site was migrated to another URL
     169- Improvement (Pro): Extends ignored options for unused images
     170- Improvement (Pro): The column with image sources forms in the Media Library list view only shows if the user has the permission to edit any image information
     171- Improvement (Pro): The forms to edit image sources in the Media Library list view only show if the user has the permission to edit information for that given image
     172- Improvement (Pro): The Indexer for Unused Images now works with posts translated by WPML
     173- Fix: Removes old index information when the last image in a post is removed
     174- Fix (Pro): Some reserved characters in URLs caused (e.g., `&`) the bulk edit fields for images sources in the media library to be cut off
     175- Dev: Extracts post meta handling (`isc_image_posts`, `isc_post_images`) into dedicated classes.
     176- Dev (Pro): Disable image source form fields in the Media Library list view when submitting the filter form to prevent broken URLs. This is related to a compatibility issue caused by a third-party setup
     177- Dev (Pro): The Indexer now works in batches to prevent timeouts on large sites when Query Monitor is installed
     178- Dev (Pro): Pages with the Global List shortcode are now ignored by the Indexer.
     179- Dev: Adds cleanup routines for meta data for deleted and trashed posts.
     180- Dev: Replace some direct DB calls with WP functions
     181- Updates German translation
    155182
    156183= 3.1.4 =
  • image-source-control-isc/tags/3.2.0/uninstall.php

    r3253182 r3283881  
    1010
    1111    // delete post meta fields
    12     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_post_images' ), array( '%s' ) );
    13     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_posts' ), array( '%s' ) );
    14     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source' ), array( '%s' ) );
    15     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source_own' ), array( '%s' ) );
    16     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source_url' ), array( '%s' ) );
    17     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_possible_usages' ), array( '%s' ) );
    18     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_possible_usages_last_check' ), array( '%s' ) );
     12    delete_post_meta_by_key( 'isc_post_images' );
     13    delete_post_meta_by_key( 'isc_image_posts' );
     14    delete_post_meta_by_key( 'isc_image_source' );
     15    delete_post_meta_by_key( 'isc_image_source_own' );
     16    delete_post_meta_by_key( 'isc_image_source_url' );
     17    delete_post_meta_by_key( 'isc_possible_usages' );
     18    delete_post_meta_by_key( 'isc_possible_usages_last_check' );
     19    delete_post_meta_by_key( 'isc_post_images_before_update' );
    1920
    2021    // delete main plugin options
  • image-source-control-isc/trunk/admin/assets/js/settings.js

    r3253182 r3283881  
    1818        $( '#isc-settings-caption-style input' ).on( 'change', function(){ isc_toggle_caption_position(); } );
    1919        $( '#isc-settings-global-list-indexed-images' ).on( 'change', function(){ isc_show_reindex_warning(); } );
    20         $('#isc-settings-plugin-modules input[type="checkbox"]').on('change', function() { isc_toggle_module_sections(); });
     20        $( '#isc-settings-plugin-modules input[type="checkbox"]').on('change', function() { isc_toggle_module_sections(); });
     21        $( '#isc-settings-plugin-images-only' ).on( 'change', function() {
     22            const enabled = this.checked;
     23            $( '#isc-settings-plugin-images-only-indexer' ).toggleClass( 'hidden' );
     24            $( '#isc-settings-plugin-images-only-cleanup-wrapper' ).toggleClass( 'hidden', !enabled );
     25        });
    2126
    2227        // Show and update preview when a position option is clicked
  • image-source-control-isc/trunk/admin/assets/js/sources.js

    r3101157 r3283881  
    102102            }
    103103        );
     104        // show frontend storage
     105        $( '#isc-show-storage' ).on(
     106            'click',
     107            function(){
     108                // disable the button
     109                var button      = this;
     110                button.disabled = true;
     111
     112                $.ajax(
     113                    {
     114                        type: 'POST',
     115                        url: ajaxurl,
     116                        data: {
     117                            action: 'isc-show-storage',
     118                            nonce: isc.ajaxNonce,
     119                        },
     120                        success:function(data, textStatus, XMLHttpRequest){
     121                            // display return messages
     122                            $( '#isc-show-storage-output' ).html( data );
     123                            button.disabled = false;
     124                        },
     125                        error: function(MLHttpRequest, textStatus, errorThrown){
     126                            $( '#isc-show-storage-output' ).html( errorThrown );
     127                            button.disabled = false;
     128                        }
     129                    }
     130                );
     131            }
     132        );
    104133        // clear frontend storage
    105134        $( '#isc-clear-storage' ).on(
  • image-source-control-isc/trunk/admin/templates/sources/storage.php

    r3253182 r3283881  
    2222<button id="isc-clear-storage" class="button button-secondary"><?php esc_html_e( 'clear storage', 'image-source-control-isc' ); ?></button>
    2323<div id="isc-clear-storage-feedback"></div>
     24<?php
     25// if the WordPress debug mode is enabled, show the button to dump the storage
     26if ( $storage_size > 0 && defined( 'WP_DEBUG' ) && WP_DEBUG ) :
     27    ?>
     28    <br/>
     29    <button id="isc-show-storage" class="button button-secondary"><?php esc_html_e( 'show storage', 'image-source-control-isc' ); ?></button>
     30    <pre id="isc-show-storage-output"></pre>
     31    <?php
     32endif; ?>
  • image-source-control-isc/trunk/includes/image-sources/admin.php

    r3254673 r3283881  
    66 * Admin features for image sources
    77 */
    8 class Admin {
    9     use \ISC\Options;
    10 
     8class Admin extends Image_Sources {
    119    /**
    1210     * Initiate admin functions
    1311     */
    1412    public function __construct() {
     13        parent::__construct();
     14
    1515        // load components
    1616        new Image_Sources_Admin_Scripts();
  • image-source-control-isc/trunk/includes/image-sources/admin/ajax.php

    r3253182 r3283881  
    33namespace ISC\Image_Sources;
    44
    5 use ISC_Model;
     5use ISC\Image_Sources\Post_Meta\Image_Posts_Meta;
     6use ISC\Image_Sources\Post_Meta\Post_Images_Meta;
    67
    78/**
     
    1819        add_action( 'wp_ajax_isc-image-post-relations', [ $this, 'list_image_post_relations' ] );
    1920        add_action( 'wp_ajax_isc-clear-index', [ $this, 'clear_index' ] );
     21        add_action( 'wp_ajax_isc-show-storage', [ $this, 'show_storage' ] );
    2022        add_action( 'wp_ajax_isc-clear-storage', [ $this, 'clear_storage' ] );
    2123        add_action( 'wp_ajax_isc-clear-image-posts-index', [ $this, 'clear_image_posts_index' ] );
     
    98100        }
    99101
    100         die(
    101             sprintf(
    102             // translators: %d is the number of deleted entries
    103                 esc_html__( '%d entries deleted', 'image-source-control-isc' ),
    104                 (int) ISC_Model::clear_index()
    105             )
    106         );
     102        if ( \ISC\Indexer::clear_index() ) {
     103            wp_send_json_success( esc_html__( 'Index cleared', 'image-source-control-isc' ) );
     104        } else {
     105            wp_send_json_error( 'Error' );
     106        }
     107    }
     108
     109    /**
     110     * Show the storage array for debugging
     111     */
     112    public function show_storage() {
     113        check_ajax_referer( 'isc-admin-ajax-nonce', 'nonce' );
     114
     115        if ( ! current_user_can( 'manage_options' ) ) {
     116            die( 'Wrong capabilities' );
     117        }
     118
     119        $storage_model = new \ISC_Storage_Model();
     120        $images        = $storage_model->get_storage();
     121
     122        // We are in debug mode, so it is fine to just output the content
     123        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     124        print_r( $images );
     125
     126        die();
    107127    }
    108128
     
    137157
    138158        $image_id = (int) $_POST['image_id'];
    139         delete_post_meta( $image_id, 'isc_image_posts' );
     159        $deleted  = Image_Posts_Meta::delete( $image_id );
    140160
    141         die( 'Image-Posts index cleared' );
     161        if ( $deleted ) {
     162            wp_send_json_success( 'Image-Posts index cleared' );
     163        } else {
     164            wp_send_json_success( 'Image-Posts index cleared (or did not exist)' );
     165        }
    142166    }
    143167
     
    157181
    158182        $post_id = (int) $_POST['post_id'];
    159         delete_post_meta( $post_id, 'isc_post_images' );
     183        $deleted = Post_Images_Meta::delete( $post_id );
    160184
    161         die( 'Post-Images index cleared' );
     185        if ( $deleted ) {
     186            wp_send_json_success( 'Post-Images index cleared' );
     187        } else {
     188            wp_send_json_success( 'Post-Images index cleared (or did not exist)' );
     189        }
    162190    }
    163191}
  • image-source-control-isc/trunk/includes/image-sources/admin/fields.php

    r3253281 r3283881  
    2323     * Add custom field to attachment
    2424     *
    25      * @param array  $form_fields field fields.
    26      * @param object $post        post object.
     25     * @param array    $form_fields field fields.
     26     * @param \WP_Post $post        post object.
    2727     *
    2828     * @return array with form fields
    2929     */
    3030    public function add_isc_fields( $form_fields, $post ) {
     31        // Check if we should process this attachment based on settings
     32        if ( ! \ISC\Media_Type_Checker::should_process_attachment( $post ) ) {
     33            return $form_fields;
     34        }
     35
    3136        /**
    3237         * Return, when the ISC fields are enabled for blocks, and we are not using the block editor.
  • image-source-control-isc/trunk/includes/image-sources/admin/media-library-filters.php

    r3256706 r3283881  
    4545     */
    4646    public function filter_media_library( \WP_Query $query ) {
    47         Admin_Utils::is_media_library_list_view_page();
     47        if ( ! Admin_Utils::is_media_library_list_view_page() ) {
     48            return;
     49        }
    4850
    4951        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    5052        $filter = isset( $_GET['isc_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['isc_filter'] ) ) : '';
     53
     54        // Add image type check if images_only is enabled
     55        if ( \ISC\Media_Type_Checker::enabled_images_only_option() ) {
     56            $query->set( 'post_mime_type', 'image%' );
     57        }
    5158
    5259        if ( $filter === 'with_source' ) {
  • image-source-control-isc/trunk/includes/image-sources/analyze-html.php

    r3253281 r3283881  
    120120    /**
    121121     * Extract image URLs from HTML.
    122      * One image URL per HTML tag will be found.
     122     * One image URL per HTML attribute will be found.
     123     *
     124     * Limitations:
     125     * - retrieves the first valid image URL from any HTML tag
     126     * - if an IMG tag has a SRC attribute with a valid image URL, all other tags with valid URLs are ignored
     127     * - technically, one could generate tags with different images from the Media Library, e.g., having a differently cut URL in a data-attribute
     128     *  that then shows dynamically using JavaScript
     129     *  this would cause one of the images not being listed with a source or a used image
     130     *  since I haven’t seen that in the wild, this case is deliberately ignored
    123131     *
    124132     * @param string $html Any HTML code.
  • image-source-control-isc/trunk/includes/image-sources/image-sources.php

    r3253182 r3283881  
    1010 */
    1111class Image_Sources {
    12     /**
    13      * Define default meta fields
    14      *
    15      * @var array option fields.
    16      */
    17     protected $fields = [
    18         'image_source'     => [
    19             'id'      => 'isc_image_source',
    20             'default' => '',
    21         ],
    22         'image_source_url' => [
    23             'id'      => 'isc_image_source_url',
    24             'default' => '',
    25         ],
    26         'image_source_own' => [
    27             'id'      => 'isc_image_source_own',
    28             'default' => '',
    29         ],
    30         'image_posts'      => [
    31             'id'      => 'isc_image_posts',
    32             'default' => [],
    33         ],
    34         'image_licence'    => [
    35             'id'      => 'isc_image_licence',
    36             'default' => '',
    37         ],
    38     ];
    39 
    4012    /**
    4113     * Allowed image file types/extensions
     
    129101
    130102        /**
    131          * Clear post-image index whenever the content of a single post is updated
     103         * Update index when a post is deleted or moved into trash
     104         */
     105        add_action( 'before_delete_post', [ '\ISC\Indexer', 'handle_post_deletion' ] );
     106        add_action( 'wp_trash_post', [ '\ISC\Indexer', 'handle_post_deletion' ] );
     107
     108        /**
     109         * Clear post-image index whenever the content of a single post is updated and move the content to a temporary post meta
    132110         * this could force reindexing the post after adding or removing image sources
    133111         */
    134         add_action( 'wp_insert_post', [ 'ISC_Model', 'clear_single_post_images_index' ] );
    135 
    136         /**
    137          * Fire when a post or page was updated
    138          */
    139         add_action( 'post_updated', [ 'ISC_Model', 'update_image_post_meta' ], 10, 3 );
     112        add_action( 'wp_insert_post', [ '\ISC\Indexer', 'prepare_for_reindex' ] );
    140113    }
    141114
  • image-source-control-isc/trunk/includes/indexer.php

    r3253182 r3283881  
    33namespace ISC;
    44
     5use ISC\Image_Sources\Post_Meta\Image_Posts_Meta;
     6use ISC\Image_Sources\Post_Meta\Post_Images_Meta;
    57use ISC_Log, ISC_Model;
    68
     
    1012class Indexer {
    1113
    12     /**
    13      * Handle index updates
    14      *
    15      * @param string $content content of the target post.
     14    const BEFORE_UPDATE_META_KEY = 'isc_post_images_before_update';
     15
     16    /**
     17     * Handle index updates on frontend visit after a post save.
     18     * Compares pre-save state with current render and updates indexes.
     19     *
     20     * @param string $content Rendered content of the target post.
    1621     *
    1722     * @return void
     
    2631
    2732        // Skip indexing if this is a page with a Global list.
    28         if ( has_shortcode( $content, '[isc_list_all]' ) || false !== strpos( $content, 'isc_all_image_list_box' ) ) {
    29             // an empty isc_post_images meta value is used to indicate that the post was already indexed
    30             ISC_Model::update_post_images_meta( $post->ID, [] );
     33        if ( self::is_global_list_page( $content ) ) {
     34            // Ensure no temporary meta is left behind if user adds shortcode later.
     35            self::cleanup_after_reindex( $post->ID ); // Use the cleanup method
     36            // An empty isc_post_images meta value indicates the post was indexed (or intentionally skipped).
     37            if ( Post_Images_Meta::get( $post->ID ) === '' ) {
     38                Post_Images_Meta::update_images_in_posts( $post->ID, [] );
     39            }
     40            ISC_Log::log( sprintf( 'Exiting update_indexes for post %d: Global list page.', $post->ID ) );
    3141            return;
    3242        }
     
    5060             */
    5161            if ( $attachments !== '' ) {
     62                // Remove the temporary data since we are not updating the index
     63                self::cleanup_after_reindex( $post->ID );
    5264                return;
    5365            }
    5466        }
    5567
    56         ISC_Log::log( 'start updating index for post ID ' . $post->ID );
    57 
    58         // check if we can even save the image information
    59         // abort on archive pages since some output from other plugins might be disabled here
    60         if (
    61             is_archive()
    62             || is_home()
    63             || ! self::can_save_image_information( $post->ID ) ) {
     68        ISC_Log::log( 'Start updating index for post ID ' . $post->ID );
     69
     70        // Check if we can even save the image information
     71        // Abort on archive pages, home, or unsupported post types
     72        if ( is_archive() || is_home() || ! self::can_save_image_information( $post->ID ) ) {
     73            ISC_Log::log( sprintf( 'Exiting update_indexes for post %d: Cannot save image information (archive/home/post type).', $post->ID ) );
     74            // Clean up temporary meta if we abort here
     75            self::cleanup_after_reindex( $post->ID );
    6476            return;
    6577        }
    6678
    67         $image_ids = ISC_Model::filter_image_ids( $content );
    68 
    69         ISC_Log::log( 'updating index with image IDs ' . implode( ', ', $image_ids ) );
    70 
    71         // retrieve images added to a post or page and save all information as a post meta value for the post
    72         ISC_Model::update_post_images_meta( $post->ID, $image_ids );
    73 
    74         // add the post ID to the list of posts associated with a given image
    75         ISC_Model::update_image_posts_meta( $post->ID, $image_ids );
     79        // 1. Get the state before the last update(s).
     80        $old_indexed_data = self::get_pre_update_state( $post->ID );
     81
     82        // 2. Get the image IDs from the currently rendered content.
     83        // Call filter_image_ids only ONCE here.
     84        $new_rendered_ids = ISC_Model::filter_image_ids( $content );
     85
     86        $thumb_id = get_post_thumbnail_id( $post->ID );
     87        if ( ! empty( $thumb_id ) && ! isset( $new_rendered_ids[ $thumb_id ] ) ) {
     88            // Add thumbnail to the list if it's not already there from content parsing.
     89            // The value structure should match what filter_image_ids returns,
     90            // though sync_image_post_associations only cares about the keys.
     91            // The 'thumbnail' flag itself is added later in Post_Meta\Post_Images_Meta::update_images_in_posts.
     92            $thumb_url = wp_get_attachment_url( $thumb_id );
     93            if ( $thumb_url ) {
     94                $new_rendered_ids[ $thumb_id ] = [ $thumb_url ];
     95                ISC_Log::log( sprintf( 'Added thumbnail ID %d to new_rendered_ids for post %d.', $thumb_id, $post->ID ) );
     96            }
     97        }
     98
     99        /**
     100         * Allows developers to modify the list before synchronization.
     101         *
     102         * @return array $new_rendered_ids Image IDs found in the content ([id => data]).
     103         *               id is expected to be numeric and the attachment post type
     104         */
     105        $new_rendered_ids = apply_filters( 'isc_images_in_posts_simple', $new_rendered_ids, $post->ID );
     106        if ( has_filter( 'isc_images_in_posts_simple' ) ) {
     107            ISC_Log::log( sprintf( 'Post %d - new_rendered_ids after isc_images_in_posts_simple filter ran: %s', $post->ID, ! empty( $new_rendered_ids ) ? implode( ', ', array_keys( $new_rendered_ids ) ) : 'Empty' ) );
     108        }
     109
     110        // Check if image IDs refer to a valid post type (default: 'attachment').
     111        $valid_image_post_types = apply_filters( 'isc_valid_post_types', [ 'attachment' ] );
     112        if ( ! empty( $new_rendered_ids ) ) { // Avoid errors if array is empty
     113            foreach ( $new_rendered_ids as $_id => $_data ) {
     114                // Ensure ID is numeric before checking post type
     115                if ( ! is_numeric( $_id ) || $_id <= 0 ) {
     116                    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     117                    ISC_Log::log( sprintf( 'Removing invalid image ID %s from index for post %d.', print_r( $_id, true ), $post->ID ) );
     118                    unset( $new_rendered_ids[ $_id ] );
     119                    continue;
     120                }
     121                $post_type = get_post_type( (int) $_id );
     122                if ( ! $post_type || ! in_array( $post_type, $valid_image_post_types, true ) ) {
     123                    ISC_Log::log( sprintf( 'Removing image ID %d (type: %s) due to invalid post type for post %d.', $_id, $post_type ? $post_type : 'unknown', $post->ID ) );
     124                    unset( $new_rendered_ids[ $_id ] );
     125                }
     126            }
     127        }
     128
     129        // 3. Sync the image->post associations based on comparison.
     130        // This handles adding/removing $post->ID from individual image's isc_image_posts meta.
     131        self::sync_image_post_associations( $post->ID, $old_indexed_data, $new_rendered_ids );
     132
     133        // 4. Update the main post->image index ('isc_post_images') with the current state.
     134        // This saves the $new_rendered_ids to the post's meta.
     135        Post_Images_Meta::update_images_in_posts( $post->ID, $new_rendered_ids );
     136
     137        // 5. Clean up the temporary meta key.
     138        self::cleanup_after_reindex( $post->ID );
    76139
    77140        /**
    78141         * Triggered after updating the indexes.
    79142         *
    80          * @param int $post->ID Post ID.
    81          * @param string $content Post content.
    82          * @param array $image_ids Image IDs found in the content.
     143         * @param int    $post_id          Post ID.
     144         * @param string $content          Post content.
     145         * @param array  $new_rendered_ids Image IDs found in the content ([id => data]).
    83146         */
    84         do_action( 'isc_after_update_indexes', $post->ID, $content, $image_ids );
    85     }
    86 
     147        do_action( 'isc_after_update_indexes', $post->ID, $content, $new_rendered_ids );
     148    }
    87149
    88150    /**
     
    101163        } else {
    102164            // check if a post-images index exists
    103             $attachments = get_post_meta( $post_id, 'isc_post_images', true );
     165            $attachments = Post_Images_Meta::get( $post_id );
    104166            if ( $attachments === '' ) {
    105167                ISC_Log::log( 'no post-images index found' );
     
    173235    public static function is_index_bot(): bool {
    174236        $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
    175         return strpos( $user_agent, 'ISC Index Bot' ) !== false;
     237        // Check the user agent first
     238        $is_bot = strpos( $user_agent, 'ISC Index Bot' ) !== false;
     239
     240        /**
     241         * Apply a filter to allow overriding the result, passing the original check result
     242         *
     243         * @param bool $is_bot The result of the initial check.
     244         * @return bool The final result after applying the filter.
     245         */
     246        return apply_filters( 'isc_is_index_bot', $is_bot );
     247    }
     248
     249    /**
     250     * Prepares a post for re-indexing on the next frontend visit.
     251     * Moves the current post-image index to a temporary key if it exists.
     252     *
     253     * Hooked to 'wp_insert_post'.
     254     *
     255     * @param int $post_id Post ID.
     256     *
     257     * @return bool True if preparation was done (index moved), false otherwise.
     258     */
     259    public static function prepare_for_reindex( int $post_id ): bool {
     260        if ( ! self::can_save_image_information( $post_id ) ) {
     261            ISC_Log::log( sprintf( 'Skipping prepare_for_reindex for post %d: Cannot save image information.', $post_id ) );
     262            return false;
     263        }
     264
     265        $old_value = Post_Images_Meta::get( $post_id );
     266
     267        if ( is_array( $old_value ) ) {
     268            ISC_Log::log( sprintf( 'Preparing post %d for frontend re-index. Moving existing index.', $post_id ) );
     269            update_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY, $old_value );
     270            Post_Images_Meta::delete( $post_id );
     271            return true;
     272        } else {
     273            ISC_Log::log( sprintf( 'Skipping prepare_for_reindex for post %d: No existing index found or already prepared.', $post_id ) );
     274            return false;
     275        }
     276    }
     277
     278    /**
     279     * Retrieves the pre-update state of the post-image index.
     280     *
     281     * @param int $post_id Post ID.
     282     *
     283     * @return array The old index map, or an empty array if none found.
     284     */
     285    public static function get_pre_update_state( int $post_id ): array {
     286        $state = get_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY, true );
     287        return is_array( $state ) ? $state : [];
     288    }
     289
     290    /**
     291     * Compares old and new image associations for a post and updates
     292     * the 'isc_image_posts' meta field on individual images accordingly.
     293     *
     294     * @param int   $post_id       The ID of the post being updated.
     295     * @param array $old_image_map Map of images previously associated [id => data].
     296     * @param array $new_image_map Map of images currently associated [id => data].
     297     */
     298    public static function sync_image_post_associations( int $post_id, array $old_image_map, array $new_image_map ) {
     299        ISC_Log::log( sprintf( 'Entering for post %d.', $post_id ) );
     300
     301        // 1. Calculate differences based on image IDs (keys).
     302        $old_ids = array_keys( $old_image_map );
     303        $new_ids = array_keys( $new_image_map );
     304
     305        // Find IDs present in the new map but not in the old map.
     306        $added_ids = array_diff( $new_ids, $old_ids );
     307        // Find IDs present in the old map but not in the new map.
     308        $removed_ids = array_diff( $old_ids, $new_ids );
     309
     310        ISC_Log::log( sprintf( 'Post %d - Sync Calculated Added IDs: %s', $post_id, ! empty( $added_ids ) ? implode( ', ', $added_ids ) : 'None' ) );
     311        ISC_Log::log( sprintf( 'Post %d - Sync Calculated Removed IDs: %s', $post_id, ! empty( $removed_ids ) ? implode( ', ', $removed_ids ) : 'None' ) );
     312
     313        // 2. Update added associations
     314        foreach ( $added_ids as $id ) {
     315            // Basic validation for ID
     316            if ( ! empty( $id ) && is_numeric( $id ) ) {
     317                Image_Posts_Meta::add_image_post_association( (int) $id, $post_id );
     318            } else {
     319                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     320                ISC_Log::log( sprintf( 'Skipping add association for invalid ID: %s', print_r( $id, true ) ) );
     321            }
     322        }
     323
     324        // 3. Update removed associations
     325        foreach ( $removed_ids as $id ) {
     326            // Basic validation for ID
     327            if ( ! empty( $id ) && is_numeric( $id ) ) {
     328                Image_Posts_Meta::remove_image_post_association( (int) $id, $post_id );
     329            } else {
     330                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
     331                ISC_Log::log( sprintf( 'Skipping remove association for invalid ID: %s', print_r( $id, true ) ) );
     332            }
     333        }
     334        ISC_Log::log( sprintf( 'Exiting for post %d.', $post_id ) );
     335    }
     336
     337    /**
     338     * Cleans up the temporary meta key after re-indexing.
     339     *
     340     * @param int $post_id Post ID.
     341     */
     342    public static function cleanup_after_reindex( int $post_id ) {
     343        ISC_Log::log( sprintf( 'Deleting temporary index key %s for post %d.', self::BEFORE_UPDATE_META_KEY, $post_id ) );
     344        delete_post_meta( $post_id, self::BEFORE_UPDATE_META_KEY );
     345    }
     346
     347    /**
     348     * Remove all image-post relations
     349     * this concerns the post meta fields `isc_image_posts` and `isc_post_images`
     350     *
     351     * @return bool True on success, false on failure.
     352     */
     353    public static function clear_index(): bool {
     354        return Post_Images_Meta::delete_all() && Image_Posts_Meta::delete_all();
     355    }
     356
     357    /**
     358     * Handle post deletion and clean up image-post associations.
     359     *
     360     * @param int $post_id Post ID.
     361     */
     362    public static function handle_post_deletion( int $post_id ): void {
     363        $images = Post_Images_Meta::get( $post_id );
     364        if ( is_array( $images ) ) {
     365            foreach ( array_keys( $images ) as $image_id ) {
     366                Image_Posts_Meta::remove_image_post_association( (int) $image_id, $post_id );
     367            }
     368        }
     369
     370        // Clean up the isc_post_images meta too
     371        Post_Images_Meta::delete( $post_id );
     372    }
     373
     374    /**
     375     * Return true if the content indicates that this is a page with the Global List on it
     376     *
     377     * @param string $content The content to check.
     378     * @return bool True if the content contains the Global List shortcode or class, false otherwise.
     379     */
     380    public static function is_global_list_page( $content ): bool {
     381        return has_shortcode( $content, '[isc_list_all]' ) || false !== strpos( $content, 'isc_all_image_list_box' );
    176382    }
    177383}
  • image-source-control-isc/trunk/includes/model.php

    r3253182 r3283881  
    22
    33use ISC\Image_Sources\Image_Sources;
     4use ISC\Image_Sources\Post_Meta;
     5use ISC\indexer;
     6
    47/**
    58 * Logic to get and store sources
     
    2225     */
    2326    public function __construct() {
    24         // attachment field handling
    25         add_action( 'add_attachment', [ $this, 'attachment_added' ], 10, 2 );
    2627        add_filter( 'attachment_fields_to_save', [ $this, 'isc_fields_save' ], 10, 2 );
    2728    }
     
    5556     * the function should be used to push a post ID to the (maybe) existing meta field
    5657     *
     58     * @removed since April 2025
     59     *
    5760     * @param integer $post_id ID of the target post.
    5861     * @param array   $image_ids IDs of the attachments in the content.
    5962     */
    6063    public static function update_image_posts_meta( $post_id, $image_ids ) {
    61         ISC_Log::log( 'enter update_image_posts_meta()' );
    62 
    63         $added_images   = [];
    64         $removed_images = [];
    65 
    66         // add thumbnail information
    67         $thumb_id = get_post_thumbnail_id( $post_id );
    68         if ( ! empty( $thumb_id ) ) {
    69             $image_ids[ $thumb_id ] = wp_get_attachment_url( $thumb_id );
    70             ISC_Log::log( 'thumbnail found with ID ' . $thumb_id );
    71         }
    72 
    73         // apply filter to image array, so other developers can add their own logic
    74         $image_ids = apply_filters( 'isc_images_in_posts_simple', $image_ids, $post_id );
    75 
    76         ISC_Log::log( 'known image IDs: ' . implode( ",\n\t", $image_ids ) );
    77 
    78         // check if image IDs refer to an attachment post type
    79         $valid_image_post_types = apply_filters( 'isc_valid_post_types', [ 'attachment' ] );
    80         foreach ( $image_ids as $_id => $_url ) {
    81             if ( ! in_array( get_post_type( $_id ), $valid_image_post_types, true ) ) {
    82                 ISC_Log::log( 'remove image due to invalid post type: ' . $_id );
    83                 unset( $image_ids[ $_id ] );
    84             }
    85         }
    86 
    87         $isc_post_images = get_post_meta( $post_id, 'isc_post_images', true );
    88         // just needed in very rare cases, when updates comes from outside of isc and meta fields doesn’t exist yet
    89         if ( empty( $isc_post_images ) ) {
    90             $isc_post_images = [];
    91         }
    92 
    93         foreach ( $image_ids as $id => $url ) {
    94             if ( is_array( $isc_post_images ) && ! array_key_exists( $id, $isc_post_images ) ) {
    95                 ISC_Log::log( 'add new image: ' . $id );
    96                 $added_images[] = $id;
    97             }
    98         }
    99         if ( is_array( $isc_post_images ) ) {
    100             foreach ( $isc_post_images as $old_id => $value ) {
    101                 if ( ! array_key_exists( $old_id, $image_ids ) ) {
    102                     $removed_images[] = $old_id;
    103                     ISC_Log::log( 'remove image: ' . $old_id );
    104                 } elseif ( ! empty( $old_id ) ) {
    105                         $meta = get_post_meta( $old_id, 'isc_image_posts', true );
    106                     if ( empty( $meta ) ) {
    107                         ISC_Log::log( sprintf( 'adding isc_image_posts for image %d and post %d', $old_id, $post_id ) );
    108                         self::update_image_posts_meta_with_limit( $old_id, [ $post_id ] );
    109                     } elseif ( is_array( $meta ) && ! in_array( $post_id, $meta ) ) {
    110                         // In case the isc_image_posts is not up to date
    111                         $meta[] = $post_id;
    112                         $meta   = array_unique( $meta );
    113                         ISC_Log::log( sprintf( 'updating isc_image_posts for image %d and posts %s', $old_id, implode( ",\n\t", $meta ) ) );
    114                         self::update_image_posts_meta_with_limit( $old_id, $meta );
    115                     }
    116                 }
    117             }
    118         }
    119 
    120         foreach ( $added_images as $id ) {
    121             $meta = get_post_meta( $id, 'isc_image_posts', true );
    122             if ( ! is_array( $meta ) || $meta === [] ) {
    123                 ISC_Log::log( sprintf( 'adding isc_image_posts for NEW image %d and post %d', $id, $post_id ) );
    124                 self::update_image_posts_meta_with_limit( $id, [ $post_id ] );
    125             } else {
    126                 $meta[] = $post_id;
    127                 $meta   = array_unique( $meta );
    128                 ISC_Log::log( sprintf( 'adding isc_image_posts for NEW image %d and posts %s', $id, implode( ', ', $meta ) ) );
    129                 self::update_image_posts_meta_with_limit( $id, $meta );
    130             }
    131         }
    132 
    133         foreach ( $removed_images as $id ) {
    134             $image_meta = get_post_meta( $id, 'isc_image_posts', true );
    135             if ( is_array( $image_meta ) ) {
    136                 $offset = array_search( $post_id, $image_meta );
    137                 if ( $offset !== false ) {
    138                     array_splice( $image_meta, $offset, 1 );
    139                     $image_meta = array_unique( $image_meta );
    140                     ISC_Log::log( sprintf( 'updating isc_image_posts for REMOVED image %d and posts %s', $id, implode( ', ', $image_meta ) ) );
    141                     self::update_image_posts_meta_with_limit( $id, $image_meta );
    142                 }
    143             }
    144         }
    145     }
    146 
    147     /**
    148      * Update the isc_image_posts meta field with a filtered limit
    149      *
    150      * @param integer $post_id   ID of the target post.
    151      * @param array   $image_ids IDs of the attachments in the content.
    152      */
    153     public static function update_image_posts_meta_with_limit( int $post_id, array $image_ids ) {
    154         // limit the number of post IDs to 10
    155         $image_ids = array_slice( $image_ids, 0, apply_filters( 'isc_image_posts_meta_limit', 10 ) );
    156 
    157         self::update_post_meta( $post_id, 'isc_image_posts', $image_ids );
    158     }
    159 
    160     /**
    161      * Retrieve images added to a post or page and save all information as a post meta value for the post
    162      *
    163      * @param integer $post_id   ID of a post.
    164      * @param array   $image_ids IDs of the attachments in the content.
    165      */
    166     public static function update_post_images_meta( $post_id, $image_ids ) {
    167         // add thumbnail information
    168         $thumb_id = get_post_thumbnail_id( $post_id );
    169 
    170         /**
    171          * If an image is used both inside the post and as post thumbnail, the thumbnail entry overrides the regular image.
    172          */
    173         if ( ! empty( $thumb_id ) ) {
    174             $image_ids[ $thumb_id ] = [
    175                 'src'       => wp_get_attachment_url( $thumb_id ),
    176                 'thumbnail' => true,
    177             ];
    178             ISC_Log::log( 'thumbnail found with ID ' . $thumb_id );
    179         }
    180 
    181         // apply filter to image array, so other developers can add their own logic
    182         $image_ids = apply_filters( 'isc_images_in_posts', $image_ids, $post_id );
    183 
    184         if ( empty( $image_ids ) ) {
    185             $image_ids = [];
    186         }
    187 
    188         ISC_Log::log( 'save isc_post_images with size of ' . count( $image_ids ) );
    189 
    190         self::update_post_meta( $post_id, 'isc_post_images', $image_ids );
    191     }
    192 
    193     /**
    194      * Update attachment meta field
    195      *
    196      * @param integer $att_id attachment post ID.
    197      */
    198     public function attachment_added( $att_id ) {
    199         if ( ! isset( $this->fields ) ) {
    200             return;
    201         }
    202 
    203         foreach ( $this->fields as $field ) {
    204             self::update_post_meta( $att_id, $field['id'], $field['default'] );
    205         }
     64
     65        ISC_Log::log( 'function removed. \ISC\Indexer::update_indexes covers most of it now' );
    20666    }
    20767
     
    262122     * Update image-post index attached to attachments when a post is updated
    263123     *
     124     * @removed since April 2025
     125     *
    264126     * @param int     $post_ID      Post ID.
    265127     * @param WP_Post $post_after   Post object following the update.
     
    269131     */
    270132    public static function update_image_post_meta( $post_ID, $post_after, $post_before ) {
    271         if ( ! \ISC\Indexer::can_save_image_information( $post_ID ) ) {
    272             return;
    273         }
    274 
    275         $image_ids = self::filter_image_ids( $post_before->post_content );
    276         $thumb_id  = get_post_thumbnail_id( $post_ID );
    277         if ( ! empty( $thumb_id ) ) {
    278             $image_ids[ $thumb_id ] = '';
    279         }
    280 
    281         // iterate through all image ids and remove the post ID from their "image_posts" meta data
    282         foreach ( $image_ids as $image_id => $image_src ) {
    283             $meta = get_post_meta( $image_id, 'isc_image_posts', true );
    284             if ( is_array( $meta ) ) {
    285                 unset( $meta[ array_search( $post_ID, $meta ) ] );
    286                 self::update_post_meta( $image_id, 'isc_image_posts', $meta );
    287             }
    288         }
     133        ISC_Log::log( 'function removed without a replacement.' );
    289134    }
    290135
     
    293138     * namely the post meta field `isc_post_images`
    294139     *
     140     * @moved since April 2025
     141     *
    295142     * @param int $post_id Post ID.
    296143     */
    297144    public static function clear_single_post_images_index( $post_id ) {
    298         delete_post_meta( $post_id, 'isc_post_images' );
     145        ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Meta\Post_Images_Meta::delete' );
     146
     147        Post_Meta\Post_Images_Meta::delete( $post_id );
    299148    }
    300149
     
    303152     * namely the post meta field `isc_post_images`
    304153     *
     154     * @moved since April 2025
     155     *
    305156     * @return int|false The number of rows updated, or false on error.
    306157     */
    307158    public static function clear_post_images_index() {
    308         global $wpdb;
    309 
    310         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    311         return $wpdb->delete( $wpdb->postmeta, [ 'meta_key' => 'isc_post_images' ], [ '%s' ] );
     159        ISC_Log::log( 'function moved. \ISC\Image_Sources\Post_Images_Meta::delete_all' );
     160
     161        return Post_Meta\Post_Images_Meta::delete_all();
    312162    }
    313163
     
    316166     * namely the post meta field `isc_image_posts`
    317167     *
     168     * @moved since April 2025
     169     *
    318170     * @return int|false The number of rows updated, or false on error.
    319171     */
    320172    public static function clear_image_posts_index() {
    321         global $wpdb;
    322 
    323         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
    324         return $wpdb->delete( $wpdb->postmeta, [ 'meta_key' => 'isc_image_posts' ], [ '%s' ] );
     173        ISC_Log::log( 'function moved. \ISC\Image_Sources\Image_Posts_Meta::delete_all' );
     174
     175        return Post_Meta\Image_Posts_Meta::delete_all();
    325176    }
    326177
     
    329180     * this concerns the post meta fields `isc_image_posts` and `isc_post_images`
    330181     *
    331      * @return int|false The number of rows updated, or false on error.
    332      */
    333     public static function clear_index() {
    334         $rows_deleted_1 = self::clear_post_images_index();
    335         $rows_deleted_2 = self::clear_image_posts_index();
    336 
    337         if ( $rows_deleted_1 !== false && $rows_deleted_2 !== false ) {
    338             return $rows_deleted_1 + $rows_deleted_2;
    339         }
    340 
    341         return false;
     182     * @deprecated since April 2025
     183     *
     184     * @return bool True if the option was removed
     185     */
     186    public static function clear_index(): bool {
     187        ISC_Log::log( 'function moved. \ISC\Indexer::clear_index' );
     188
     189        return Indexer::clear_index();
    342190    }
    343191
     
    383231        global $wpdb;
    384232
     233        $where_clause = "wp_posts.post_type = 'attachment' AND wp_posts.post_status = 'inherit'";
     234
     235        // Add image type check if images_only is enabled
     236        if ( \ISC\Media_Type_Checker::enabled_images_only_option() ) {
     237            $where_clause .= " AND wp_posts.post_mime_type LIKE 'image/%'";
     238        }
     239
    385240        /**
    386241         * Using EXISTS instead of LEFT JOINs resulted in much faster queries and helped caching the results.
     
    388243        $query = "SELECT wp_posts.ID, wp_posts.post_title, wp_posts.post_parent
    389244            FROM {$wpdb->posts} AS wp_posts
    390             WHERE wp_posts.post_type = 'attachment'
    391               AND wp_posts.post_status = 'inherit'
     245            WHERE {$where_clause}
    392246              AND NOT EXISTS (
    393247                    SELECT 1
     
    418272        // The result of the query is already cached for a day, or until an image is added or removed.
    419273        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
    420         return $wpdb->get_results( $wpdb->prepare( $query, 0, self::MAX_POSTS ) );
     274        $attachments = $wpdb->get_results( $wpdb->prepare( $query, 0, self::MAX_POSTS ) );
     275
     276        $count = count( $attachments );
     277
     278        // update the transient if the number of results differ
     279        if ( $count !== (int) get_transient( 'isc-show-missing-sources-warning' ) ) {
     280            set_transient( 'isc-show-missing-sources-warning', $count, DAY_IN_SECONDS );
     281        }
     282
     283        return $attachments;
    421284    }
    422285
     
    685548     * @param string $key     post meta key.
    686549     * @param mixed  $value   value of the post meta information.
     550     *
     551     * @return bool|int
    687552     */
    688553    public static function update_post_meta( int $post_id, string $key, $value ) {
    689 
    690554        /**
    691555         * Run when any post meta information is stored by ISC
     
    699563        do_action( 'isc_update_post_meta', $post_id, $key, $value );
    700564
    701         update_post_meta( $post_id, $key, $value );
     565        return update_post_meta( $post_id, $key, $value );
    702566    }
    703567
     
    763627        return $file;
    764628    }
     629
     630    /**
     631     * Remove all plugin meta from non-image attachments
     632     *
     633     * @return void
     634     */
     635    public static function remove_plugin_meta_from_non_images() {
     636        // The meta keys used by ISC that should be removed from non-images
     637        $isc_meta_keys = [
     638            'isc_image_source',
     639            'isc_image_source_url',
     640            'isc_image_license',
     641            'isc_image_source_own',
     642            'isc_image_posts',
     643            'isc_possible_usages',
     644            'isc_possible_usages_last_check',
     645        ];
     646
     647        /**
     648         * Filter the meta keys to be removed from non-image attachments
     649         *
     650         * @param array $isc_meta_keys The meta keys to be removed from non-image attachments.
     651         */
     652        apply_filters( 'isc_cleanup_non_image_meta_keys', $isc_meta_keys );
     653
     654        $attachments = get_posts(
     655            [
     656                'post_type'      => 'attachment',
     657                'post_status'    => 'inherit',
     658                'posts_per_page' => -1,
     659            ]
     660        );
     661
     662        if ( empty( $attachments ) ) {
     663            return;
     664        }
     665
     666        foreach ( $attachments as $attachment ) {
     667            if ( ! \ISC\Media_Type_Checker::is_image( $attachment ) ) {
     668                foreach ( $isc_meta_keys as $meta_key ) {
     669                    delete_post_meta( $attachment->ID, $meta_key );
     670                }
     671            }
     672        }
     673    }
    765674}
  • image-source-control-isc/trunk/includes/options.php

    r3253182 r3283881  
    8484        $default['standard_source']           = 'custom_text';
    8585        $default['standard_source_text']      = '';
     86        $default['images_only']               = false;
    8687
    8788        /**
  • image-source-control-isc/trunk/includes/settings/sections/plugin-options.php

    r3253182 r3283881  
    1717        add_settings_section( 'isc_settings_section_plugin', __( 'Plugin options', 'image-source-control-isc' ), '__return_false', 'isc_settings_page' );
    1818        add_settings_field( 'modules', __( 'Modules', 'image-source-control-isc' ), [ $this, 'render_field_modules' ], 'isc_settings_page', 'isc_settings_section_plugin' );
     19        add_settings_field( 'images_only', __( 'Images only', 'image-source-control-isc' ), [ $this, 'render_field_images_only' ], 'isc_settings_page', 'isc_settings_section_plugin' );
    1920        add_settings_field( 'remove_on_uninstall', __( 'Delete data on uninstall', 'image-source-control-isc' ), [ $this, 'render_field_remove_on_uninstall' ], 'isc_settings_page', 'isc_settings_section_plugin' );
    2021    }
     
    3738
    3839    /**
     40     * Render the option to restrict functionality to images only.
     41     */
     42    public function render_field_images_only() {
     43        $options = $this->get_options();
     44        $checked = ! empty( $options['images_only'] );
     45        require_once ISCPATH . '/admin/templates/settings/plugin/images-only.php';
     46    }
     47
     48    /**
    3949     * Render the option to remove all options and meta data when the plugin is deleted.
    4050     */
     
    5464     */
    5565    public function validate_settings( array $output, array $input ): array {
     66        $output['modules']             = ( isset( $input['modules'] ) && is_array( $input['modules'] ) ) ? $input['modules'] : [];
     67        $output['images_only']         = ! empty( $input['images_only'] );
     68        $output['remove_on_uninstall'] = ! empty( $input['remove_on_uninstall'] );
    5669
    57         $output['modules']             = ( isset( $input['modules'] ) && is_array( $input['modules'] ) ) ? $input['modules'] : [];
    58         $output['remove_on_uninstall'] = ! empty( $input['remove_on_uninstall'] );
     70        // Cleanup meta data for non-images
     71        if (
     72            ! empty( $input['images_only'] ) &&
     73            ! empty( $input['images_only_cleanup'] )
     74        ) {
     75            \ISC_Model::remove_plugin_meta_from_non_images();
     76        }
    5977
    6078        return $output;
  • image-source-control-isc/trunk/isc.php

    r3256989 r3283881  
    22/**
    33 * Plugin Name: Image Source Control Lite
    4  * Version: 3.1.4
     4 * Version: 3.2.0
    55 * Plugin URI: https://imagesourcecontrol.com/
    66 * Description: Image Source Control saves the source of an image, lists them and warns if it is missing.
     
    3030}
    3131
    32 define( 'ISCVERSION', '3.1.4' );
     32define( 'ISCVERSION', '3.2.0' );
    3333define( 'ISCNAME', 'Image Source Control' );
    3434define( 'ISCDIR', basename( __DIR__ ) );
  • image-source-control-isc/trunk/lib/composer/autoload_classmap.php

    r3253182 r3283881  
    2424    'ISC\\Image_Sources\\Image_Sources' => $baseDir . '/includes/image-sources/image-sources.php',
    2525    'ISC\\Image_Sources\\Image_Sources_Admin_Scripts' => $baseDir . '/includes/image-sources/admin/scripts.php',
     26    'ISC\\Image_Sources\\Post_Meta\\Image_Posts_Meta' => $baseDir . '/includes/image-sources/Post_Meta/Image_Posts_Meta.php',
     27    'ISC\\Image_Sources\\Post_Meta\\Post_Images_Meta' => $baseDir . '/includes/image-sources/Post_Meta/Post_Images_Meta.php',
    2628    'ISC\\Image_Sources\\Renderer' => $baseDir . '/includes/image-sources/renderer.php',
    2729    'ISC\\Image_Sources\\Renderer\\Caption' => $baseDir . '/includes/image-sources/renderer/caption.php',
     
    2931    'ISC\\Image_Sources\\Utils' => $baseDir . '/includes/image-sources/utils.php',
    3032    'ISC\\Indexer' => $baseDir . '/includes/indexer.php',
     33    'ISC\\Media_Type_Checker' => $baseDir . '/includes/media-type-checker.php',
    3134    'ISC\\Newsletter' => $baseDir . '/includes/newsletter.php',
    3235    'ISC\\Options' => $baseDir . '/includes/options.php',
  • image-source-control-isc/trunk/lib/composer/autoload_static.php

    r3253182 r3283881  
    4444        'ISC\\Image_Sources\\Image_Sources' => __DIR__ . '/../..' . '/includes/image-sources/image-sources.php',
    4545        'ISC\\Image_Sources\\Image_Sources_Admin_Scripts' => __DIR__ . '/../..' . '/includes/image-sources/admin/scripts.php',
     46        'ISC\\Image_Sources\\Post_Meta\\Image_Posts_Meta' => __DIR__ . '/../..' . '/includes/image-sources/Post_Meta/Image_Posts_Meta.php',
     47        'ISC\\Image_Sources\\Post_Meta\\Post_Images_Meta' => __DIR__ . '/../..' . '/includes/image-sources/Post_Meta/Post_Images_Meta.php',
    4648        'ISC\\Image_Sources\\Renderer' => __DIR__ . '/../..' . '/includes/image-sources/renderer.php',
    4749        'ISC\\Image_Sources\\Renderer\\Caption' => __DIR__ . '/../..' . '/includes/image-sources/renderer/caption.php',
     
    4951        'ISC\\Image_Sources\\Utils' => __DIR__ . '/../..' . '/includes/image-sources/utils.php',
    5052        'ISC\\Indexer' => __DIR__ . '/../..' . '/includes/indexer.php',
     53        'ISC\\Media_Type_Checker' => __DIR__ . '/../..' . '/includes/media-type-checker.php',
    5154        'ISC\\Newsletter' => __DIR__ . '/../..' . '/includes/newsletter.php',
    5255        'ISC\\Options' => __DIR__ . '/../..' . '/includes/options.php',
  • image-source-control-isc/trunk/public/public.php

    r3253182 r3283881  
    182182        }
    183183
     184        if ( ISC\Indexer::is_global_list_page( $content ) ) {
     185            ISC_Log::log( 'skipped adding sources because the content contains the Global List' );
     186            return $content;
     187        }
     188
    184189        // return if this is not the main query or within the loop
    185190        if ( ! self::is_main_loop() ) {
     
    629634
    630635        foreach ( $attachments as $_attachment ) {
     636            // skip non-images if option is selected
     637            if ( ! \ISC\Media_Type_Checker::should_process_attachment( $_attachment ) ) {
     638                ISC_Log::log( sprintf( 'skipped image %d because it is not an image', $_attachment->ID ) );
     639                continue;
     640            }
     641
    631642            $connected_atts[ $_attachment->ID ]['source']   = self::get_image_source_text_raw( $_attachment->ID );
    632643            $connected_atts[ $_attachment->ID ]['standard'] = Standard_Source::use_standard_source( $_attachment->ID );
  • image-source-control-isc/trunk/readme.txt

    r3275604 r3283881  
    44Requires at least: 6.0
    55Tested up to: 6.8
    6 Stable tag: 3.1.4
     6Stable tag: 3.2.0
    77Requires PHP: 7.4
    88License: GPLv3 or later
     
    4848* Warn about missing image sources
    4949* Manage, display, and link available licenses
     50* Enable the features for any files in the media library or for images only
     51* Filter the media library list by images with or without sources
    5052
    5153**Featured Image Caption**
     
    6163[Check out all features of Image Source Control](https://imagesourcecontrol.com/?utm_source=wporg&utm_medium=link&utm_campaign=all-features).
    6264
     65* The Indexer looks for all images in all published content in one go
    6366* List credits for images outside the content
    6467* Add multiple links to the source string
     
    6770* Show image usage in the image details and the List view of the media library
    6871* Bulk-edit image copyright information in the media library
     72* Preview image credits in the media library
    6973* Show the standard picture credit for all images without a selected source
    7074* [Display IPTC copyright metadata](https://imagesourcecontrol.com/blog/iptc-copyright-information-image-caption-wordpress/) in the backend and automatically as a standard source in the frontend
     
    8084
    8185Extended compatibility with Elementor, Avada, WP Bakery, and other page builders
    82 as well as with plugins like Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
     86as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
    8387
    8488[See Pricing](https://imagesourcecontrol.com/pricing/?utm_source=wporg&utm_medium=link&utm_campaign=pricing).
     
    1511551. Customizing the global list of image sources
    1521561. Manage image usage licenses
     1571. The Indexer searches for all images in published content
    153158
    154159== Changelog ==
     160
     161= 3.2.0 =
     162
     163- Feature: You can use the option “Images only” to disable features for non-images in the media library, e.g., PDF files
     164- Improvement: When authors change content, ISC now looks for removed or new images at the next visit of that page in the frontend. This highly improved compatibility with page builders and dynamic content like shortcodes.
     165- Improvement: When WP_DEBUG is enabled, show a button to list the content of the internal storage on the Tools page
     166- Improvement: Removes image source output on pages with the Global List on it
     167- Improvement (Pro): Ignores image URLs in `href` attributes when looking for image sources
     168- Improvement (Pro): Clears the URL storage when the Indexer runs. This can help with issues when a site was migrated to another URL
     169- Improvement (Pro): Extends ignored options for unused images
     170- Improvement (Pro): The column with image sources forms in the Media Library list view only shows if the user has the permission to edit any image information
     171- Improvement (Pro): The forms to edit image sources in the Media Library list view only show if the user has the permission to edit information for that given image
     172- Improvement (Pro): The Indexer for Unused Images now works with posts translated by WPML
     173- Fix: Removes old index information when the last image in a post is removed
     174- Fix (Pro): Some reserved characters in URLs caused (e.g., `&`) the bulk edit fields for images sources in the media library to be cut off
     175- Dev: Extracts post meta handling (`isc_image_posts`, `isc_post_images`) into dedicated classes.
     176- Dev (Pro): Disable image source form fields in the Media Library list view when submitting the filter form to prevent broken URLs. This is related to a compatibility issue caused by a third-party setup
     177- Dev (Pro): The Indexer now works in batches to prevent timeouts on large sites when Query Monitor is installed
     178- Dev (Pro): Pages with the Global List shortcode are now ignored by the Indexer.
     179- Dev: Adds cleanup routines for meta data for deleted and trashed posts.
     180- Dev: Replace some direct DB calls with WP functions
     181- Updates German translation
    155182
    156183= 3.1.4 =
  • image-source-control-isc/trunk/uninstall.php

    r3253182 r3283881  
    1010
    1111    // delete post meta fields
    12     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_post_images' ), array( '%s' ) );
    13     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_posts' ), array( '%s' ) );
    14     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source' ), array( '%s' ) );
    15     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source_own' ), array( '%s' ) );
    16     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_image_source_url' ), array( '%s' ) );
    17     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_possible_usages' ), array( '%s' ) );
    18     $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => 'isc_possible_usages_last_check' ), array( '%s' ) );
     12    delete_post_meta_by_key( 'isc_post_images' );
     13    delete_post_meta_by_key( 'isc_image_posts' );
     14    delete_post_meta_by_key( 'isc_image_source' );
     15    delete_post_meta_by_key( 'isc_image_source_own' );
     16    delete_post_meta_by_key( 'isc_image_source_url' );
     17    delete_post_meta_by_key( 'isc_possible_usages' );
     18    delete_post_meta_by_key( 'isc_possible_usages_last_check' );
     19    delete_post_meta_by_key( 'isc_post_images_before_update' );
    1920
    2021    // delete main plugin options
Note: See TracChangeset for help on using the changeset viewer.