self::CAPABILITY ); } /** * Submenu under Products. */ public function menu(): void { add_submenu_page( 'edit.php?post_type=product', __( 'Product tab settings', 'pnscripts-tabcrest' ), __( 'Tab settings', 'pnscripts-tabcrest' ), self::CAPABILITY, self::SLUG, array( $this, 'render' ) ); } /** * Bookmarks of the 1.0.x page (page=pnscripts-product-tabs) open the renamed page. */ public function redirect_legacy_slug(): void { $url = self::legacy_redirect_url(); if ( null !== $url ) { wp_safe_redirect( $url ); exit; } } /** * Where a request for the 1.0.x page slug should go, or null when it is not one. */ public static function legacy_redirect_url(): ?string { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only redirect between two admin pages. $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( self::LEGACY_SLUG !== $page || ! current_user_can( self::CAPABILITY ) ) { return null; } return self::url( $tab ); } /** * Register the option with its sanitiser. */ public function register_setting(): void { register_setting( self::GROUP, Settings::OPTION, array( 'type' => 'array', 'sanitize_callback' => array( Settings::class, 'sanitize' ), 'show_in_rest' => false, 'default' => Settings::defaults(), ) ); } /** * "Settings" link on the Plugins screen. * * @param mixed $links Links. * @return array */ public function action_links( mixed $links ): array { $links = is_array( $links ) ? $links : array(); array_unshift( $links, sprintf( '%s', esc_url( self::url() ), esc_html__( 'Settings', 'pnscripts-tabcrest' ) ), sprintf( '%s', esc_url( admin_url( 'edit.php?post_type=' . GlobalTabs::POST_TYPE ) ), esc_html__( 'Global tabs', 'pnscripts-tabcrest' ) ) ); return $links; } /** * Page URL. * * @param string $tab Page tab. */ public static function url( string $tab = '' ): string { $args = array( 'post_type' => 'product', 'page' => self::SLUG, ); if ( '' !== $tab ) { $args['tab'] = $tab; } return add_query_arg( $args, admin_url( 'edit.php' ) ); } /** * Assets on our page. * * @param string $hook_suffix Page hook. */ public function enqueue( string $hook_suffix ): void { if ( 'product_page_' . self::SLUG !== $hook_suffix ) { return; } wp_enqueue_style( 'pnscripts-product-tabs-admin', PNSCRIPTS_PRODUCT_TABS_URL . 'assets/css/admin.css', array(), PNSCRIPTS_PRODUCT_TABS_VERSION ); wp_enqueue_script( 'pnscripts-product-tabs-import', PNSCRIPTS_PRODUCT_TABS_URL . 'assets/js/admin-import.js', array(), PNSCRIPTS_PRODUCT_TABS_VERSION, true ); wp_localize_script( 'pnscripts-product-tabs-import', 'pnscriptsProductTabsImport', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'action' => self::AJAX_ACTION, 'nonce' => wp_create_nonce( self::AJAX_ACTION ), 'confirmImport' => __( 'Import the YIKES tabs now? YIKES data is not changed and you can undo the import.', 'pnscripts-tabcrest' ), 'confirmUndo' => __( 'Remove all tabs created by the import (tabs you added yourself stay)?', 'pnscripts-tabcrest' ), /* translators: %d: number of products handled so far */ 'running' => __( 'Working… %d products handled', 'pnscripts-tabcrest' ), 'failed' => __( 'The request failed. Reload the page and try again.', 'pnscripts-tabcrest' ), 'doneDry' => __( 'Dry run finished: nothing was changed. These numbers are what an import will do.', 'pnscripts-tabcrest' ), 'doneImport' => __( 'Import finished. Check a few product pages, then deactivate YIKES Custom Product Tabs.', 'pnscripts-tabcrest' ), 'doneUndo' => __( 'Import removed.', 'pnscripts-tabcrest' ), /* translators: %d: number of further notes */ 'moreNotes' => __( '… and %d more notes like these.', 'pnscripts-tabcrest' ), 'labels' => self::report_labels(), ) ); } /** * Labels of the report counters. * * @return array */ public static function report_labels(): array { return array( 'products_total' => __( 'Products with YIKES tabs', 'pnscripts-tabcrest' ), 'saved_found' => __( 'YIKES saved tabs', 'pnscripts-tabcrest' ), 'saved_created' => __( 'Global tabs to create / created', 'pnscripts-tabcrest' ), 'saved_existing' => __( 'Saved tabs imported before (left as they are)', 'pnscripts-tabcrest' ), 'products_imported' => __( 'Products to import / imported', 'pnscripts-tabcrest' ), 'products_unchanged' => __( 'Products unchanged since the last import (skipped)', 'pnscripts-tabcrest' ), 'tabs_custom' => __( 'Product tabs copied', 'pnscripts-tabcrest' ), 'tabs_linked' => __( 'Product tabs linked to a global tab', 'pnscripts-tabcrest' ), 'tabs_disabled_empty' => __( 'Tabs without a title (imported switched off)', 'pnscripts-tabcrest' ), 'tabs_disabled_duplicate' => __( 'Tabs hidden by a duplicate title in YIKES (imported switched off)', 'pnscripts-tabcrest' ), 'replaced_defaults' => __( 'Default tabs YIKES replaced (hidden on those products)', 'pnscripts-tabcrest' ), 'removed_globals' => __( 'Imported global tabs removed', 'pnscripts-tabcrest' ), 'removed_products' => __( 'Products cleaned', 'pnscripts-tabcrest' ), ); } /** * AJAX: one import batch. */ public function ajax_import(): void { check_ajax_referer( self::AJAX_ACTION, 'nonce' ); if ( ! current_user_can( self::CAPABILITY ) ) { wp_send_json_error( array( 'message' => __( 'You are not allowed to import tabs.', 'pnscripts-tabcrest' ) ), 403 ); } $mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : YikesImporter::MODE_DRY; $cursor = isset( $_POST['cursor'] ) ? absint( wp_unslash( $_POST['cursor'] ) ) : 0; if ( ! in_array( $mode, array( YikesImporter::MODE_DRY, YikesImporter::MODE_IMPORT, YikesImporter::MODE_UNDO ), true ) ) { wp_send_json_error( array( 'message' => 'mode' ), 400 ); } wp_send_json_success( $this->importer->run_batch( $mode, $cursor ) ); } /** * Page. */ public function render(): void { if ( ! current_user_can( self::CAPABILITY ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only navigation. $tab = isset( $_GET['tab'] ) && 'import' === sanitize_key( wp_unslash( $_GET['tab'] ) ) ? 'import' : 'settings'; ?>

render_import(); } else { $this->render_settings(); } ?>
settings->all(); $name = Settings::OPTION; $labels = ProductPanel::default_labels(); ?>

%d' ); ?>

/>

', esc_attr( Settings::OPTION . '[' . $key . ']' ), checked( $checked, true, false ), esc_html( $label ) ); } /** * Import screen. */ private function render_import(): void { $products = $this->importer->count_products(); $active = class_exists( 'YIKES_Custom_Product_Tabs_Display', false ) || defined( 'YIKES_Custom_Product_Tabs_Version' ); $last = (int) get_option( YikesImporter::LAST_RUN, 0 ); ?>

0 ? wp_date( get_option( 'date_format', 'Y-m-d' ) . ' ' . get_option( 'time_format', 'H:i' ), $last ) : false; if ( is_string( $when ) ) { echo ' · '; printf( /* translators: %s: date and time */ esc_html__( 'last import: %s', 'pnscripts-tabcrest' ), esc_html( $when ) ); } ?>

id ) { return; } if ( get_user_meta( get_current_user_id(), self::DISMISS_KEY, true ) || (int) get_option( YikesImporter::LAST_RUN, 0 ) > 0 ) { return; } if ( 0 === $this->importer->count_products() ) { return; } $dismiss = wp_nonce_url( add_query_arg( self::DISMISS_KEY, '1' ), self::DISMISS_KEY ); wp_enqueue_script( 'pnscripts-product-tabs-notice', PNSCRIPTS_PRODUCT_TABS_URL . 'assets/js/admin-notice.js', array(), PNSCRIPTS_PRODUCT_TABS_VERSION, true ); printf( '', esc_html__( 'PN Scripts Tabcrest found tabs from Custom Product Tabs for WooCommerce (YIKES). Import them in one click; YIKES data stays untouched.', 'pnscripts-tabcrest' ), esc_url( self::url( 'import' ) ), esc_html__( 'Review the import', 'pnscripts-tabcrest' ), esc_url( $dismiss ), esc_html__( 'Dismiss', 'pnscripts-tabcrest' ) ); } /** * Store the dismissal. */ public function maybe_dismiss(): void { if ( ! isset( $_GET[ self::DISMISS_KEY ], $_GET['_wpnonce'] ) ) { return; } if ( ! wp_verify_nonce( sanitize_key( wp_unslash( $_GET['_wpnonce'] ) ), self::DISMISS_KEY ) ) { return; } update_user_meta( get_current_user_id(), self::DISMISS_KEY, 1 ); wp_safe_redirect( remove_query_arg( array( self::DISMISS_KEY, '_wpnonce' ) ) ); exit; } }