options = $options; $this->db = $db; add_action( 'admin_init', [ $this, 'init' ] ); } /** * Register settings fields. * * @return void */ public function init() { $this->settings_options = [ [ 'option_group' => Site_Traffic_Manager::OPTION_NAME, 'option_name' => Site_Traffic_Manager::OPTION_NAME, 'args' => [ 'sanitize_callback' => [ $this, 'sanitize_settings' ] ], ], [ 'option_group' => Consent_Manager::OPTION_NAME, 'option_name' => Consent_Manager::OPTION_NAME, 'args' => [ 'sanitize_callback' => [ $this, 'sanitize_consent_settings' ] ], ], ]; $this->settings_sections = [ [ 'id' => 'adminoptim-site-traffic-manager-settings', 'title' => __( 'Tracking Settings', 'admin-optimizer' ), 'description' => __( 'Currently, we only track page views. We will add support for tracking other stats for your site in a future update.', 'admin-optimizer' ), 'menu_slug' => Site_Traffic_Manager::OPTION_NAME, 'option_name' => Site_Traffic_Manager::OPTION_NAME, 'fields' => [ [ 'type' => 'radio', 'title' => __( 'Pageview Update Mode', 'admin-optimizer' ), 'id' => 'site-traffic-manager-update-mode', 'name' => 'update_mode', 'default' => 'direct', 'choices' => [ 'direct' => __( 'Direct DB Update', 'admin-optimizer' ), 'cache' => __( 'Using Cache (recommended for high-traffic sites)', 'admin-optimizer' ), ], 'desc' => $this->get_update_mode_description(), ], [ 'type' => 'checkboxes', 'title' => __( 'Collect Page Views for the Following Post Types', 'admin-optimizer' ), 'id' => 'site-traffic-manager-post-types', 'name' => 'post_types', 'default' => [ 'post', 'page' ], 'choices' => $this->get_post_type_choices(), 'desc' => __( 'Select "All Post Types" to track every supported public post type, including ones added later.', 'admin-optimizer' ), ], [ 'type' => 'checkboxes', 'title' => __( 'Also Collect Page Views for Non-Post Pages', 'admin-optimizer' ), 'id' => 'site-traffic-manager-view-types', 'name' => 'view_types', 'default' => [], 'choices' => [ 'non_post' => __( 'Home Page, Archives, Search Results, and Other Non-Post Pages', 'admin-optimizer' ), ], 'desc' => __( 'Tracks these views by URL rather than by post, and shows them in the Statistics tab alongside posts.', 'admin-optimizer' ), ], ], ], [ 'id' => 'adminoptim-consent-manager-settings', 'title' => __( 'Consent Settings', 'admin-optimizer' ), 'description' => __( 'Site Traffic Manager records aggregate page-view counts without setting cookies or creating visitor profiles. It uses a short-lived, salted one-way hash of each request\'s IP address only to limit duplicate counts; the raw IP address is never stored. Site Traffic Manager is therefore not gated by this banner. Enable the banner when your site uses optional services that require consent under applicable privacy laws.', 'admin-optimizer' ), 'menu_slug' => Consent_Manager::OPTION_NAME, 'option_name' => Consent_Manager::OPTION_NAME, 'fields' => [ [ 'type' => 'checkbox', 'title' => __( 'Enable Consent Banner', 'admin-optimizer' ), 'id' => 'enable-consent-manager-banner', 'name' => 'enable_banner', ], [ 'type' => 'number', 'title' => __( 'Consent expiry in days', 'admin-optimizer' ), 'id' => 'consent-manager-expiry-days', 'name' => 'expiry_days', 'min' => '1', 'max' => '365', 'default' => '180', ], ], ], [ 'id' => 'adminoptim-consent-manager-banner', 'title' => __( 'Banner', 'admin-optimizer' ), 'description' => '', 'menu_slug' => Consent_Manager::OPTION_NAME, 'option_name' => Consent_Manager::OPTION_NAME, 'fields' => [ [ 'type' => 'text', 'title' => __( 'Banner Title', 'admin-optimizer' ), 'id' => 'consent-banner-title', 'name' => 'banner_title', 'default' => __( 'Your privacy choices', 'admin-optimizer' ), ], [ 'type' => 'textarea', 'title' => __( 'Banner Message', 'admin-optimizer' ), 'id' => 'consent-banner-message', 'name' => 'banner_message', 'default' => __( 'We use optional services only when you allow their category. You can change your choices at any time.', 'admin-optimizer' ), ], [ 'type' => 'text', 'title' => __( 'Accept All button label', 'admin-optimizer' ), 'id' => 'consent-accept-label', 'name' => 'accept_label', 'default' => __( 'Accept All', 'admin-optimizer' ), ], [ 'type' => 'text', 'title' => __( 'Reject Optional button label', 'admin-optimizer' ), 'id' => 'consent-reject-label', 'name' => 'reject_label', 'default' => __( 'Reject Optional', 'admin-optimizer' ), ], [ 'type' => 'text', 'title' => __( 'Manage Preferences button label', 'admin-optimizer' ), 'id' => 'consent-manage-label', 'name' => 'manage_label', 'default' => __( 'Manage Preferences', 'admin-optimizer' ), ], ], ], [ 'id' => 'adminoptim-consent-manager-preferences', 'title' => __( 'Preferences', 'admin-optimizer' ), 'description' => '', 'menu_slug' => Consent_Manager::OPTION_NAME, 'option_name' => Consent_Manager::OPTION_NAME, 'fields' => [ [ 'type' => 'text', 'title' => __( 'Preferences title', 'admin-optimizer' ), 'id' => 'consent-preferences-title', 'name' => 'preferences_title', 'default' => __( 'Consent preferences', 'admin-optimizer' ), ], [ 'type' => 'textarea', 'title' => __( 'Preferences explanatory text', 'admin-optimizer' ), 'id' => 'consent-preferences-text', 'name' => 'preferences_text', 'default' => __( 'Choose which optional categories you allow. Necessary services are always active.', 'admin-optimizer' ), ], [ 'type' => 'text', 'title' => __( 'Save Preferences button label', 'admin-optimizer' ), 'id' => 'consent-save-label', 'name' => 'save_label', 'default' => __( 'Save Preferences', 'admin-optimizer' ), ], ], ], [ 'id' => 'adminoptim-consent-manager-categories', 'title' => __( 'Categories', 'admin-optimizer' ), 'description' => '', 'menu_slug' => Consent_Manager::OPTION_NAME, 'option_name' => Consent_Manager::OPTION_NAME, 'fields' => [ [ 'type' => 'text', 'title' => __( 'Necessary description', 'admin-optimizer' ), 'id' => 'consent-necessary-description', 'name' => 'necessary_description', 'default' => __( 'Required for the site to function and cannot be switched off.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Functional enabled', 'admin-optimizer' ), 'id' => 'consent-functional-enabled', 'name' => 'functional_enabled', ], [ 'type' => 'text', 'title' => __( 'Functional description', 'admin-optimizer' ), 'id' => 'consent-functional-description', 'name' => 'functional_description', 'default' => __( 'Supports optional site features and preferences.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Analytics enabled', 'admin-optimizer' ), 'id' => 'consent-analytics-enabled', 'name' => 'analytics_enabled', ], [ 'type' => 'text', 'title' => __( 'Analytics description', 'admin-optimizer' ), 'id' => 'consent-analytics-description', 'name' => 'analytics_description', 'default' => __( 'Helps measure how visitors use the site.', 'admin-optimizer' ), ], [ 'type' => 'checkbox', 'title' => __( 'Marketing enabled', 'admin-optimizer' ), 'id' => 'consent-marketing-enabled', 'name' => 'marketing_enabled', ], [ 'type' => 'text', 'title' => __( 'Marketing description', 'admin-optimizer' ), 'id' => 'consent-marketing-description', 'name' => 'marketing_description', 'default' => __( 'Supports advertising and campaign measurement.', 'admin-optimizer' ), ], ], ], ]; $this->setup(); } /** * Get post type choices, including an "All Post Types" option. * * @return array */ private function get_post_type_choices(): array { $custom_post_types = get_post_types( [ 'show_ui' => true, '_builtin' => false, 'public' => true, ], 'objects' ); $choices = [ 'all' => __( 'All Public Post Types', 'admin-optimizer' ), 'post' => __( 'Post', 'admin-optimizer' ), 'page' => __( 'Page', 'admin-optimizer' ), ]; foreach ( $custom_post_types as $custom_post_type ) { $choices[ $custom_post_type->name ] = $custom_post_type->label; } return $choices; } /** * Get the description shown under the update mode field. * * @return string */ private function get_update_mode_description(): string { if ( wp_using_ext_object_cache() && function_exists( 'wp_cache_incr' ) ) { return __( 'Cache mode defers database writes through your persistent object cache (Redis/Memcached) and flushes them every five minutes. Use it on high-traffic sites.', 'admin-optimizer' ); } return __( 'Cache mode requires a persistent object cache such as Redis or Memcached and is unavailable on this site.', 'admin-optimizer' ); } /** * Sanitize settings before storing in the database. * * @param array|null $options Values to be sanitized. * * @return array */ public function sanitize_settings( $options ) { $sanitized = []; if ( ! is_array( $options ) ) { return $sanitized; } $sanitized['update_mode'] = ( isset( $options['update_mode'] ) && 'cache' === $options['update_mode'] ) ? 'cache' : 'direct'; $valid_post_types = array_keys( $this->get_post_type_choices() ); if ( isset( $options['post_types'] ) && is_array( $options['post_types'] ) ) { if ( in_array( 'all', $options['post_types'], true ) ) { $sanitized['post_types'] = [ 'all' ]; } else { $sanitized['post_types'] = array_values( array_intersect( $options['post_types'], $valid_post_types ) ); } } else { $sanitized['post_types'] = [ 'post', 'page' ]; } $valid_view_types = [ 'non_post' ]; $sanitized['view_types'] = ( isset( $options['view_types'] ) && is_array( $options['view_types'] ) ) ? array_values( array_intersect( $options['view_types'], $valid_view_types ) ) : []; return $sanitized; } /** * Sanitize consent settings before storing them. * * @param array|null $options Values to sanitize. * @return array */ public function sanitize_consent_settings( $options ) { return Consent_Manager::normalize_settings( $options ); } /** * Get available post types for statistics filtering. * * @return array */ protected function get_filterable_post_types() { $configured_types = isset( $this->options['post_types'] ) && is_array( $this->options['post_types'] ) ? $this->options['post_types'] : []; $all_public_types = get_post_types( [ 'public' => true ], 'objects' ); $filterable = []; if ( in_array( 'all', $configured_types, true ) ) { $filterable = $all_public_types; } else { foreach ( $configured_types as $post_type_name ) { $post_type_name = sanitize_key( $post_type_name ); if ( isset( $all_public_types[ $post_type_name ] ) ) { $filterable[ $post_type_name ] = $all_public_types[ $post_type_name ]; } } } $options = [ 'all' => __( 'All (Including Home Page & Archives)', 'admin-optimizer' ), 'all_post_types' => __( 'All Post Types', 'admin-optimizer' ), ]; foreach ( $filterable as $post_type ) { $options[ $post_type->name ] = $post_type->label; } return $options; } /** * Check whether the statistics filter request has a valid nonce. * * @return bool */ protected function has_valid_filter_nonce() { if ( ! isset( $_GET[ self::FILTER_NONCE_NAME ] ) ) { return false; } $nonce = sanitize_text_field( wp_unslash( $_GET[ self::FILTER_NONCE_NAME ] ) ); return 1 === wp_verify_nonce( $nonce, self::FILTER_NONCE_ACTION ); } /** * Get available time period filters. * * @return array */ protected function get_time_period_options() { return [ 'all_time' => __( 'All Time', 'admin-optimizer' ), 'last_3_days' => __( 'Last 3 Days', 'admin-optimizer' ), 'last_7_days' => __( 'Last 7 Days', 'admin-optimizer' ), 'last_30_days' => __( 'Last 30 Days', 'admin-optimizer' ), 'last_90_days' => __( 'Last 3 Months', 'admin-optimizer' ), 'custom' => __( 'Custom Range', 'admin-optimizer' ), ]; } /** * Render the module's admin page. * * @return void */ public function render_settings_page() { $tab = ! empty( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'statistics'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! in_array( $tab, [ 'statistics', 'settings', 'consent' ], true ) ) { $tab = 'statistics'; } ?>