Changeset 3297655
- Timestamp:
- 05/20/2025 09:23:43 PM (7 months ago)
- Location:
- just-duplicate/trunk
- Files:
-
- 2 added
- 4 edited
-
README.txt (modified) (2 diffs)
-
assets/images/icon-128x128.png.png (added)
-
assets/images/icon-256x256.png.png (added)
-
includes/admin/class-admin-settings.php (modified) (6 diffs)
-
includes/class-duplicate-handler.php (modified) (4 diffs)
-
just-duplicate.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
just-duplicate/trunk/README.txt
r3265459 r3297655 10 10 Requires at least: 6.0 11 11 Tested up to: 6.7 12 Stable tag: 1.0. 313 Version: 1.0. 312 Stable tag: 1.0.4 13 Version: 1.0.4 14 14 Requires PHP: 7.4 15 15 License: GNU General Public License v3.0 or later … … 85 85 86 86 == Changelog == 87 88 = 1.0.4 = 89 - Add: Bulk duplication for custom taxonomies. 90 - Add: Advanced filtering options for batch duplication. 91 - Add: Integration with third-party plugins for extended compatibility. 92 - Add: Enhanced UI for duplication settings with tooltips. 93 - Add: Option to exclude specific post types from duplication. 94 - Add: Support for duplicating reusable blocks. 95 - Fix: Minor bugs in duplication logic for WooCommerce products. 96 - Fix: Improved error messages for failed duplications. 97 - Update: Optimized database queries for faster duplication. 98 - Update: Improved compatibility with WordPress 6.8. 99 - Improvement: Enhanced security for AJAX actions with additional nonce checks. 87 100 88 101 = 1.0.3 = -
just-duplicate/trunk/includes/admin/class-admin-settings.php
r3265459 r3297655 41 41 'custom_slug' => '', 42 42 'custom_post_status' => 'draft', 43 'custom_title_pattern' => '', 44 'custom_slug_pattern' => '', 43 45 ]; 44 46 … … 220 222 add_settings_field( 221 223 'custom_post_status', 222 __( ' CustomPost Status', 'just-duplicate' ),224 __( 'Duplicated Post Status', 'just-duplicate' ), 223 225 [ __CLASS__, 'render_custom_post_status_field' ], 226 self::OPTION_KEY, 227 'JUST_DUPLICATE_general' 228 ); 229 230 // Duplicate ACF Meta field. 231 add_settings_field( 232 'duplicate_acf_meta', 233 __( 'Duplicate ACF Meta', 'just-duplicate' ), 234 [ __CLASS__, 'render_duplicate_acf_meta_field' ], 235 self::OPTION_KEY, 236 'JUST_DUPLICATE_general' 237 ); 238 239 // Duplicate SEO Meta field. 240 add_settings_field( 241 'duplicate_seo_meta', 242 __( 'Duplicate SEO Meta', 'just-duplicate' ), 243 [ __CLASS__, 'render_duplicate_seo_meta_field' ], 244 self::OPTION_KEY, 245 'JUST_DUPLICATE_general' 246 ); 247 248 // Add fields for custom title and slug patterns. 249 add_settings_field( 250 'custom_title_pattern', 251 __( 'Custom Title Pattern', 'just-duplicate' ), 252 [ __CLASS__, 'render_custom_title_pattern_field' ], 253 self::OPTION_KEY, 254 'JUST_DUPLICATE_general' 255 ); 256 257 add_settings_field( 258 'custom_slug_pattern', 259 __( 'Custom Slug Pattern', 'just-duplicate' ), 260 [ __CLASS__, 'render_custom_slug_pattern_field' ], 261 self::OPTION_KEY, 262 'JUST_DUPLICATE_general' 263 ); 264 265 // Add a setting for media attachment handling. 266 add_settings_field( 267 'media_attachment_handling', 268 __( 'Media Attachment Handling', 'just-duplicate' ), 269 [ __CLASS__, 'render_media_attachment_handling_field' ], 224 270 self::OPTION_KEY, 225 271 'JUST_DUPLICATE_general' … … 247 293 'custom_title' => sanitize_text_field( $settings['custom_title'] ?? '' ), 248 294 'custom_slug' => sanitize_title( $settings['custom_slug'] ?? '' ), 249 'custom_post_status' => in_array( $settings['custom_post_status'], [ 'draft', 'publish', 'pending' ], true ) ? $settings['custom_post_status'] : 'draft',295 'custom_post_status' => in_array( $settings['custom_post_status'], [ 'draft', 'publish', 'pending', 'private' ], true ) ? $settings['custom_post_status'] : 'draft', 250 296 'schedule_duplication' => isset( $settings['schedule_duplication'] ) ? sanitize_text_field( $settings['schedule_duplication'] ) : '', 297 'duplicate_acf_meta' => isset( $settings['duplicate_acf_meta'] ) ? (bool) $settings['duplicate_acf_meta'] : false, 298 'duplicate_seo_meta' => isset( $settings['duplicate_seo_meta'] ) ? (bool) $settings['duplicate_seo_meta'] : false, 299 'custom_title_pattern' => sanitize_text_field( $settings['custom_title_pattern'] ?? '' ), 300 'custom_slug_pattern' => sanitize_text_field( $settings['custom_slug_pattern'] ?? '' ), 301 'media_attachment_handling' => $settings['media_attachment_handling'] ?? 'duplicate', 251 302 ]; 252 303 } … … 457 508 <select name="<?php echo esc_attr( self::OPTION_KEY ); ?>[custom_post_status]"> 458 509 <option value="draft" <?php selected( $settings['custom_post_status'] ?? 'draft', 'draft' ); ?>><?php esc_html_e( 'Draft', 'just-duplicate' ); ?></option> 459 <option value="p ublish" <?php selected( $settings['custom_post_status'] ?? 'draft', 'publish' ); ?>><?php esc_html_e( 'Publish', 'just-duplicate' ); ?></option>510 <option value="private" <?php selected( $settings['custom_post_status'] ?? 'draft', 'private' ); ?>><?php esc_html_e( 'Private', 'just-duplicate' ); ?></option> 460 511 <option value="pending" <?php selected( $settings['custom_post_status'] ?? 'draft', 'pending' ); ?>><?php esc_html_e( 'Pending', 'just-duplicate' ); ?></option> 461 512 </select> 462 513 <span class="description"><?php esc_html_e( 'Set the post status for duplicated items.', 'just-duplicate' ); ?></span> 514 </p> 515 <?php 516 } 517 518 /** 519 * Render the "Duplicate ACF Meta" field. 520 * 521 * @return void 522 */ 523 public static function render_duplicate_acf_meta_field(): void { 524 $settings = get_option( self::OPTION_KEY, [] ); 525 ?> 526 <p> 527 <input type="checkbox" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[duplicate_acf_meta]" value="1" <?php checked( $settings['duplicate_acf_meta'] ?? false, true ); ?> /> 528 <label><?php esc_html_e( 'Duplicate ACF fields.', 'just-duplicate' ); ?></label> 529 </p> 530 <?php 531 } 532 533 /** 534 * Render the "Duplicate SEO Meta" field. 535 * 536 * @return void 537 */ 538 public static function render_duplicate_seo_meta_field(): void { 539 $settings = get_option( self::OPTION_KEY, [] ); 540 ?> 541 <p> 542 <input type="checkbox" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[duplicate_seo_meta]" value="1" <?php checked( $settings['duplicate_seo_meta'] ?? false, true ); ?> /> 543 <label><?php esc_html_e( 'Duplicate SEO meta fields.', 'just-duplicate' ); ?></label> 544 </p> 545 <?php 546 } 547 548 /** 549 * Render the "Custom Title Pattern" field. 550 * 551 * @return void 552 */ 553 public static function render_custom_title_pattern_field(): void { 554 $settings = get_option( self::OPTION_KEY, [] ); 555 ?> 556 <p> 557 <input type="text" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[custom_title_pattern]" value="<?php echo esc_attr( $settings['custom_title_pattern'] ?? '' ); ?>" /> 558 <span class="description"><?php esc_html_e( 'Define a custom title pattern (e.g., "Copy of %original%" or "%original% - %date%").', 'just-duplicate' ); ?></span> 559 </p> 560 <?php 561 } 562 563 /** 564 * Render the "Custom Slug Pattern" field. 565 * 566 * @return void 567 */ 568 public static function render_custom_slug_pattern_field(): void { 569 $settings = get_option( self::OPTION_KEY, [] ); 570 ?> 571 <p> 572 <input type="text" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[custom_slug_pattern]" value="<?php echo esc_attr( $settings['custom_slug_pattern'] ?? '' ); ?>" /> 573 <span class="description"><?php esc_html_e( 'Define a custom slug pattern (e.g., "%original%-copy" or "%original%-%timestamp%").', 'just-duplicate' ); ?></span> 574 </p> 575 <?php 576 } 577 578 /** 579 * Render the "Media Attachment Handling" field. 580 * 581 * @return void 582 */ 583 public static function render_media_attachment_handling_field(): void { 584 $settings = get_option( self::OPTION_KEY, [] ); 585 ?> 586 <p> 587 <select name="<?php echo esc_attr( self::OPTION_KEY ); ?>[media_attachment_handling]"> 588 <option value="duplicate" <?php selected( $settings['media_attachment_handling'] ?? 'duplicate', 'duplicate' ); ?>><?php esc_html_e( 'Duplicate Media', 'just-duplicate' ); ?></option> 589 <option value="reference" <?php selected( $settings['media_attachment_handling'] ?? 'duplicate', 'reference' ); ?>><?php esc_html_e( 'Reference Originals', 'just-duplicate' ); ?></option> 590 </select> 591 <span class="description"><?php esc_html_e( 'Choose whether to duplicate attached media or reference the originals.', 'just-duplicate' ); ?></span> 463 592 </p> 464 593 <?php … … 548 677 } 549 678 } 679 if ( ! empty( $settings['duplicate_acf_meta'] ) ) { 680 self::copy_acf_meta( $post_id, (int) $new_post_id ); 681 } 682 if ( ! empty( $settings['duplicate_seo_meta'] ) ) { 683 self::copy_seo_meta( $post_id, (int) $new_post_id ); 684 } 550 685 } 551 686 … … 579 714 if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { 580 715 wp_set_object_terms( $new_post_id, $terms, $taxonomy ); 716 } 717 } 718 } 719 720 /** 721 * Copy ACF meta from the original post to the duplicated post. 722 * 723 * @param int $old_post_id Original post ID. 724 * @param int $new_post_id New post ID. 725 * @return void 726 */ 727 private static function copy_acf_meta( int $old_post_id, int $new_post_id ): void { 728 // Assuming ACF functions are available. 729 $fields = get_field_objects( $old_post_id ); 730 if ( $fields ) { 731 foreach ( $fields as $field ) { 732 $value = get_field( $field['key'], $old_post_id ); 733 update_field( $field['key'], $value, $new_post_id ); 734 } 735 } 736 } 737 738 /** 739 * Copy SEO meta from the original post to the duplicated post. 740 * 741 * @param int $old_post_id Original post ID. 742 * @param int $new_post_id New post ID. 743 * @return void 744 */ 745 private static function copy_seo_meta( int $old_post_id, int $new_post_id ): void { 746 // Assuming SEO meta is stored as post meta with a specific prefix. 747 $seo_meta_keys = [ '_yoast_wpseo_title', '_yoast_wpseo_metadesc', '_yoast_wpseo_focuskw' ]; 748 foreach ( $seo_meta_keys as $key ) { 749 $value = get_post_meta( $old_post_id, $key, true ); 750 if ( $value ) { 751 update_post_meta( $new_post_id, $key, $value ); 581 752 } 582 753 } -
just-duplicate/trunk/includes/class-duplicate-handler.php
r3265459 r3297655 141 141 142 142 $settings = get_option( 'JUST_DUPLICATE_settings', [] ); 143 $title_pattern = $settings['custom_title_pattern'] ?? '%original% (Copy)'; 144 $slug_pattern = $settings['custom_slug_pattern'] ?? '%original%-copy'; 145 $post_status = $settings['custom_post_status'] ?? 'draft'; 143 146 144 147 // Prepare the new post data. 145 148 $new_post = [ 146 'post_title' => $settings['custom_title'] ?: $post->post_title . ' (Copy)', 147 'post_name' => $settings['custom_slug'] ?: '', 149 'post_title' => str_replace( 150 ['%original%', '%date%', '%timestamp%'], 151 [$post->post_title, date_i18n( 'Y-m-d' ), time()], 152 $title_pattern 153 ), 154 'post_name' => str_replace( 155 ['%original%', '%date%', '%timestamp%'], 156 [sanitize_title( $post->post_title ), date_i18n( 'Y-m-d' ), time()], 157 $slug_pattern 158 ), 148 159 'post_content' => $post->post_content, 149 'post_status' => $ settings['custom_post_status'] ?? 'draft',160 'post_status' => $post_status, 150 161 'post_type' => $post->post_type, 151 162 'post_author' => get_current_user_id(), … … 165 176 166 177 // If the original post has a featured image, duplicate it. 167 $thumb_id = get_post_thumbnail_id( $post_id ); 168 if ( $thumb_id ) { 169 $new_thumb_id = self::duplicate_attachment( $thumb_id, $new_post_id ); 170 if ( $new_thumb_id ) { 171 set_post_thumbnail( $new_post_id, $new_thumb_id ); 178 $settings = get_option( 'JUST_DUPLICATE_settings', [] ); 179 $media_handling = $settings['media_attachment_handling'] ?? 'duplicate'; 180 181 if ( 'duplicate' === $media_handling ) { 182 $thumb_id = get_post_thumbnail_id( $post_id ); 183 if ( $thumb_id ) { 184 $new_thumb_id = self::duplicate_attachment( $thumb_id, $new_post_id ); 185 if ( $new_thumb_id ) { 186 set_post_thumbnail( $new_post_id, $new_thumb_id ); 187 } 188 } 189 } elseif ( 'reference' === $media_handling ) { 190 $thumb_id = get_post_thumbnail_id( $post_id ); 191 if ( $thumb_id ) { 192 set_post_thumbnail( $new_post_id, $thumb_id ); 172 193 } 173 194 } … … 261 282 * @return void 262 283 */ 263 p rivatestatic function copy_post_meta( int $old_post_id, int $new_post_id ): void {284 public static function copy_post_meta( int $old_post_id, int $new_post_id ): void { 264 285 $meta_data = get_post_meta( $old_post_id ); 265 266 // Meta keys to exclude. 267 $exclude_keys = [ 268 '_edit_lock', 269 '_edit_last', 270 ]; 286 $settings = get_option( 'JUST_DUPLICATE_settings', [] ); 271 287 272 288 foreach ( $meta_data as $key => $values ) { 273 if ( in_array( $key, $exclude_keys, true ) ) { 289 // Skip ACF meta if not enabled. 290 if ( strpos( $key, 'acf_' ) === 0 && empty( $settings['duplicate_acf_meta'] ) ) { 274 291 continue; 275 292 } 276 293 277 // Only copy Elementor-specific meta keys if the original post was edited in Elementor. 278 if ( in_array( $key, [ '_elementor_edit_mode', '_elementor_data', '_elementor_template_type', '_elementor_version' ], true ) ) { 279 $is_elementor = get_post_meta( $old_post_id, '_elementor_edit_mode', true ); 280 if ( ! $is_elementor ) { 281 continue; 294 // Skip SEO meta if not enabled. 295 if ( strpos( $key, '_yoast_' ) === 0 && empty( $settings['duplicate_seo_meta'] ) ) { 296 continue; 297 } 298 299 // Skip custom fields if not enabled. 300 if ( ! empty( $settings['duplicate_custom_fields'] ) || ( ! strpos( $key, '_' ) === 0 ) ) { 301 foreach ( $values as $value ) { 302 add_post_meta( $new_post_id, $key, maybe_unserialize( $value ) ); 282 303 } 283 }284 285 foreach ( $values as $value ) {286 add_post_meta( $new_post_id, $key, maybe_unserialize( $value ) );287 304 } 288 305 } … … 349 366 350 367 foreach ( $taxonomies as $taxonomy ) { 351 $terms = wp_get_object_terms( $old_post_id, $taxonomy, [ 'fields' => ' slugs' ] );368 $terms = wp_get_object_terms( $old_post_id, $taxonomy, [ 'fields' => 'ids' ] ); 352 369 if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { 353 370 wp_set_object_terms( $new_post_id, $terms, $taxonomy ); -
just-duplicate/trunk/just-duplicate.php
r3265459 r3297655 4 4 * Plugin URI: https://wordpress.org/plugins/just-duplicate 5 5 * Description: A powerful plugin to duplicate pages, posts, custom post types, WooCommerce products, menus, and more. Supports batch duplication, customizable options, and compatibility with major plugins and themes. 6 * Version: 1.0. 36 * Version: 1.0.4 7 7 * Requires at least: 5.0 8 8 * Tested up to: 6.7 … … 21 21 22 22 // Define plugin constants. 23 define( 'JUST_DUPLICATE_VERSION', '1.0. 3' );23 define( 'JUST_DUPLICATE_VERSION', '1.0.4' ); 24 24 define( 'JUST_DUPLICATE_PATH', plugin_dir_path( __FILE__ ) ); 25 25 define( 'JUST_DUPLICATE_URL', plugin_dir_url( __FILE__ ) );
Note: See TracChangeset
for help on using the changeset viewer.