'yes', 'default_free' => 1, 'default_max_order' => 5, 'default_max_customer' => 10, 'button_label' => 'Order a sample', 'allow_guests' => 'yes', ); } /** * Get merged global settings. * * @return array */ function tryv_get_settings() { $saved = get_option( 'tryv_sample_settings', array() ); if ( ! is_array( $saved ) ) { $saved = array(); } return wp_parse_args( $saved, tryv_get_default_settings() ); } /** * Sanitize the settings array. Every field is validated here; nothing * from the request is stored raw. * * @param mixed $input Raw option value. * @return array */ function tryv_sanitize_settings( $input ) { $defaults = tryv_get_default_settings(); $clean = array(); if ( ! is_array( $input ) ) { return $defaults; } $clean['enabled'] = ( isset( $input['enabled'] ) && 'yes' === $input['enabled'] ) ? 'yes' : 'no'; $clean['allow_guests'] = ( isset( $input['allow_guests'] ) && 'yes' === $input['allow_guests'] ) ? 'yes' : 'no'; $clean['default_free'] = isset( $input['default_free'] ) ? max( 0, absint( $input['default_free'] ) ) : $defaults['default_free']; $clean['default_max_order'] = isset( $input['default_max_order'] ) ? max( 0, absint( $input['default_max_order'] ) ) : $defaults['default_max_order']; $clean['default_max_customer'] = isset( $input['default_max_customer'] ) ? max( 0, absint( $input['default_max_customer'] ) ) : $defaults['default_max_customer']; // 0 means "no limit" for the two maximums, and "no free samples" for default_free. $clean['button_label'] = isset( $input['button_label'] ) ? sanitize_text_field( wp_unslash( $input['button_label'] ) ) : $defaults['button_label']; if ( '' === $clean['button_label'] ) { $clean['button_label'] = $defaults['button_label']; } return $clean; } /** * Render one checkbox field. * * @param array $args Field arguments: key, label. */ function tryv_render_checkbox_field( $args ) { $settings = tryv_get_settings(); $key = $args['key']; $checked = ( isset( $settings[ $key ] ) && 'yes' === $settings[ $key ] ) ? 'checked' : ''; printf( ' ', esc_attr( $key ), esc_attr( $checked ), esc_html( $args['label'] ) ); } /** * Render one number field. * * @param array $args Field arguments: key, label, description. */ function tryv_render_number_field( $args ) { $settings = tryv_get_settings(); $key = $args['key']; printf( '

%3$s

', esc_attr( $key ), absint( $settings[ $key ] ), esc_html( $args['description'] ) ); } /** * Render the button label field. */ function tryv_render_button_label_field() { $settings = tryv_get_settings(); printf( '', esc_attr( $settings['button_label'] ) ); } /** * Register hooks only (D1 fix, v1.0.2). * * Previously this function called add_submenu_page(), * add_settings_section() and add_settings_field() directly, and it * was called on plugins_loaded. Those functions are defined only in * wp-admin includes, which are not loaded at plugins_loaded on a * stock install, so the plugin fatalled on every page load * (Call to undefined function add_settings_section()). * * Now: tryv_settings_init() only registers hooks. Settings * registration runs on admin_init, when wp-admin includes are * loaded; menu registration runs on admin_menu. Neither runs on * the front end, and nothing here is called at plugins_loaded. * The test-site mu-plugin shim that required those wp-admin * includes early was a test harness workaround only and is NOT * copied into this plugin. */ function tryv_settings_init() { add_action( 'admin_init', 'tryv_settings_register' ); add_action( 'admin_menu', 'tryv_settings_menu' ); } /** * Register the settings menu page. The page capability is * manage_woocommerce, so shop managers and administrators only. * Runs on admin_menu only — never on plugins_loaded. */ function tryv_settings_menu() { add_submenu_page( 'woocommerce', __( 'Tryvertising Settings', 'tryvertising' ), __( 'Tryvertising', 'tryvertising' ), 'manage_woocommerce', 'tryv-sample-settings', 'tryv_render_settings_page' ); } /** * Register the settings, section and fields. * Runs on admin_init only — never on plugins_loaded. */ function tryv_settings_register() { register_setting( 'tryv_sample_settings_group', 'tryv_sample_settings', array( 'sanitize_callback' => 'tryv_sanitize_settings' ) ); add_settings_section( 'tryv_main', __( 'Sample defaults', 'tryvertising' ), '__return_null', 'tryv-sample-settings' ); add_settings_field( 'tryv_enabled', __( 'Enable samples', 'tryvertising' ), 'tryv_render_checkbox_field', 'tryv-sample-settings', 'tryv_main', array( 'key' => 'enabled', 'label' => __( 'Show the sample button on eligible products.', 'tryvertising' ) ) ); add_settings_field( 'tryv_button_label', __( 'Sample button label', 'tryvertising' ), 'tryv_render_button_label_field', 'tryv-sample-settings', 'tryv_main' ); add_settings_field( 'tryv_default_free', __( 'Free samples per customer, per product', 'tryvertising' ), 'tryv_render_number_field', 'tryv-sample-settings', 'tryv_main', array( 'key' => 'default_free', 'description' => __( 'The first this many samples of a product are free for a customer; further samples are charged at the sample price. 0 means no free samples. Counted per variation when a variation is sampled; a pack counts against every component, and a pack is only free while every component still has free allowance.', 'tryvertising' ) ) ); add_settings_field( 'tryv_default_max_order', __( 'Maximum samples per order, per product', 'tryvertising' ), 'tryv_render_number_field', 'tryv-sample-settings', 'tryv_main', array( 'key' => 'default_max_order', 'description' => __( '0 means no per-order limit. Counted per variation when a variation is sampled; a pack counts against every component.', 'tryvertising' ) ) ); add_settings_field( 'tryv_default_max_customer', __( 'Maximum samples per customer, per product', 'tryvertising' ), 'tryv_render_number_field', 'tryv-sample-settings', 'tryv_main', array( 'key' => 'default_max_customer', 'description' => __( 'Counts pending, on-hold, processing and completed sample orders. 0 means no lifetime limit. A customer is matched by billing email when known, otherwise by user account; when both are known, both order sets count.', 'tryvertising' ) ) ); add_settings_field( 'tryv_allow_guests', __( 'Allow guest samples', 'tryvertising' ), 'tryv_render_checkbox_field', 'tryv-sample-settings', 'tryv_main', array( 'key' => 'allow_guests', 'label' => __( 'Guests may order samples. Their per-customer limits are enforced at checkout, once a billing email is known.', 'tryvertising' ) ) ); } /** * Render the settings page. Capability is enforced by the menu * registration; re-check here so a direct URL cannot bypass it. */ function tryv_render_settings_page() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to view this page.', 'tryvertising' ) ); } ?>