Templates // ------------------------------------------------------------------------- public static function row_actions( array $actions, WP_Post $post ): array { if ( self::POST_TYPE !== $post->post_type ) return $actions; if ( ! current_user_can( 'manage_options' ) ) return $actions; $url = wp_nonce_url( add_query_arg( [ 'action' => 'eppro_duplicate_template_get', 'post_id' => $post->ID, ], admin_url( 'admin.php?page=eppro-templates' ) ), self::NONCE_ACTION . '_' . $post->ID ); $actions['eppro_duplicate'] = sprintf( '%s', esc_url( $url ), esc_html__( 'Duplicate', 'admore-widgets-suite' ) ); return $actions; } // ------------------------------------------------------------------------- // Handle GET-based duplicate (from row action) // ------------------------------------------------------------------------- public static function handle_duplicate(): void { if ( ! isset( $_GET['action'] ) || 'eppro_duplicate_template_get' !== $_GET['action'] ) return; if ( ! current_user_can( 'manage_options' ) ) return; $post_id = absint( $_GET['post_id'] ?? 0 ); check_admin_referer( self::NONCE_ACTION . '_' . $post_id ); $new_id = self::do_duplicate( $post_id ); if ( is_wp_error( $new_id ) ) { wp_die( esc_html( $new_id->get_error_message() ) ); } wp_safe_redirect( admin_url( 'admin.php?page=eppro-templates&duplicated=1' ) ); exit; } // ------------------------------------------------------------------------- // AJAX duplicate (called from admin page JS) // ------------------------------------------------------------------------- public static function ajax_duplicate(): void { check_ajax_referer( 'eppro_admin_nonce', '_wpnonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 ); } $post_id = absint( $_POST['post_id'] ?? 0 ); $new_id = self::do_duplicate( $post_id ); if ( is_wp_error( $new_id ) ) { wp_send_json_error( [ 'message' => $new_id->get_error_message() ] ); } wp_send_json_success( [ 'reload' => true, 'new_id' => $new_id ] ); } // ------------------------------------------------------------------------- // Core duplication logic // ------------------------------------------------------------------------- /** * Meta that must not be copied: per-post caches (Elementor would think the * copy's CSS file already exists), editing locks and popup view counts. */ private const SKIP_META = [ '_elementor_css', '_elementor_page_assets', '_elementor_element_cache', '_edit_lock', '_edit_last', '_wp_old_slug', '_eppro_popup_views' ]; /** * Duplicate an Elementor template, popup or Theme Builder template as a draft. * Used by the Templates, Popups and Theme Builder admin screens. */ public static function do_duplicate( int $post_id ): int|WP_Error { $orig = get_post( $post_id ); $types = [ self::POST_TYPE, EPPRO_Popup_CPT::POST_TYPE ]; if ( class_exists( 'EPPRO_Theme_Builder_CPT' ) ) { $types[] = EPPRO_Theme_Builder_CPT::POST_TYPE; } if ( ! $orig || ! in_array( $orig->post_type, $types, true ) ) { return new WP_Error( 'invalid_post', __( 'Post not found.', 'admore-widgets-suite' ) ); } // wp_insert_post() and add_post_meta() unslash their input, so slash it // first — otherwise backslashes in content and in Elementor's JSON data // ("_elementor_data") are stripped and the copy breaks in the editor. $new_id = wp_insert_post( wp_slash( [ 'post_title' => $orig->post_title . ' ' . __( '(Copy)', 'admore-widgets-suite' ), 'post_type' => $orig->post_type, 'post_status' => 'draft', 'post_content' => $orig->post_content, 'post_author' => get_current_user_id(), 'post_excerpt' => $orig->post_excerpt, ] ), true ); if ( is_wp_error( $new_id ) ) return $new_id; // Copy all meta $all_meta = get_post_meta( $post_id ); foreach ( $all_meta as $key => $values ) { if ( in_array( $key, self::SKIP_META, true ) ) continue; foreach ( $values as $value ) { add_post_meta( $new_id, $key, wp_slash( maybe_unserialize( $value ) ) ); } } // For EPP popups, start as inactive if ( EPPRO_Popup_CPT::POST_TYPE === $orig->post_type ) { $popup_meta = get_post_meta( $new_id, EPPRO_Popup_Settings::META_KEY, true ); if ( is_array( $popup_meta ) ) { $popup_meta['active'] = false; update_post_meta( $new_id, EPPRO_Popup_Settings::META_KEY, $popup_meta ); } } // Copy taxonomy terms $taxonomies = get_object_taxonomies( $orig->post_type ); foreach ( $taxonomies as $taxonomy ) { $terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'ids' ] ); if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { wp_set_object_terms( $new_id, $terms, $taxonomy ); } } return $new_id; } // ------------------------------------------------------------------------- // Admin page: list Elementor templates // ------------------------------------------------------------------------- public static function render_page(): void { if ( ! current_user_can( 'manage_options' ) ) return; $duplicated = ! empty( $_GET['duplicated'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only success notice flag. $templates = get_posts( [ 'post_type' => self::POST_TYPE, 'post_status' => [ 'publish', 'draft', 'private' ], 'posts_per_page' => -1, 'orderby' => 'date', 'order' => 'DESC', ] ); ?>

ID, '_elementor_template_type', true ); $edit_url = add_query_arg( [ 'post' => $tpl->ID, 'action' => 'elementor' ], admin_url( 'post.php' ) ); $status_label = get_post_status_object( $tpl->post_status )->label ?? $tpl->post_status; $badge_class = 'publish' === $tpl->post_status ? 'eppro-badge-active' : 'eppro-badge-draft'; ?>