Plugin Directory

Changeset 3203556


Ignore:
Timestamp:
12/06/2024 11:10:22 AM (4 months ago)
Author:
infinitnet
Message:

v2.3.5 release

Location:
content-update-scheduler
Files:
2 edited
3 copied

Legend:

Unmodified
Added
Removed
  • content-update-scheduler/tags/2.3.5/content-update-scheduler.php

    r3194088 r3203556  
    88 * Author: Infinitnet
    99 * Author URI: https://infinitnet.io/
    10  * Version: 2.3.4
     10 * Version: 2.3.5
    1111 * License: GPLv3
    1212 * Text Domain: content-update-scheduler
     
    8888
    8989    /**
    90      * Handles the CSS copying for Elementor and Oxygen plugins.
    91      *
    92      * @param int $source_post_id The source post ID.
    93      * @param int $destination_post_id The destination post ID.
    94      *
    95      * @return void
    96      */
    97     private static function handle_plugin_css_copy($source_post_id, $destination_post_id)
    98     {
    99         // Elementor plugin active.
    100         if (in_array('elementor/elementor.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     90     * Copies Elementor-specific data between posts
     91     *
     92     * @param int $source_id Source post ID
     93     * @param int $destination_id Destination post ID
     94     * @return bool Success status
     95     */
     96    private static function copy_elementor_data($source_id, $destination_id) {
     97        // Early exit if Elementor isn't active
     98        if (!defined('ELEMENTOR_VERSION')) {
     99            return false;
     100        }
     101
     102        try {
     103            // Core Elementor meta keys that must be preserved
     104            $elementor_meta_keys = [
     105                '_elementor_data',
     106                '_elementor_edit_mode',
     107                '_elementor_page_settings',
     108                '_elementor_version',
     109                '_elementor_template_type',
     110                '_elementor_controls_usage'
     111            ];
     112
     113            // Get all meta at once for efficiency
     114            $source_meta = get_post_meta($source_id);
     115           
     116            foreach ($elementor_meta_keys as $key) {
     117                if (!isset($source_meta[$key][0])) {
     118                    continue;
     119                }
     120
     121                $value = $source_meta[$key][0];
     122               
     123                // Special handling for Elementor's JSON data
     124                if ($key === '_elementor_data') {
     125                    // Ensure valid JSON
     126                    $decoded = json_decode($value);
     127                    if (json_last_error() === JSON_ERROR_NONE) {
     128                        // Preserve exact JSON structure with proper slashing
     129                        update_post_meta($destination_id, $key, wp_slash($value));
     130                    } else {
     131                        error_log(sprintf(
     132                            'Content Update Scheduler: Invalid Elementor JSON in post %d',
     133                            $source_id
     134                        ));
     135                    }
     136                    continue;
     137                }
     138
     139                // Copy other Elementor meta directly
     140                update_post_meta($destination_id, $key, maybe_unserialize($value));
     141            }
     142
     143            // Copy Elementor CSS file
    101144            $upload_dir = wp_upload_dir();
    102             $source_css = $upload_dir['basedir'] . '/elementor/css/post-' . $source_post_id . '.css';
    103             $destination_css = $upload_dir['basedir'] . '/elementor/css/post-' . $destination_post_id . '.css';
    104 
     145            $source_css = $upload_dir['basedir'] . '/elementor/css/post-' . $source_id . '.css';
     146            $dest_css = $upload_dir['basedir'] . '/elementor/css/post-' . $destination_id . '.css';
     147           
    105148            if (file_exists($source_css)) {
    106                 copy($source_css, $destination_css);
    107             }
    108 
    109         }
    110 
    111         // Oxygen plugin active.
    112         if (in_array('oxygen/functions.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     149                @copy($source_css, $dest_css);
     150            }
     151
     152            return true;
     153
     154        } catch (Exception $e) {
     155            error_log(sprintf(
     156                'Content Update Scheduler: Error copying Elementor data: %s',
     157                $e->getMessage()
     158            ));
     159            return false;
     160        }
     161    }
     162
     163    /**
     164     * Handles the Oxygen builder CSS copying
     165     *
     166     * @param int $source_id Source post ID
     167     * @param int $destination_id Destination post ID
     168     * @return bool Success status
     169     */
     170    private static function handle_wpml_relationships($source_id, $destination_id, $is_publishing = false) {
     171        // Early exit if WPML isn't active
     172        if (!defined('ICL_SITEPRESS_VERSION')) {
     173            return false;
     174        }
     175
     176        try {
     177            // Basic validation
     178            $post_type = get_post_type($source_id);
     179            if (!$post_type || $post_type !== get_post_type($destination_id)) {
     180                return false;
     181            }
     182
     183            $element_type = 'post_' . $post_type;
     184
     185            // Get source language details
     186            $source_details = apply_filters('wpml_element_language_details', null, array(
     187                'element_id' => $source_id,
     188                'element_type' => $element_type
     189            ));
     190
     191            if (!$source_details) {
     192                return false;
     193            }
     194
     195            /**
     196             * Filter whether to create new translation group
     197             *
     198             * @param bool $create_new_group Whether to create new translation group
     199             * @param int $source_id Source post ID
     200             * @param int $destination_id Destination post ID
     201             * @param bool $is_publishing Whether this is a publish operation
     202             */
     203            $create_new_group = apply_filters(
     204                'content_update_scheduler_wpml_new_translation_group',
     205                !$is_publishing,
     206                $source_id,
     207                $destination_id,
     208                $is_publishing
     209            );
     210
     211            // Set language details
     212            do_action('wpml_set_element_language_details', array(
     213                'element_id' => $destination_id,
     214                'element_type' => $element_type,
     215                'trid' => $create_new_group ? null : apply_filters('wpml_element_trid', null, $source_id, $element_type),
     216                'language_code' => $source_details->language_code,
     217                'source_language_code' => $source_details->source_language_code
     218            ));
     219
     220            // Copy essential WPML meta
     221            $wpml_meta_keys = array(
     222                '_wpml_media_featured',
     223                '_wpml_media_duplicate',
     224                '_wpml_media_processed'
     225            );
     226
     227            foreach ($wpml_meta_keys as $meta_key) {
     228                $value = get_post_meta($source_id, $meta_key, true);
     229                if ($value !== '') {
     230                    update_post_meta($destination_id, $meta_key, $value);
     231                }
     232            }
     233
     234            /**
     235             * Fires after WPML relationships have been handled
     236             *
     237             * @param int $source_id Source post ID
     238             * @param int $destination_id Destination post ID
     239             * @param bool $is_publishing Whether this is a publish operation
     240             */
     241            do_action('content_update_scheduler_after_wpml_handling',
     242                $source_id,
     243                $destination_id,
     244                $is_publishing
     245            );
     246
     247            return true;
     248
     249        } catch (Exception $e) {
     250            error_log(sprintf(
     251                'Content Update Scheduler: WPML handling error for posts %d->%d: %s',
     252                $source_id,
     253                $destination_id,
     254                $e->getMessage()
     255            ));
     256            return false;
     257        }
     258    }
     259
     260    private static function copy_oxygen_data($source_id, $destination_id) {
     261        // Early exit if Oxygen isn't active
     262        if (!in_array('oxygen/functions.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     263            return false;
     264        }
     265
     266        try {
    113267            $upload_dir = wp_upload_dir();
    114             $dir = $upload_dir['basedir'] . '/oxygen/css/' . get_post_field('post_name', $source_post_id) . '-' . $source_post_id . '.css';
    115             $chdir = $upload_dir['basedir'] . '/oxygen/css/' . get_post_field('post_name', $destination_post_id) . '-' . $destination_post_id . '.css';
    116 
    117             if (!file_exists($chdir)) {
    118                 fopen($chdir, "w");
    119             }
    120             if (!file_exists($dir)) {
    121                 fopen($dir, "w");
    122             }
    123             copy($dir, $chdir);
     268            $source_css = $upload_dir['basedir'] . '/oxygen/css/' .
     269                         get_post_field('post_name', $source_id) . '-' . $source_id . '.css';
     270            $dest_css = $upload_dir['basedir'] . '/oxygen/css/' .
     271                       get_post_field('post_name', $destination_id) . '-' . $destination_id . '.css';
     272
     273            // Create destination file if it doesn't exist
     274            if (!file_exists($dest_css)) {
     275                @touch($dest_css);
     276            }
     277
     278            // Copy CSS if source exists
     279            if (file_exists($source_css)) {
     280                @copy($source_css, $dest_css);
     281            }
     282
     283            return true;
     284
     285        } catch (Exception $e) {
     286            error_log(sprintf(
     287                'Content Update Scheduler: Error copying Oxygen data: %s',
     288                $e->getMessage()
     289            ));
     290            return false;
    124291        }
    125292    }
     
    804971        }
    805972
    806         self::handle_plugin_css_copy($post->ID, $new_post_id);
     973        self::copy_elementor_data($post->ID, $new_post_id);
     974        self::copy_oxygen_data($post->ID, $new_post_id);
     975        self::handle_wpml_relationships($post->ID, $new_post_id);
    807976
    808977        // copy meta and terms over to the new post.
     
    8861055    }
    8871056
    888     public static function copy_meta_and_terms($source_post_id, $destination_post_id, $restore_references = false)
     1057    /**
     1058     * Copies meta and terms from one post to another
     1059     *
     1060     * @param int  $source_post_id      The post from which to copy.
     1061     * @param int  $destination_post_id The post which will get the meta and terms.
     1062     * @param bool $restore_references  Whether to restore references to the original post ID.
     1063     *
     1064     * @return void
     1065     */
     1066    private static function copy_meta_and_terms($source_post_id, $destination_post_id, $restore_references = false)
    8891067    {
    8901068        $source_post = get_post($source_post_id);
    8911069        $destination_post = get_post($destination_post_id);
    8921070
    893         // abort if any of the ids is not a post.
    894         if (! $source_post || ! $destination_post) {
     1071        // Abort if any of the ids is not a post.
     1072        if (!$source_post || !$destination_post) {
    8951073            return;
    8961074        }
    8971075
    898         // Copy meta
    899         $meta = get_post_meta($source_post->ID);
    900         foreach ($meta as $key => $values) {
    901             delete_post_meta($destination_post->ID, $key);
    902             foreach ($values as $value) {
    903                 $processed_value = self::copy_meta_value($value);
    904                
    905                 if ($restore_references && is_string($processed_value) &&
    906                     strpos($processed_value, (string)$source_post->ID) !== false) {
    907                     $processed_value = str_replace(
    908                         (string)$source_post->ID,
    909                         (string)$destination_post->ID,
    910                         $processed_value
    911                     );
    912                 }
    913                
    914                 add_post_meta($destination_post->ID, $key, $processed_value);
    915             }
    916         }
    917 
    918 
    919         // and now for copying the terms.
    920         $taxonomies = get_object_taxonomies($source_post->post_type);
    921         foreach ($taxonomies as $taxonomy) {
    922             $post_terms = wp_get_object_terms($source_post->ID, $taxonomy, array(
    923                 'orderby' => 'term_order',
    924             ));
    925             $terms = array();
    926             foreach ($post_terms as $term) {
    927                 $terms[] = $term->slug;
    928             }
    929             // reset taxonomy to empty.
    930             wp_set_object_terms($destination_post->ID, null, $taxonomy);
    931             // then add new terms.
    932             wp_set_object_terms($destination_post->ID, $terms, $taxonomy);
     1076        // Store current kses status and temporarily disable filters.
     1077        $should_filter = ! current_filter('content_save_pre');
     1078        if ($should_filter) {
     1079            remove_filter('content_save_pre', 'wp_filter_post_kses');
     1080            remove_filter('db_insert_value', 'wp_filter_kses');
     1081        }
     1082
     1083        try {
     1084            // Copy meta.
     1085            $meta = get_post_meta($source_post->ID);
     1086            foreach ($meta as $key => $values) {
     1087                delete_post_meta($destination_post->ID, $key);
     1088                foreach ($values as $value) {
     1089                    $processed_value = self::copy_meta_value($value);
     1090                   
     1091                    if ($restore_references && is_string($processed_value) &&
     1092                        strpos($processed_value, (string)$source_post->ID) !== false) {
     1093                        $processed_value = str_replace(
     1094                            (string)$source_post->ID,
     1095                            (string)$destination_post->ID,
     1096                            $processed_value
     1097                        );
     1098                    }
     1099                   
     1100                    add_post_meta($destination_post->ID, $key, $processed_value);
     1101                }
     1102            }
     1103
     1104            // Copy terms.
     1105            $taxonomies = get_object_taxonomies($source_post->post_type);
     1106            foreach ($taxonomies as $taxonomy) {
     1107                $post_terms = wp_get_object_terms($source_post->ID, $taxonomy, array(
     1108                    'orderby' => 'term_order',
     1109                ));
     1110                $terms = array();
     1111                foreach ($post_terms as $term) {
     1112                    $terms[] = $term->slug;
     1113                }
     1114                wp_set_object_terms($destination_post->ID, null, $taxonomy);
     1115                wp_set_object_terms($destination_post->ID, $terms, $taxonomy);
     1116            }
     1117        } finally {
     1118            // Restore filters if they were active.
     1119            if ($should_filter) {
     1120                add_filter('content_save_pre', 'wp_filter_post_kses');
     1121                add_filter('db_insert_value', 'wp_filter_kses');
     1122            }
    9331123        }
    9341124    }
     
    11041294            $original_stock_quantity = get_post_meta($orig->ID, '_stock', true);
    11051295
    1106             self::handle_plugin_css_copy($post->ID, $orig_id);
     1296            self::copy_elementor_data($post->ID, $orig_id);
     1297            self::copy_oxygen_data($post->ID, $orig_id);
     1298            self::handle_wpml_relationships($post->ID, $orig_id, true);
    11071299
    11081300            /**
  • content-update-scheduler/tags/2.3.5/readme.txt

    r3194096 r3203556  
    33Tags: schedule, scheduling, update, republish, publication
    44Requires at least: 5.0
    5 Tested up to: 6.7
    6 Stable tag: 2.3.4
     5Tested up to: 6.7.1
     6Stable tag: 2.3.5
    77Requires PHP: 7.4
    88License: GPLv3
     
    6262
    6363== Changelog ==
     64
     65= 2.3.5 =
     66* refactor: Improve meta and terms copying with filter management and visibility
     67* refactor: Improve Elementor and Oxygen data copying with focused, robust methods
     68* refactor: Add WPML relationship handling for duplicated posts
    6469
    6570= 2.3.4 =
  • content-update-scheduler/trunk/content-update-scheduler.php

    r3194088 r3203556  
    88 * Author: Infinitnet
    99 * Author URI: https://infinitnet.io/
    10  * Version: 2.3.4
     10 * Version: 2.3.5
    1111 * License: GPLv3
    1212 * Text Domain: content-update-scheduler
     
    8888
    8989    /**
    90      * Handles the CSS copying for Elementor and Oxygen plugins.
    91      *
    92      * @param int $source_post_id The source post ID.
    93      * @param int $destination_post_id The destination post ID.
    94      *
    95      * @return void
    96      */
    97     private static function handle_plugin_css_copy($source_post_id, $destination_post_id)
    98     {
    99         // Elementor plugin active.
    100         if (in_array('elementor/elementor.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     90     * Copies Elementor-specific data between posts
     91     *
     92     * @param int $source_id Source post ID
     93     * @param int $destination_id Destination post ID
     94     * @return bool Success status
     95     */
     96    private static function copy_elementor_data($source_id, $destination_id) {
     97        // Early exit if Elementor isn't active
     98        if (!defined('ELEMENTOR_VERSION')) {
     99            return false;
     100        }
     101
     102        try {
     103            // Core Elementor meta keys that must be preserved
     104            $elementor_meta_keys = [
     105                '_elementor_data',
     106                '_elementor_edit_mode',
     107                '_elementor_page_settings',
     108                '_elementor_version',
     109                '_elementor_template_type',
     110                '_elementor_controls_usage'
     111            ];
     112
     113            // Get all meta at once for efficiency
     114            $source_meta = get_post_meta($source_id);
     115           
     116            foreach ($elementor_meta_keys as $key) {
     117                if (!isset($source_meta[$key][0])) {
     118                    continue;
     119                }
     120
     121                $value = $source_meta[$key][0];
     122               
     123                // Special handling for Elementor's JSON data
     124                if ($key === '_elementor_data') {
     125                    // Ensure valid JSON
     126                    $decoded = json_decode($value);
     127                    if (json_last_error() === JSON_ERROR_NONE) {
     128                        // Preserve exact JSON structure with proper slashing
     129                        update_post_meta($destination_id, $key, wp_slash($value));
     130                    } else {
     131                        error_log(sprintf(
     132                            'Content Update Scheduler: Invalid Elementor JSON in post %d',
     133                            $source_id
     134                        ));
     135                    }
     136                    continue;
     137                }
     138
     139                // Copy other Elementor meta directly
     140                update_post_meta($destination_id, $key, maybe_unserialize($value));
     141            }
     142
     143            // Copy Elementor CSS file
    101144            $upload_dir = wp_upload_dir();
    102             $source_css = $upload_dir['basedir'] . '/elementor/css/post-' . $source_post_id . '.css';
    103             $destination_css = $upload_dir['basedir'] . '/elementor/css/post-' . $destination_post_id . '.css';
    104 
     145            $source_css = $upload_dir['basedir'] . '/elementor/css/post-' . $source_id . '.css';
     146            $dest_css = $upload_dir['basedir'] . '/elementor/css/post-' . $destination_id . '.css';
     147           
    105148            if (file_exists($source_css)) {
    106                 copy($source_css, $destination_css);
    107             }
    108 
    109         }
    110 
    111         // Oxygen plugin active.
    112         if (in_array('oxygen/functions.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     149                @copy($source_css, $dest_css);
     150            }
     151
     152            return true;
     153
     154        } catch (Exception $e) {
     155            error_log(sprintf(
     156                'Content Update Scheduler: Error copying Elementor data: %s',
     157                $e->getMessage()
     158            ));
     159            return false;
     160        }
     161    }
     162
     163    /**
     164     * Handles the Oxygen builder CSS copying
     165     *
     166     * @param int $source_id Source post ID
     167     * @param int $destination_id Destination post ID
     168     * @return bool Success status
     169     */
     170    private static function handle_wpml_relationships($source_id, $destination_id, $is_publishing = false) {
     171        // Early exit if WPML isn't active
     172        if (!defined('ICL_SITEPRESS_VERSION')) {
     173            return false;
     174        }
     175
     176        try {
     177            // Basic validation
     178            $post_type = get_post_type($source_id);
     179            if (!$post_type || $post_type !== get_post_type($destination_id)) {
     180                return false;
     181            }
     182
     183            $element_type = 'post_' . $post_type;
     184
     185            // Get source language details
     186            $source_details = apply_filters('wpml_element_language_details', null, array(
     187                'element_id' => $source_id,
     188                'element_type' => $element_type
     189            ));
     190
     191            if (!$source_details) {
     192                return false;
     193            }
     194
     195            /**
     196             * Filter whether to create new translation group
     197             *
     198             * @param bool $create_new_group Whether to create new translation group
     199             * @param int $source_id Source post ID
     200             * @param int $destination_id Destination post ID
     201             * @param bool $is_publishing Whether this is a publish operation
     202             */
     203            $create_new_group = apply_filters(
     204                'content_update_scheduler_wpml_new_translation_group',
     205                !$is_publishing,
     206                $source_id,
     207                $destination_id,
     208                $is_publishing
     209            );
     210
     211            // Set language details
     212            do_action('wpml_set_element_language_details', array(
     213                'element_id' => $destination_id,
     214                'element_type' => $element_type,
     215                'trid' => $create_new_group ? null : apply_filters('wpml_element_trid', null, $source_id, $element_type),
     216                'language_code' => $source_details->language_code,
     217                'source_language_code' => $source_details->source_language_code
     218            ));
     219
     220            // Copy essential WPML meta
     221            $wpml_meta_keys = array(
     222                '_wpml_media_featured',
     223                '_wpml_media_duplicate',
     224                '_wpml_media_processed'
     225            );
     226
     227            foreach ($wpml_meta_keys as $meta_key) {
     228                $value = get_post_meta($source_id, $meta_key, true);
     229                if ($value !== '') {
     230                    update_post_meta($destination_id, $meta_key, $value);
     231                }
     232            }
     233
     234            /**
     235             * Fires after WPML relationships have been handled
     236             *
     237             * @param int $source_id Source post ID
     238             * @param int $destination_id Destination post ID
     239             * @param bool $is_publishing Whether this is a publish operation
     240             */
     241            do_action('content_update_scheduler_after_wpml_handling',
     242                $source_id,
     243                $destination_id,
     244                $is_publishing
     245            );
     246
     247            return true;
     248
     249        } catch (Exception $e) {
     250            error_log(sprintf(
     251                'Content Update Scheduler: WPML handling error for posts %d->%d: %s',
     252                $source_id,
     253                $destination_id,
     254                $e->getMessage()
     255            ));
     256            return false;
     257        }
     258    }
     259
     260    private static function copy_oxygen_data($source_id, $destination_id) {
     261        // Early exit if Oxygen isn't active
     262        if (!in_array('oxygen/functions.php', apply_filters('active_plugins', get_option('active_plugins')))) {
     263            return false;
     264        }
     265
     266        try {
    113267            $upload_dir = wp_upload_dir();
    114             $dir = $upload_dir['basedir'] . '/oxygen/css/' . get_post_field('post_name', $source_post_id) . '-' . $source_post_id . '.css';
    115             $chdir = $upload_dir['basedir'] . '/oxygen/css/' . get_post_field('post_name', $destination_post_id) . '-' . $destination_post_id . '.css';
    116 
    117             if (!file_exists($chdir)) {
    118                 fopen($chdir, "w");
    119             }
    120             if (!file_exists($dir)) {
    121                 fopen($dir, "w");
    122             }
    123             copy($dir, $chdir);
     268            $source_css = $upload_dir['basedir'] . '/oxygen/css/' .
     269                         get_post_field('post_name', $source_id) . '-' . $source_id . '.css';
     270            $dest_css = $upload_dir['basedir'] . '/oxygen/css/' .
     271                       get_post_field('post_name', $destination_id) . '-' . $destination_id . '.css';
     272
     273            // Create destination file if it doesn't exist
     274            if (!file_exists($dest_css)) {
     275                @touch($dest_css);
     276            }
     277
     278            // Copy CSS if source exists
     279            if (file_exists($source_css)) {
     280                @copy($source_css, $dest_css);
     281            }
     282
     283            return true;
     284
     285        } catch (Exception $e) {
     286            error_log(sprintf(
     287                'Content Update Scheduler: Error copying Oxygen data: %s',
     288                $e->getMessage()
     289            ));
     290            return false;
    124291        }
    125292    }
     
    804971        }
    805972
    806         self::handle_plugin_css_copy($post->ID, $new_post_id);
     973        self::copy_elementor_data($post->ID, $new_post_id);
     974        self::copy_oxygen_data($post->ID, $new_post_id);
     975        self::handle_wpml_relationships($post->ID, $new_post_id);
    807976
    808977        // copy meta and terms over to the new post.
     
    8861055    }
    8871056
    888     public static function copy_meta_and_terms($source_post_id, $destination_post_id, $restore_references = false)
     1057    /**
     1058     * Copies meta and terms from one post to another
     1059     *
     1060     * @param int  $source_post_id      The post from which to copy.
     1061     * @param int  $destination_post_id The post which will get the meta and terms.
     1062     * @param bool $restore_references  Whether to restore references to the original post ID.
     1063     *
     1064     * @return void
     1065     */
     1066    private static function copy_meta_and_terms($source_post_id, $destination_post_id, $restore_references = false)
    8891067    {
    8901068        $source_post = get_post($source_post_id);
    8911069        $destination_post = get_post($destination_post_id);
    8921070
    893         // abort if any of the ids is not a post.
    894         if (! $source_post || ! $destination_post) {
     1071        // Abort if any of the ids is not a post.
     1072        if (!$source_post || !$destination_post) {
    8951073            return;
    8961074        }
    8971075
    898         // Copy meta
    899         $meta = get_post_meta($source_post->ID);
    900         foreach ($meta as $key => $values) {
    901             delete_post_meta($destination_post->ID, $key);
    902             foreach ($values as $value) {
    903                 $processed_value = self::copy_meta_value($value);
    904                
    905                 if ($restore_references && is_string($processed_value) &&
    906                     strpos($processed_value, (string)$source_post->ID) !== false) {
    907                     $processed_value = str_replace(
    908                         (string)$source_post->ID,
    909                         (string)$destination_post->ID,
    910                         $processed_value
    911                     );
    912                 }
    913                
    914                 add_post_meta($destination_post->ID, $key, $processed_value);
    915             }
    916         }
    917 
    918 
    919         // and now for copying the terms.
    920         $taxonomies = get_object_taxonomies($source_post->post_type);
    921         foreach ($taxonomies as $taxonomy) {
    922             $post_terms = wp_get_object_terms($source_post->ID, $taxonomy, array(
    923                 'orderby' => 'term_order',
    924             ));
    925             $terms = array();
    926             foreach ($post_terms as $term) {
    927                 $terms[] = $term->slug;
    928             }
    929             // reset taxonomy to empty.
    930             wp_set_object_terms($destination_post->ID, null, $taxonomy);
    931             // then add new terms.
    932             wp_set_object_terms($destination_post->ID, $terms, $taxonomy);
     1076        // Store current kses status and temporarily disable filters.
     1077        $should_filter = ! current_filter('content_save_pre');
     1078        if ($should_filter) {
     1079            remove_filter('content_save_pre', 'wp_filter_post_kses');
     1080            remove_filter('db_insert_value', 'wp_filter_kses');
     1081        }
     1082
     1083        try {
     1084            // Copy meta.
     1085            $meta = get_post_meta($source_post->ID);
     1086            foreach ($meta as $key => $values) {
     1087                delete_post_meta($destination_post->ID, $key);
     1088                foreach ($values as $value) {
     1089                    $processed_value = self::copy_meta_value($value);
     1090                   
     1091                    if ($restore_references && is_string($processed_value) &&
     1092                        strpos($processed_value, (string)$source_post->ID) !== false) {
     1093                        $processed_value = str_replace(
     1094                            (string)$source_post->ID,
     1095                            (string)$destination_post->ID,
     1096                            $processed_value
     1097                        );
     1098                    }
     1099                   
     1100                    add_post_meta($destination_post->ID, $key, $processed_value);
     1101                }
     1102            }
     1103
     1104            // Copy terms.
     1105            $taxonomies = get_object_taxonomies($source_post->post_type);
     1106            foreach ($taxonomies as $taxonomy) {
     1107                $post_terms = wp_get_object_terms($source_post->ID, $taxonomy, array(
     1108                    'orderby' => 'term_order',
     1109                ));
     1110                $terms = array();
     1111                foreach ($post_terms as $term) {
     1112                    $terms[] = $term->slug;
     1113                }
     1114                wp_set_object_terms($destination_post->ID, null, $taxonomy);
     1115                wp_set_object_terms($destination_post->ID, $terms, $taxonomy);
     1116            }
     1117        } finally {
     1118            // Restore filters if they were active.
     1119            if ($should_filter) {
     1120                add_filter('content_save_pre', 'wp_filter_post_kses');
     1121                add_filter('db_insert_value', 'wp_filter_kses');
     1122            }
    9331123        }
    9341124    }
     
    11041294            $original_stock_quantity = get_post_meta($orig->ID, '_stock', true);
    11051295
    1106             self::handle_plugin_css_copy($post->ID, $orig_id);
     1296            self::copy_elementor_data($post->ID, $orig_id);
     1297            self::copy_oxygen_data($post->ID, $orig_id);
     1298            self::handle_wpml_relationships($post->ID, $orig_id, true);
    11071299
    11081300            /**
  • content-update-scheduler/trunk/readme.txt

    r3194096 r3203556  
    33Tags: schedule, scheduling, update, republish, publication
    44Requires at least: 5.0
    5 Tested up to: 6.7
    6 Stable tag: 2.3.4
     5Tested up to: 6.7.1
     6Stable tag: 2.3.5
    77Requires PHP: 7.4
    88License: GPLv3
     
    6262
    6363== Changelog ==
     64
     65= 2.3.5 =
     66* refactor: Improve meta and terms copying with filter management and visibility
     67* refactor: Improve Elementor and Oxygen data copying with focused, robust methods
     68* refactor: Add WPML relationship handling for duplicated posts
    6469
    6570= 2.3.4 =
Note: See TracChangeset for help on using the changeset viewer.