Settings > Products > Tabs. * * @package Prodtabs */ namespace Prodtabs; defined( 'ABSPATH' ) || exit; /** * Section of the WooCommerce settings. */ final class Admin_Settings { const SECTION = 'prodtabs'; /** User meta set when the welcome box is closed. */ const WELCOME_DISMISSED = 'prodtabs_welcome_dismissed'; /** Hooks. */ public static function init(): void { add_filter( 'woocommerce_get_sections_products', array( __CLASS__, 'section' ) ); add_filter( 'woocommerce_get_settings_products', array( __CLASS__, 'fields' ), 10, 2 ); add_action( 'woocommerce_admin_field_prodtabs_welcome', array( __CLASS__, 'field_welcome' ) ); add_action( 'woocommerce_admin_field_prodtabs_natives', array( __CLASS__, 'field_natives' ) ); add_action( 'woocommerce_admin_field_prodtabs_multilingual', array( __CLASS__, 'field_multilingual' ) ); add_action( 'woocommerce_admin_field_prodtabs_tools', array( __CLASS__, 'field_tools' ) ); add_action( 'woocommerce_update_options_products_' . self::SECTION, array( __CLASS__, 'save_natives' ) ); add_filter( 'sanitize_option_' . Settings::OPTION, array( Settings::class, 'sanitize' ) ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'assets' ) ); add_action( 'admin_init', array( __CLASS__, 'dismiss_welcome' ) ); add_filter( 'plugin_action_links_' . plugin_basename( PRODTABS_FILE ), array( __CLASS__, 'action_links' ) ); } /** Address of the settings section. */ public static function url(): string { return admin_url( 'admin.php?page=wc-settings&tab=products§ion=' . self::SECTION ); } /** * "Settings" link in the plugins list. * * @param string[] $links Links. */ public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'prodtabs' ) . '' ); return $links; } /** * Section entry under Products. * * @param string[] $sections Sections. */ public static function section( $sections ) { $sections[ self::SECTION ] = __( 'Tabs', 'prodtabs' ); return $sections; } /** Whether the current screen is the settings section. */ private static function is_screen(): bool { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- screen detection only. return isset( $_GET['page'], $_GET['tab'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'products' === $_GET['tab'] && self::SECTION === $_GET['section']; // phpcs:enable } /** Script of the WooCommerce tabs table. */ public static function assets(): void { if ( ! self::is_screen() ) { return; } wp_enqueue_style( 'prodtabs-admin', PRODTABS_URL . 'assets/css/admin.css', array(), Plugin::asset_version( 'assets/css/admin.css' ) ); wp_enqueue_script( 'prodtabs-admin-settings', PRODTABS_URL . 'assets/js/admin-settings.js', array( 'jquery', 'wp-a11y' ), Plugin::asset_version( 'assets/js/admin-settings.js' ), true ); wp_localize_script( 'prodtabs-admin-settings', 'prodtabsSettings', array( /* translators: 1: tab name, 2: position, 3: number of tabs. */ 'moved' => __( '%1$s moved to position %2$d of %3$d.', 'prodtabs' ), ) ); } /** * Fields of the section. * * @param array $settings Fields of the Products tab. * @param string $section Current section. */ public static function fields( $settings, $section ) { if ( self::SECTION !== $section ) { return $settings; } $values = Settings::all(); $yes = static function ( $flag ) { return $flag ? 'yes' : 'no'; }; $yikes = Import_Yikes::detect(); $fields = array( array( 'type' => 'prodtabs_welcome', 'id' => 'prodtabs_welcome', ), array( 'title' => __( 'Product tabs', 'prodtabs' ), 'type' => 'title', 'desc' => __( 'These settings apply to every product. A product can still hide a tab or change the order from the Tabs panel of its screen.', 'prodtabs' ) . "\n\n" . sprintf( /* translators: 1: URL of the products list, 2: URL of the shared tabs list, 3: name of the Tabs panel of a product, 4: name of the shared tabs menu. */ __( 'Tab content is edited in each product (%3$s panel) and, for tabs used on several products, in %4$s.', 'prodtabs' ), esc_url( admin_url( 'edit.php?post_type=product' ) ), esc_url( admin_url( 'edit.php?post_type=' . Library::POST_TYPE ) ), esc_html( Admin_Product::panel_label() ), esc_html__( 'Shared tabs', 'prodtabs' ) ) . "\n\n" . '' . esc_html__( 'ProdTabs overview', 'prodtabs' ) . '', 'id' => 'prodtabs_tabs', ), array( 'title' => __( 'WooCommerce tabs', 'prodtabs' ), 'type' => 'prodtabs_natives', 'id' => 'prodtabs_natives', 'is_option' => false, ), array( 'title' => __( 'Headings', 'prodtabs' ), 'desc' => __( 'Repeat the tab title as a heading at the top of each tab', 'prodtabs' ), 'id' => Settings::OPTION . '[heading]', 'type' => 'checkbox', 'value' => $yes( $values['heading'] ), 'default' => 'yes', ), array( 'title' => __( 'Small screens', 'prodtabs' ), 'desc' => __( 'Show the tabs as an accordion on phones', 'prodtabs' ), 'desc_tip' => __( 'Each tab becomes a section that opens on a tap, as on most shops viewed on a phone. A Product Details block already set as an accordion stays as it is.', 'prodtabs' ), 'id' => Settings::OPTION . '[accordion]', 'type' => 'checkbox', 'value' => $yes( $values['accordion'] ), 'default' => 'yes', ), array( 'title' => __( 'Accordion below', 'prodtabs' ), 'desc' => __( 'pixels of screen width', 'prodtabs' ), 'id' => Settings::OPTION . '[accordion_width]', 'type' => 'number', 'value' => (string) $values['accordion_width'], 'default' => '768', 'css' => 'width:6em', 'custom_attributes' => array( 'min' => 320, 'max' => 2000, 'step' => 1, ), ), array( 'title' => __( 'Content formatting', 'prodtabs' ), 'desc' => __( 'Format tab content with the full content filter of the theme and plugins', 'prodtabs' ), 'desc_tip' => __( 'Turn this on if a shortcode or an embed of another plugin does not show in your tabs. It is off by default because some plugins add sharing buttons or related products to every piece of content, which would repeat in each tab.', 'prodtabs' ), 'id' => Settings::OPTION . '[the_content]', 'type' => 'checkbox', 'value' => $yes( $values['the_content'] ), 'default' => 'no', ), array( 'type' => 'sectionend', 'id' => 'prodtabs_tabs', ), array( 'title' => __( 'Languages', 'prodtabs' ), 'type' => 'title', 'id' => 'prodtabs_languages', ), array( 'title' => __( 'Translation plugin', 'prodtabs' ), 'type' => 'prodtabs_multilingual', 'id' => 'prodtabs_multilingual', 'is_option' => false, ), // Only with a plugin that keeps a copy of the product per language: the others translate the page as a whole. in_array( Multilingual::plugin(), array( 'polylang', 'wpml' ), true ) ? array( 'title' => __( 'Tabs not translated yet', 'prodtabs' ), 'desc' => __( 'Hide them on the pages of the other languages', 'prodtabs' ), 'desc_tip' => __( 'By default, a tab shows its original text until it is translated. Hidden, it comes back as soon as its translation is saved.', 'prodtabs' ), 'id' => Settings::OPTION . '[hide_untranslated]', 'type' => 'checkbox', 'value' => $yes( $values['hide_untranslated'] ), 'default' => 'no', ) : null, array( 'type' => 'sectionend', 'id' => 'prodtabs_languages', ), array( 'title' => __( 'Tools', 'prodtabs' ), 'type' => 'title', 'id' => 'prodtabs_tools', ), array( 'title' => _x( 'Import', 'noun', 'prodtabs' ), 'type' => 'prodtabs_tools', 'id' => 'prodtabs_tools_import', 'is_option' => false, ), // Turned on by the import; shown as long as it is on or YIKES left data on the site. ( $values['yikes_compat'] || $yikes['products'] || $yikes['reusable'] ) ? array( 'title' => __( 'YIKES compatibility', 'prodtabs' ), 'desc' => __( 'Apply the filters and classes of Custom Product Tabs (YIKES) to the ProdTabs tabs', 'prodtabs' ), 'desc_tip' => __( 'Turned on by the import, so that what your theme or a snippet did with the YIKES tabs keeps working. Turn it off if the headings or the content of your tabs look wrong since the import.', 'prodtabs' ), 'id' => Settings::OPTION . '[yikes_compat]', 'type' => 'checkbox', 'value' => $yes( $values['yikes_compat'] ), 'default' => 'no', ) : null, array( 'title' => __( 'ProdTabs menu', 'prodtabs' ), 'desc' => __( 'Show ProdTabs in the main WordPress menu, with its overview, the shared tabs and these settings', 'prodtabs' ), 'desc_tip' => __( 'Unchecked, the shared tabs stay under Products and these settings under WooCommerce; the overview opens from the link at the top of this page.', 'prodtabs' ), 'id' => Settings::OPTION . '[menu]', 'type' => 'checkbox', 'value' => $yes( $values['menu'] ), 'default' => 'yes', ), array( 'title' => __( 'Uninstall', 'prodtabs' ), 'desc' => __( 'Delete the tabs of the products, the shared tabs and these settings when the plugin is deleted', 'prodtabs' ), 'id' => Settings::OPTION . '[delete_data]', 'type' => 'checkbox', 'value' => $yes( $values['delete_data'] ), 'default' => 'no', ), array( 'type' => 'sectionend', 'id' => 'prodtabs_tools', ), ); /** * Fields of the settings section. * * @param array $fields WooCommerce settings fields. */ return apply_filters( 'prodtabs_settings_fields', array_values( array_filter( $fields ) ) ); } /** Welcome box, until the user closes it. */ public static function field_welcome(): void { if ( get_user_meta( get_current_user_id(), self::WELCOME_DISMISSED, true ) ) { return; } $yikes = Import_Yikes::detect(); $dismiss = wp_nonce_url( add_query_arg( 'prodtabs_dismiss', 'welcome', self::url() ), 'prodtabs_dismiss_welcome' ); ?>

__( 'Description', 'prodtabs' ), 'additional_information' => __( 'Additional information', 'prodtabs' ), 'reviews' => __( 'Reviews', 'prodtabs' ), ); $count = count( $settings['native_order'] ); ?> $key ) : ?>

isset( $natives[ $key ]['title'] ) ? sanitize_text_field( (string) $natives[ $key ]['title'] ) : '', 'visible' => ! empty( $natives[ $key ]['visible'] ), ); } if ( isset( $_POST['prodtabs_natives_order'] ) && is_array( $_POST['prodtabs_natives_order'] ) ) { $settings['native_order'] = array_map( 'sanitize_key', wp_unslash( $_POST['prodtabs_natives_order'] ) ); } update_option( Settings::OPTION, $settings ); } /** State of the translation plugin. */ public static function field_multilingual( $field ): void { $plugin = Multilingual::plugin(); $lines = array(); switch ( $plugin ) { case 'polylang': $products = function_exists( 'pll_is_translated_post_type' ) && pll_is_translated_post_type( 'product' ); $lines[] = array( true, __( 'Polylang is active: shared tabs are translated like posts, in the Languages column of their list.', 'prodtabs' ) ); $lines[] = array( $products, $products ? __( 'Products are translatable: a product and its translations share one list of tabs, with the titles and texts of each language.', 'prodtabs' ) : __( 'Products are not translatable yet: turn them on in Languages > Settings > Custom post types.', 'prodtabs' ) ); /* translators: %s: name of the group of strings in Polylang, such as plugins/prodtabs. */ $lines[] = array( true, sprintf( __( 'Titles set above for the WooCommerce tabs are translated in Languages > Translations, group %s.', 'prodtabs' ), 'plugins/' . dirname( plugin_basename( PRODTABS_FILE ) ) ) ); break; case 'wpml': $wcml = defined( 'WCML_VERSION' ); $lines[] = array( true, __( 'WPML is active: shared tabs and the tabs of products are declared to WPML (file wpml-config.xml), and translated with the rest of the product.', 'prodtabs' ) ); $lines[] = array( $wcml, $wcml ? __( 'WooCommerce Multilingual is active.', 'prodtabs' ) : __( 'WooCommerce Multilingual is not active: install it to translate products with WPML.', 'prodtabs' ) ); $lines[] = array( true, __( 'Titles set above for the WooCommerce tabs are translated in WPML > String Translation.', 'prodtabs' ) ); break; case 'translatepress': $lines[] = array( true, __( 'TranslatePress is active: tab titles and content are translated on the page, like the rest of your content.', 'prodtabs' ) ); break; case 'weglot': $lines[] = array( true, __( 'Weglot is active: tab titles and content are translated on the page, like the rest of your content.', 'prodtabs' ) ); break; default: $lines[] = array( true, __( 'No translation plugin found. ProdTabs works with WPML, Polylang, TranslatePress and Weglot.', 'prodtabs' ) ); } ?>