plugin_name = $plugin_name; $this->version = $version; $this->options = $options; add_action('wp_ajax_adasprso_search_categories', array($this, 'ajax_search_categories')); add_action('admin_head', array($this, 'suppress_foreign_notices')); } /** * Remove other plugins' and themes' admin notices on our settings page * so they don't clutter the interface. Our own settings errors are output * via settings_errors() inside the page template, so they are unaffected. */ public function suppress_foreign_notices() { $screen = get_current_screen(); if ( ! $screen || 'settings_page_add-as-preferred-source' !== $screen->id ) { return; } remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); } /** * Register the stylesheets for the admin area. * * @since 1.0.0 */ public function enqueue_styles($hook) { // Only load on plugin settings page if ('settings_page_add-as-preferred-source' !== $hook) { return; } wp_enqueue_style('wp-color-picker'); wp_enqueue_style($this->plugin_name . '-admin', ADASPRSO_PLUGIN_URL . 'admin/css/add-as-preferred-source-admin.css', array(), $this->version, 'all'); } /** * Register the JavaScript for the admin area. * * @since 1.0.0 */ public function enqueue_scripts($hook) { // Only load on plugin settings page if ('settings_page_add-as-preferred-source' !== $hook) { return; } wp_enqueue_script('wp-color-picker'); wp_enqueue_script($this->plugin_name . '-admin', ADASPRSO_PLUGIN_URL . 'admin/js/add-as-preferred-source-admin.js', array('jquery', 'wp-color-picker'), $this->version, false); wp_localize_script($this->plugin_name . '-admin', 'adasprso_admin', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('adasprso_categories_nonce'), )); } /** * Add settings page link to plugins page * * @since 1.0.0 */ public function add_action_links($links) { $settings_link = '' . __('Settings', 'add-as-preferred-source') . ''; array_unshift($links, $settings_link); return $links; } /** * Show admin notice with settings link * * @since 1.0.0 */ public function admin_notice() { // Show notice only once $notice_dismissed = get_option('adasprso_notice_dismissed'); // Check if notice is already dismissed or if not on plugins page $current_screen = get_current_screen(); if ( $notice_dismissed || ! $current_screen || $current_screen->id !== 'plugins' ) { return; } if ( 'plugins' === $current_screen->id ) { // Auto-dismiss the notice after showing it once update_option('adasprso_notice_dismissed', true); ?>

plugin_name, array($this, 'display_settings_page') ); } /** * Display the settings page content * * @since 1.0.0 */ public function display_settings_page() { include_once ADASPRSO_PLUGIN_PATH . 'admin/partials/add-as-preferred-source-admin-display.php'; } /** * Register plugin settings * * @since 1.0.0 */ public function register_settings() { register_setting( 'adasprso_settings', 'adasprso_options', array($this, 'validate_settings') ); // General Settings Section add_settings_section( 'adasprso_general_section', __('General Settings', 'add-as-preferred-source'), array($this, 'general_section_callback'), 'adasprso_settings' ); // Banner Enabled add_settings_field( 'adasprso_enabled', __('Enable Banner', 'add-as-preferred-source'), array($this, 'enabled_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Website Name add_settings_field( 'adasprso_website_name', __('Website Name', 'add-as-preferred-source'), array($this, 'website_name_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Banner Text add_settings_field( 'adasprso_banner_text', __('Banner Text', 'add-as-preferred-source'), array($this, 'banner_text_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Banner Position add_settings_field( 'adasprso_banner_position', __('Banner Position', 'add-as-preferred-source'), array($this, 'banner_position_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Banner Trigger add_settings_field( 'adasprso_banner_trigger', __('Display Trigger', 'add-as-preferred-source'), array($this, 'banner_trigger_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Scroll Percentage add_settings_field( 'adasprso_banner_scroll_percent', __('Scroll Percentage', 'add-as-preferred-source'), array($this, 'banner_scroll_percent_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Delay Seconds add_settings_field( 'adasprso_banner_delay_seconds', __('Delay (seconds)', 'add-as-preferred-source'), array($this, 'banner_delay_seconds_callback'), 'adasprso_settings', 'adasprso_general_section' ); // Color Settings Section add_settings_section( 'adasprso_color_section', __('Color Settings', 'add-as-preferred-source'), array($this, 'color_section_callback'), 'adasprso_settings' ); // Banner Background Color add_settings_field( 'adasprso_banner_bg_color', __('Banner Background Color', 'add-as-preferred-source'), array($this, 'banner_bg_color_callback'), 'adasprso_settings', 'adasprso_color_section' ); // Text Color add_settings_field( 'adasprso_text_color', __('Text Color', 'add-as-preferred-source'), array($this, 'text_color_callback'), 'adasprso_settings', 'adasprso_color_section' ); // Device Settings Section add_settings_section( 'adasprso_device_section', __('Device Settings', 'add-as-preferred-source'), array($this, 'device_section_callback'), 'adasprso_settings' ); // Show on Mobile add_settings_field( 'adasprso_show_on_mobile', __('Show on Mobile', 'add-as-preferred-source'), array($this, 'show_on_mobile_callback'), 'adasprso_settings', 'adasprso_device_section' ); // Show on Desktop add_settings_field( 'adasprso_show_on_desktop', __('Show on Desktop', 'add-as-preferred-source'), array($this, 'show_on_desktop_callback'), 'adasprso_settings', 'adasprso_device_section' ); // Display Rules Section add_settings_section( 'adasprso_display_rules_section', __('Display Rules', 'add-as-preferred-source'), array($this, 'display_rules_section_callback'), 'adasprso_settings' ); // Display On add_settings_field( 'adasprso_display_on', __('Show Banner On', 'add-as-preferred-source'), array($this, 'display_on_callback'), 'adasprso_settings', 'adasprso_display_rules_section' ); // Exclude Post / Page IDs add_settings_field( 'adasprso_exclude_post_ids', __('Exclude Post / Page IDs', 'add-as-preferred-source'), array($this, 'exclude_post_ids_callback'), 'adasprso_settings', 'adasprso_display_rules_section' ); // Exclude Categories add_settings_field( 'adasprso_exclude_categories', __('Exclude Categories', 'add-as-preferred-source'), array($this, 'exclude_categories_callback'), 'adasprso_settings', 'adasprso_display_rules_section' ); // Inline Button Settings Section add_settings_section( 'adasprso_inline_section', __('Inline Button Settings', 'add-as-preferred-source'), array($this, 'inline_section_callback'), 'adasprso_settings_inline' ); // Inline Enabled add_settings_field( 'adasprso_inline_enabled', __('Enable Inline Button', 'add-as-preferred-source'), array($this, 'inline_enabled_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Position add_settings_field( 'adasprso_inline_position', __('Button Position', 'add-as-preferred-source'), array($this, 'inline_position_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Post Types add_settings_field( 'adasprso_inline_post_types', __('Post Types', 'add-as-preferred-source'), array($this, 'inline_post_types_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Background Color add_settings_field( 'adasprso_inline_bg_color', __('Background Color', 'add-as-preferred-source'), array($this, 'inline_bg_color_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Text Color add_settings_field( 'adasprso_inline_text_color', __('Text Color', 'add-as-preferred-source'), array($this, 'inline_text_color_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Text (Title) add_settings_field( 'adasprso_inline_text', __('Inline Title', 'add-as-preferred-source'), array($this, 'inline_text_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); // Inline Subtitle add_settings_field( 'adasprso_inline_subtitle', __('Inline Subtitle', 'add-as-preferred-source'), array($this, 'inline_subtitle_callback'), 'adasprso_settings_inline', 'adasprso_inline_section' ); } /** * Validate settings * * @since 1.0.0 * @param array $input The input options. * @return array The sanitized options. */ public function validate_settings($input) { $sanitized_input = array(); // No need to manually verify nonce - WordPress settings API handles this automatically // The nonce is automatically generated and verified by settings_fields() // Boolean field $sanitized_input['enabled'] = isset($input['enabled']) ? 1 : 0; // Text fields $sanitized_input['website_name'] = sanitize_text_field( isset( $input['website_name'] ) ? $input['website_name'] : '' ); $sanitized_input['banner_text'] = sanitize_text_field( isset( $input['banner_text'] ) ? $input['banner_text'] : '' ); // Banner position $sanitized_input['banner_position'] = ( isset( $input['banner_position'] ) && 'top' === $input['banner_position'] ) ? 'top' : 'bottom'; // Banner trigger $valid_triggers = array('immediate', 'scroll', 'time'); $sanitized_input['banner_trigger'] = (isset($input['banner_trigger']) && in_array($input['banner_trigger'], $valid_triggers, true)) ? $input['banner_trigger'] : 'immediate'; // Scroll percentage (10–90) $sanitized_input['banner_scroll_percent'] = isset($input['banner_scroll_percent']) ? max(10, min(90, (int) $input['banner_scroll_percent'])) : 50; // Delay seconds (1–60) $sanitized_input['banner_delay_seconds'] = isset($input['banner_delay_seconds']) ? max(1, min(60, (int) $input['banner_delay_seconds'])) : 5; // Color fields $sanitized_input['banner_bg_color'] = sanitize_hex_color( isset( $input['banner_bg_color'] ) ? $input['banner_bg_color'] : '' ); $sanitized_input['text_color'] = sanitize_hex_color( isset( $input['text_color'] ) ? $input['text_color'] : '' ); // Device settings $sanitized_input['show_on_mobile'] = isset($input['show_on_mobile']) ? 1 : 0; $sanitized_input['show_on_desktop'] = isset($input['show_on_desktop']) ? 1 : 0; // Display rules $valid_display_on = array('everywhere', 'posts_only', 'pages_only', 'front_page_only'); $sanitized_input['display_on'] = (isset($input['display_on']) && in_array($input['display_on'], $valid_display_on, true)) ? $input['display_on'] : 'everywhere'; // Exclude post/page IDs — keep only integers, re-join as comma string $raw_ids = isset($input['exclude_post_ids']) ? $input['exclude_post_ids'] : ''; $ids = array_filter(array_map('intval', explode(',', $raw_ids))); $sanitized_input['exclude_post_ids'] = implode(',', $ids); // Exclude categories — array of term IDs $sanitized_input['exclude_categories'] = array(); if (isset($input['exclude_categories']) && is_array($input['exclude_categories'])) { foreach ($input['exclude_categories'] as $cat_id) { $sanitized_input['exclude_categories'][] = absint($cat_id); } } // Inline settings $sanitized_input['inline_enabled'] = isset($input['inline_enabled']) ? 1 : 0; $valid_positions = array('above', 'below', 'both'); $sanitized_input['inline_position'] = ( isset( $input['inline_position'] ) && in_array( $input['inline_position'], $valid_positions, true ) ) ? $input['inline_position'] : 'both'; // Sanitize Post Types $sanitized_input['inline_post_types'] = array(); if (isset($input['inline_post_types']) && is_array($input['inline_post_types'])) { foreach ($input['inline_post_types'] as $post_type) { if (post_type_exists($post_type)) { $sanitized_input['inline_post_types'][] = sanitize_text_field($post_type); } } } // Sanitize Colors $sanitized_input['inline_bg_color'] = sanitize_hex_color( isset( $input['inline_bg_color'] ) ? $input['inline_bg_color'] : '' ); $sanitized_input['inline_text_color'] = sanitize_hex_color( isset( $input['inline_text_color'] ) ? $input['inline_text_color'] : '' ); $sanitized_input['inline_text'] = sanitize_text_field( isset( $input['inline_text'] ) ? $input['inline_text'] : '' ); $sanitized_input['inline_subtitle'] = sanitize_text_field( isset( $input['inline_subtitle'] ) ? $input['inline_subtitle'] : '' ); // Google button image (keep the local image path) $sanitized_input['google_button_image'] = ADASPRSO_PLUGIN_URL . 'public/images/google-preferred-source-button.png'; // Clear site caches so the updated settings take effect immediately. $this->clear_site_caches(); // Add success message add_settings_error('adasprso_options', 'settings_updated', __('Settings saved successfully!', 'add-as-preferred-source'), 'updated'); return $sanitized_input; } /** * Clear caches from popular caching plugins and the WordPress object cache. * * Runs after settings are saved so visitors see the updated banner/button * without needing a manual cache purge. */ private function clear_site_caches() { // WordPress object cache wp_cache_flush(); // WP Rocket if ( function_exists( 'rocket_clean_domain' ) ) { rocket_clean_domain(); } // W3 Total Cache if ( function_exists( 'w3tc_pgcache_flush' ) ) { w3tc_pgcache_flush(); } // WP Super Cache if ( function_exists( 'wp_cache_clear_cache' ) ) { wp_cache_clear_cache(); } // LiteSpeed Cache if ( class_exists( 'LiteSpeed_Cache_API' ) ) { do_action( 'litespeed_purge_all' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound } // Autoptimize if ( class_exists( 'autoptimizeCache' ) ) { autoptimizeCache::clearall(); } // WP Fastest Cache if ( isset( $GLOBALS['wp_fastest_cache'] ) && method_exists( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) { $GLOBALS['wp_fastest_cache']->deleteCache( true ); } // Hummingbird if ( class_exists( '\Hummingbird\Core\Filesystem' ) ) { do_action( 'wphb_clear_page_cache' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound } // Cache Enabler if ( class_exists( 'Cache_Enabler' ) ) { do_action( 'cache_enabler_clear_complete_cache' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound } // SG Optimizer (SiteGround) if ( function_exists( 'sg_cachepress_purge_cache' ) ) { sg_cachepress_purge_cache(); } // Comet Cache if ( class_exists( 'comet_cache' ) ) { comet_cache::clearCache(); } // Swift Performance if ( class_exists( 'Swift_Performance_Cache' ) ) { Swift_Performance_Cache::clear_all_cache(); } // Breeze (Cloudways) if ( class_exists( 'Breeze_PurgeCache' ) ) { do_action( 'breeze_clear_all_cache' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound } } /** * General section callback * * @since 1.0.0 */ public function general_section_callback() { echo '

' . esc_html__('Configure the general settings for your preferred source banner.', 'add-as-preferred-source') . '

'; } /** * Color section callback * * @since 1.0.0 */ public function color_section_callback() { echo '

' . esc_html__('Customize the colors for your preferred source banner.', 'add-as-preferred-source') . '

'; } /** * Device section callback * * @since 1.0.0 */ public function device_section_callback() { echo '

' . esc_html__('Choose on which devices the banner should be displayed.', 'add-as-preferred-source') . '

'; } /** * Enable callback * * @since 1.0.0 */ public function enabled_callback() { $enabled = isset($this->options['enabled']) ? $this->options['enabled'] : 0; echo ''; echo ''; } /** * Website name callback * * @since 1.0.0 */ public function website_name_callback() { $website_name = isset($this->options['website_name']) ? $this->options['website_name'] : get_bloginfo('name'); echo ''; echo '

' . esc_html__('Enter your website name for the banner text.', 'add-as-preferred-source') . '

'; } /** * Banner text callback * * @since 1.0.0 */ public function banner_text_callback() { $banner_text = isset($this->options['banner_text']) ? $this->options['banner_text'] : 'Add as a preferred source on Google'; echo ''; echo '

' . esc_html__('Enter the text to display in the banner. Use {website_name} to include the website name.', 'add-as-preferred-source') . '

'; } /** * Banner position callback * * @since 1.0.0 */ public function banner_position_callback() { $banner_position = isset($this->options['banner_position']) ? $this->options['banner_position'] : 'bottom'; ?>

options['banner_bg_color']) ? $this->options['banner_bg_color'] : '#1a1a1a'; echo ''; } /** * Text color callback * * @since 1.0.0 */ public function text_color_callback() { $text_color = isset($this->options['text_color']) ? $this->options['text_color'] : '#ffffff'; echo ''; } /** * Show on mobile callback * * @since 1.0.0 */ public function show_on_mobile_callback() { $show_on_mobile = isset($this->options['show_on_mobile']) ? $this->options['show_on_mobile'] : 1; echo ''; echo ''; } /** * Show on desktop callback * * @since 1.0.0 */ public function show_on_desktop_callback() { $show_on_desktop = isset($this->options['show_on_desktop']) ? $this->options['show_on_desktop'] : 1; echo ''; echo ''; } /** * Inline section callback */ public function inline_section_callback() { echo '

' . esc_html__('Configure the inline "Add as Preferred Source" button that appears within your content.', 'add-as-preferred-source') . '

'; } /** * Inline enabled callback */ public function inline_enabled_callback() { $inline_enabled = isset($this->options['inline_enabled']) ? $this->options['inline_enabled'] : 0; echo ''; echo ''; } /** * Inline position callback */ public function inline_position_callback() { $inline_position = isset($this->options['inline_position']) ? $this->options['inline_position'] : 'both'; ?>

options['inline_post_types']) ? $this->options['inline_post_types'] : array('post'); // Get all public post types $args = array( 'public' => true, '_builtin' => false ); $post_types = get_post_types($args, 'objects'); // Add built-in types $post_types['post'] = get_post_type_object('post'); $post_types['page'] = get_post_type_object('page'); echo '
'; foreach ($post_types as $post_file => $post_type) { echo '
'; } echo '
'; echo '

' . esc_html__('Select the post types where the button should appear.', 'add-as-preferred-source') . '

'; } /** * Inline Background Color Callback */ public function inline_bg_color_callback() { $bg_color = isset($this->options['inline_bg_color']) ? $this->options['inline_bg_color'] : '#ffffff'; echo ''; } /** * Inline Text Color Callback */ public function inline_text_color_callback() { $text_color = isset($this->options['inline_text_color']) ? $this->options['inline_text_color'] : '#1c1e21'; echo ''; } /** * Inline text callback */ public function inline_text_callback() { $inline_text = isset($this->options['inline_text']) ? $this->options['inline_text'] : 'Stay connected via Google News'; echo ''; echo '

' . esc_html__('The main title text.', 'add-as-preferred-source') . '

'; } /** * Inline subtitle callback */ public function inline_subtitle_callback() { $inline_subtitle = isset($this->options['inline_subtitle']) ? $this->options['inline_subtitle'] : 'Follow us for the latest travel updates and guides.'; echo ''; echo '

' . esc_html__('The secondary text displayed below the title.', 'add-as-preferred-source') . '

'; } /** * Banner trigger callback */ public function banner_trigger_callback() { $trigger = isset($this->options['banner_trigger']) ? $this->options['banner_trigger'] : 'immediate'; ?>

options['banner_scroll_percent']) ? (int) $this->options['banner_scroll_percent'] : 50; $trigger = isset($this->options['banner_trigger']) ? $this->options['banner_trigger'] : 'immediate'; $style = ( $trigger !== 'scroll' ) ? 'display:none;' : ''; echo '
'; echo ' %'; echo '

' . esc_html__('Show the banner after the visitor has scrolled this percentage of the page (10–90).', 'add-as-preferred-source') . '

'; echo '
'; } /** * Banner delay seconds callback */ public function banner_delay_seconds_callback() { $seconds = isset($this->options['banner_delay_seconds']) ? (int) $this->options['banner_delay_seconds'] : 5; $trigger = isset($this->options['banner_trigger']) ? $this->options['banner_trigger'] : 'immediate'; $style = ( $trigger !== 'time' ) ? 'display:none;' : ''; echo '
'; echo ' ' . esc_html__('seconds', 'add-as-preferred-source'); echo '

' . esc_html__('Show the banner after this many seconds on the page (1–60).', 'add-as-preferred-source') . '

'; echo '
'; } /** * Display rules section callback */ public function display_rules_section_callback() { echo '

' . esc_html__('Control exactly where the banner appears on your site.', 'add-as-preferred-source') . '

'; } /** * Display On callback */ public function display_on_callback() { $display_on = isset($this->options['display_on']) ? $this->options['display_on'] : 'everywhere'; ?>

options['exclude_post_ids']) ? $this->options['exclude_post_ids'] : ''; echo ''; echo '

' . esc_html__('Enter comma-separated post or page IDs where the banner should never appear.', 'add-as-preferred-source') . '

'; } /** * Exclude Categories callback — renders a lightweight custom tag-search widget (no external library). */ public function exclude_categories_callback() { $excluded = isset($this->options['exclude_categories']) ? array_map('intval', (array) $this->options['exclude_categories']) : array(); // Pre-load labels for already-selected categories only (typically a very small set). $preloaded = array(); if (!empty($excluded)) { $terms = get_terms(array( 'taxonomy' => 'category', 'include' => $excluded, 'hide_empty' => false, )); if (!is_wp_error($terms)) { foreach ($terms as $t) { $preloaded[$t->term_id] = $t->name . ' (' . $t->count . ')'; } } } echo ''; // .adasprso-cat-search echo '

' . esc_html__('Search and select categories where the banner should not appear.', 'add-as-preferred-source') . '

'; } /** * AJAX handler — returns up to 20 categories matching a search string. */ public function ajax_search_categories() { check_ajax_referer('adasprso_categories_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('unauthorized'); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce already verified above $search = isset($_GET['q']) ? sanitize_text_field(wp_unslash($_GET['q'])) : ''; $terms = get_terms(array( 'taxonomy' => 'category', 'search' => $search, 'number' => 20, 'hide_empty' => false, )); $results = array(); if (!is_wp_error($terms)) { foreach ($terms as $term) { $results[] = array( 'id' => $term->term_id, 'text' => $term->name . ' (' . $term->count . ')', ); } } wp_send_json(array('results' => $results)); } }