fields = new AHA_Prev_Next_Settings_Fields( $this->option_name, array( $this, 'get_settings' ) ); add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_init', array( $this, 'maybe_save_uninstall_preference' ) ); add_filter( 'aha_prev_next_settings_tabs', array( $this, 'append_uninstall_tab' ), 100 ); } /** * Append Uninstall tab after other add-ons register their tabs. * * @param array $tabs Tab slug => label. * @return array */ public function append_uninstall_tab( $tabs ) { if ( ! is_array( $tabs ) ) { $tabs = array(); } $tabs['uninstall'] = __( 'Uninstall', 'aha-prev-next' ); return $tabs; } /** * Save “remove data on uninstall” preference (Uninstall tab). * * @return void */ public function maybe_save_uninstall_preference() { if ( ! isset( $_POST['aha_pn_uninstall_prefs'] ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Permission denied.', 'aha-prev-next' ) ); } check_admin_referer( 'aha_pn_uninstall_prefs', 'aha_pn_uninstall_prefs_nonce' ); $erase = isset( $_POST['aha_pn_remove_data_on_uninstall'] ) ? '1' : '0'; update_option( AHA_PREV_NEXT_UNINSTALL_DELETE_DATA_OPTION, $erase, false ); wp_safe_redirect( add_query_arg( array( 'page' => 'aha-prev-next', 'tab' => 'uninstall', 'aha_pn_uninstall_saved' => '1', ), admin_url( 'admin.php' ) ) ); exit; } /** * Register admin menu. * * @return void */ public function register_menu() { add_menu_page( __( 'Aha Prev Next', 'aha-prev-next' ), __( 'Aha Prev Next', 'aha-prev-next' ), 'manage_options', 'aha-prev-next', array( $this, 'render_settings_page' ), 'dashicons-image-flip-horizontal', 76 ); } /** * Register settings, sections and fields. * * @return void */ public function register_settings() { register_setting( 'aha_prev_next_group', $this->option_name, array( 'sanitize_callback' => array( $this, 'sanitize_settings' ), ) ); // General tab. add_settings_section( 'aha_prev_next_general', __( 'General', 'aha-prev-next' ), array( $this->fields, 'render_section_general' ), 'aha-prev-next-general' ); add_settings_field( 'aha_prev_next_enable', __( 'Enable', 'aha-prev-next' ), array( $this->fields, 'render_field_enable' ), 'aha-prev-next-general', 'aha_prev_next_general' ); // Display tab. add_settings_section( 'aha_prev_next_display', __( 'Display', 'aha-prev-next' ), array( $this->fields, 'render_section_display' ), 'aha-prev-next-display' ); add_settings_field( 'aha_prev_next_post_types', __( 'Enabled post types', 'aha-prev-next' ), array( $this->fields, 'render_field_post_types' ), 'aha-prev-next-display', 'aha_prev_next_display' ); add_settings_field( 'aha_prev_next_position', __( 'Position', 'aha-prev-next' ), array( $this->fields, 'render_field_position' ), 'aha-prev-next-display', 'aha_prev_next_display' ); add_settings_field( 'aha_prev_next_woo_position', __( 'WooCommerce product position', 'aha-prev-next' ), array( $this->fields, 'render_field_woo_position' ), 'aha-prev-next-display', 'aha_prev_next_display' ); // Style tab. add_settings_section( 'aha_prev_next_style', __( 'Style', 'aha-prev-next' ), array( $this->fields, 'render_section_style' ), 'aha-prev-next-style' ); add_settings_field( 'aha_prev_next_show_thumbnail', __( 'Thumbnail', 'aha-prev-next' ), array( $this->fields, 'render_field_show_thumbnail' ), 'aha-prev-next-style', 'aha_prev_next_style' ); add_settings_field( 'aha_prev_next_title_length', __( 'Title length', 'aha-prev-next' ), array( $this->fields, 'render_field_title_length' ), 'aha-prev-next-style', 'aha_prev_next_style' ); add_settings_field( 'aha_prev_next_colors', __( 'Colors', 'aha-prev-next' ), array( $this->fields, 'render_field_colors' ), 'aha-prev-next-style', 'aha_prev_next_style' ); } /** * Sanitize settings input. * * @param mixed $input Raw input. * @return array */ public function sanitize_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $existing = get_option( $this->option_name, array() ); $existing = is_array( $existing ) ? $existing : array(); // Start from existing values so saving one tab doesn't wipe the others. $sanitized = $existing; // Checkboxes: only update when the field is present in POST (tab rendered). if ( array_key_exists( 'enable', $input ) ) { $sanitized['enable'] = ! empty( $input['enable'] ) ? 1 : 0; } if ( array_key_exists( 'show_thumbnail', $input ) ) { $sanitized['show_thumbnail'] = ! empty( $input['show_thumbnail'] ) ? 1 : 0; } // Enabled post types (only allow those returned by allowed list). if ( array_key_exists( 'enabled_post_types', $input ) ) { $allowed = function_exists( 'aha_prev_next_get_allowed_post_types' ) ? aha_prev_next_get_allowed_post_types() : array( 'post', 'page' ); $allowed = array_fill_keys( (array) $allowed, true ); $sanitized['enabled_post_types'] = array(); if ( is_array( $input['enabled_post_types'] ) ) { foreach ( $input['enabled_post_types'] as $pt ) { $pt = sanitize_key( $pt ); if ( isset( $allowed[ $pt ] ) ) { $sanitized['enabled_post_types'][] = $pt; } } } // If user unchecked everything, keep it empty? For now fallback to defaults. if ( empty( $sanitized['enabled_post_types'] ) ) { $sanitized['enabled_post_types'] = array( 'post', 'page' ); } } // Layout settings. if ( array_key_exists( 'position', $input ) ) { $pos = sanitize_key( $input['position'] ); $sanitized['position'] = in_array( $pos, array( 'above', 'below' ), true ) ? $pos : 'below'; } if ( array_key_exists( 'woo_position', $input ) ) { $woo_pos = sanitize_key( $input['woo_position'] ); $sanitized['woo_position'] = in_array( $woo_pos, array( 'above', 'below', 'woocommerce_after_single_product', 'woocommerce_after_single_product_summary', 'woocommerce_before_single_product', 'disable', ), true ) ? $woo_pos : 'woocommerce_after_single_product'; } // Title length. if ( array_key_exists( 'title_length', $input ) ) { $len = absint( $input['title_length'] ); if ( 0 === $len ) { $sanitized['title_length'] = 0; } else { $sanitized['title_length'] = max( 10, min( 120, $len ) ); } } // Colors. if ( array_key_exists( 'card_bg', $input ) ) { $sanitized['card_bg'] = sanitize_hex_color( $input['card_bg'] ) ?: '#f3f3f3'; } if ( array_key_exists( 'title_color', $input ) ) { $sanitized['title_color'] = sanitize_hex_color( $input['title_color'] ) ?: '#000000'; } if ( array_key_exists( 'arrow_color', $input ) ) { $sanitized['arrow_color'] = sanitize_hex_color( $input['arrow_color'] ) ?: '#000000'; } if ( array_key_exists( 'subtitle_color', $input ) ) { $sanitized['subtitle_color'] = sanitize_hex_color( $input['subtitle_color'] ) ?: '#6b6b6b'; } return $sanitized; } /** * Get merged settings with defaults. * * @return array */ public function get_settings() { $defaults = array( 'enable' => 1, 'enabled_post_types' => array( 'post', 'page' ), 'position' => 'below', 'woo_position' => 'woocommerce_after_single_product', 'show_thumbnail' => 1, 'title_length' => 40, 'card_bg' => '#f3f3f3', 'title_color' => '#000000', 'arrow_color' => '#000000', 'subtitle_color' => '#6b6b6b', ); $settings = get_option( $this->option_name, array() ); $settings = is_array( $settings ) ? $settings : array(); return wp_parse_args( $settings, $defaults ); } /** * Render settings page. * * @return void */ public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only tab state. $active_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'general'; $tabs = array( 'general' => __( 'General', 'aha-prev-next' ), 'display' => __( 'Display', 'aha-prev-next' ), 'style' => __( 'Style', 'aha-prev-next' ), 'shortcode' => __( 'Shortcode', 'aha-prev-next' ), ); /** * Filter settings tabs (allows add-ons to add extra tabs). * * @param array $tabs Associative array of tab_slug => label. */ $tabs = apply_filters( 'aha_prev_next_settings_tabs', $tabs ); if ( ! array_key_exists( $active_tab, $tabs ) ) { $active_tab = 'general'; } ?>