false, 'label' => '' ); } return $d; } public static function get_slots_config( $raw = false ) { $stored = get_option( self::OPTION_KEY, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } $out = self::default_config(); foreach ( $out as $i => $def ) { if ( isset( $stored[ $i ] ) && is_array( $stored[ $i ] ) ) { $out[ $i ] = array( 'enabled' => ! empty( $stored[ $i ]['enabled'] ), 'label' => isset( $stored[ $i ]['label'] ) ? (string) $stored[ $i ]['label'] : $def['label'], ); } if ( ! $raw && '' === trim( (string) $out[ $i ]['label'] ) ) { $out[ $i ]['label'] = self::slot_label( $i ); } } return $out; } public static function get_enabled_slots() { $out = array(); foreach ( self::get_slots_config() as $i => $s ) { if ( ! empty( $s['enabled'] ) && '' !== trim( (string) $s['label'] ) ) { $out[ $i ] = $s; } } return $out; } private static function table() { global $wpdb; return $wpdb->prefix . 'aieo_dmm_custom_tabs'; } private static function table_ok() { global $wpdb; static $ok = null; if ( null === $ok ) { $t = self::table(); $ok = ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) === $t ); } return $ok; } public static function get_all_content( $product_id ) { $product_id = (int) $product_id; if ( $product_id <= 0 || ! self::table_ok() ) { return array(); } global $wpdb; $t = self::table(); $rows = (array) $wpdb->get_results( $wpdb->prepare( "SELECT slot_index, content FROM `{$t}` WHERE product_id = %d", $product_id ), ARRAY_A ); $out = array(); foreach ( $rows as $r ) { $out[ (int) $r['slot_index'] ] = (string) $r['content']; } return $out; } public static function save_content( $product_id, $slot, $html ) { $product_id = (int) $product_id; $slot = (int) $slot; if ( $product_id <= 0 || $slot < 1 || $slot > self::SLOT_COUNT || ! self::table_ok() ) { return; } global $wpdb; $t = self::table(); $html = (string) $html; if ( '' === trim( $html ) ) { $wpdb->delete( $t, array( 'product_id' => $product_id, 'slot_index' => $slot ), array( '%d', '%d' ) ); return; } $wpdb->replace( $t, array( 'product_id' => $product_id, 'slot_index' => $slot, 'content' => $html, 'updated_at' => current_time( 'mysql', true ), ), array( '%d', '%d', '%s', '%s' ) ); } public function register_hooks() { add_action( 'wp_ajax_aieo_dmm_custom_tabs_save', array( $this, 'ajax_save' ) ); add_action( 'wp_ajax_aieo_dmm_custom_tabs_migrate', array( $this, 'ajax_migrate' ) ); add_filter( 'woocommerce_product_data_tabs', array( $this, 'add_product_data_tab' ) ); add_action( 'woocommerce_product_data_panels', array( $this, 'render_product_data_panel' ) ); add_action( 'woocommerce_process_product_meta', array( $this, 'save_product_meta' ) ); add_filter( 'woocommerce_product_tabs', array( $this, 'add_tabs' ) ); add_action( 'init', array( $this, 'maybe_replace_legacy_tabs' ), 20 ); } public function maybe_replace_legacy_tabs() { if ( empty( self::get_enabled_slots() ) ) { return; } if ( function_exists( 'custom_pods_product_tabs' ) ) { remove_filter( 'woocommerce_product_tabs', 'custom_pods_product_tabs' ); remove_filter( 'woocommerce_product_tabs', 'custom_pods_product_tabs', 10 ); } } public function add_tabs( $tabs ) { global $product; if ( ! $product || ! is_a( $product, 'WC_Product' ) ) { return $tabs; } $content = self::get_all_content( $product->get_id() ); foreach ( self::get_enabled_slots() as $i => $slot ) { $html = isset( $content[ $i ] ) ? trim( (string) $content[ $i ] ) : ''; if ( '' === $html ) { continue; } $tabs[ 'aieo_custom_tab_' . (int) $i ] = array( 'title' => $slot['label'], 'priority' => 50 + (int) $i, 'callback' => static function () use ( $html ) { echo wpautop( wp_kses_post( $html ) ); }, ); } return $tabs; } public function add_product_data_tab( $tabs ) { $tabs['aieo_custom_tabs'] = array( 'label' => __( 'Custom Tabs', 'ai-eshop-optimizer' ), 'target' => 'aieo_custom_tabs_product_data', 'priority' => 66, 'class' => array(), ); return $tabs; } public function render_product_data_panel() { global $post; echo '
'; return; } $content = self::get_all_content( (int) $post->ID ); echo '' . esc_html( $slot['label'] ) . '
'; wp_editor( isset( $content[ $i ] ) ? (string) $content[ $i ] : '', 'aieo_custom_tab_' . (int) $i, array( 'textarea_name' => 'aieo_custom_tab_' . (int) $i, 'media_buttons' => true, 'tinymce' => true, 'quicktags' => true, 'textarea_rows' => 8, ) ); } echo '' . esc_html( sprintf( _n( '%s Pods field will be copied into Custom Tabs (usage into Tab 1, specification into Tab 2). Content those tabs already hold for the same products is overwritten.', '%s Pods fields will be copied into Custom Tabs (usage into Tab 1, specification into Tab 2). Content those tabs already hold for the same products is overwritten.', $pods_rows, 'ai-eshop-optimizer' ), number_format_i18n( $pods_rows ) ) ) . '
', array( 'confirm' => __( 'Migrate from Pods', 'ai-eshop-optimizer' ) ) ); } $job = \Rooster\Admin\States::job( array( 'id' => 'aieo-ct-job', 'state' => $migrated ? 'idle' : 'not-run', 'run' => __( 'Migrate from Pods (usage / specification)', 'ai-eshop-optimizer' ), 'run_attrs' => array( 'id' => 'aieo-ct-migrate' ) + $ask_attrs, 'primary' => false, 'result' => $migrated ? __( 'Last migration', 'ai-eshop-optimizer' ) : '', 'last' => $migrated ? $migrated : 0, ) ); echo $job; echo $dialog; echo $close; rooster_admin_band_close(); $migrate_js = ''; echo $migrate_js; } public function ajax_save() { AIEO_DMM::verify_ajax_request(); $clean = array(); for ( $i = 1; $i <= self::SLOT_COUNT; $i++ ) { $clean[ $i ] = array( 'enabled' => ! empty( $_POST[ 'enabled_' . $i ] ), 'label' => isset( $_POST[ 'label_' . $i ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'label_' . $i ] ) ) : '', ); } update_option( self::OPTION_KEY, $clean, false ); wp_send_json_success( array( 'message' => __( 'Custom Tabs saved.', 'ai-eshop-optimizer' ) ) ); } public function ajax_migrate() { AIEO_DMM::verify_ajax_request(); @ob_end_clean(); $stats = self::run_migration(); wp_send_json_success( array( 'stats' => $stats, 'migrated_at' => time() ) ); } public static function run_migration() { global $wpdb; $stats = array( 'processed' => 0, 'migrated' => 0, 'skipped' => 0 ); if ( ! self::table_ok() ) { return $stats; } $map = array( 'usage' => 1, 'specification' => 2 ); $rows = (array) $wpdb->get_results( "SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_key IN ('usage','specification') AND TRIM(meta_value) <> ''", ARRAY_A ); foreach ( $rows as $r ) { $stats['processed']++; $slot = isset( $map[ $r['meta_key'] ] ) ? (int) $map[ $r['meta_key'] ] : 0; if ( ! $slot ) { $stats['skipped']++; continue; } self::save_content( (int) $r['post_id'], $slot, wp_kses_post( (string) $r['meta_value'] ) ); $stats['migrated']++; } $cfg = self::get_slots_config( true ); $changed = false; $seed = array( 1 => 'Εφαρμογή', 2 => 'Συστατικά' ); foreach ( $seed as $i => $greek ) { if ( empty( $cfg[ $i ]['enabled'] ) ) { $cfg[ $i ]['enabled'] = true; if ( '' === trim( (string) $cfg[ $i ]['label'] ) || in_array( $cfg[ $i ]['label'], array( 'Application', 'Ingredients' ), true ) ) { $cfg[ $i ]['label'] = $greek; } $changed = true; } } if ( $changed ) { update_option( self::OPTION_KEY, $cfg, false ); } update_option( self::MIGRATED_OPTION, time(), false ); return $stats; } }