'; echo esc_html__( 'AI Snippet SEO Helper works as an integration for Rank Math SEO and requires the Rank Math SEO plugin to be installed and active.', 'ai-snippet-seo-helper' ); echo '

'; } } // Check if Rank Math SEO is active. On WordPress 6.5+ the "Requires Plugins" header // already blocks activation without Rank Math; this notice covers older versions and // the case where Rank Math is deactivated after this plugin was activated. if ( ! function_exists( 'is_plugin_active' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } if ( ! function_exists( 'is_plugin_active' ) || ! is_plugin_active( 'seo-by-rank-math/rank-math.php' ) ) { add_action( 'admin_notices', 'aish_rank_math_required_notice' ); // Do not register metaboxes, bulk actions or other helpers if Rank Math is not active. return; } } /* Simple helpers (kept separate to avoid PRO collisions) */ if ( ! function_exists('aish_model_cost') ) { function aish_model_cost(string $model): int { $m = strtolower(trim($model)); if ($m === 'gpt-4o') return 5; if ($m === 'gpt-4.1') return 3; if ($m === 'o3') return 12; return 1; // gpt-4-turbo } } if ( ! function_exists('aish_status_remaining') ) { function aish_status_remaining(array $st): int { if (isset($st['remaining'])) return (int)$st['remaining']; if (isset($st['remaining_total'])) return (int)$st['remaining_total']; if (isset($st['global_remaining'])) return (int)$st['global_remaining']; if (isset($st['plan_limit_total'], $st['usage_total'])) return max(0, (int)$st['plan_limit_total'] - (int)$st['usage_total']); if (isset($st['plan_limit'], $st['usage'])) return max(0, (int)$st['plan_limit'] - (int)$st['usage']); return 0; } } /* Token-info fallback shim */ if ( ! function_exists('aish_get_token_status') ) { function aish_get_token_status(string $site_token, string $plugin = 'ai-snippet-seo-helper'): array { $resp = wp_remote_post( home_url('/wp-json/rankpilotai/v1/token-info'), [ 'headers' => ['Content-Type' => 'application/json'], 'body' => wp_json_encode([ 'site_token' => $site_token, 'plugin' => $plugin, ]), 'timeout' => 15, ]); if ( is_wp_error($resp) || wp_remote_retrieve_response_code($resp) !== 200 ) { return ['remaining' => 0, 'error' => 'token-info']; } return json_decode( wp_remote_retrieve_body($resp), true ) ?: ['remaining'=>0]; } } /* ADMIN ASSETS (post editor + list/bulk) */ function aish_enqueue_admin_assets( $hook ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. $force = ! empty( $_GET['aish_job'] ); $is_settings = ( $hook === 'toplevel_page_ai-snippet-seo-helper' ); if ( ! $force && ! $is_settings && ! in_array( $hook, [ 'post.php', 'post-new.php', 'edit.php' ], true ) ) { return; } wp_enqueue_style( 'aish-admin-css', AISH_URL . 'admin/assets/css/admin.css', [], AISH_VERSION ); wp_enqueue_script( 'aish-admin-js', AISH_URL . 'admin/assets/js/admin.js', [ 'jquery' ], AISH_VERSION, true ); $opts = get_option( AISH_OPT_KEY, [] ); wp_localize_script( 'aish-admin-js', 'AISH_Ajax', [ 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aish_nonce' ), 'rest_url' => home_url( '/wp-json/rankpilotai/v1' ), 'home' => home_url( '/' ), 'mode' => aish_active_mode(), ] ); } add_action( 'admin_enqueue_scripts', 'aish_enqueue_admin_assets' ); add_action( 'wp_ajax_aish_bulk_tick', 'aish_ajax_bulk_tick' ); add_action( 'wp_ajax_aish_bulk_cancel', 'aish_ajax_bulk_cancel' ); /* Trial register AJAX */ function aish_ajax_trial_register() { check_ajax_referer( 'aish_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'msg' => 'Unauthorized' ] ); } $res = wp_remote_post( 'https://rankpilotai.com/wp-json/rankpilotai/v1/trial-register', [ 'headers' => [ 'Content-Type' => 'application/json' ], 'body' => wp_json_encode( [ 'site_url' => home_url(), 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ), 'plugin' => 'ai-snippet-seo-helper', ] ), 'timeout' => 15, ] ); if ( is_wp_error( $res ) ) { wp_send_json_error( [ 'msg' => $res->get_error_message() ] ); } $code = (int) wp_remote_retrieve_response_code( $res ); $json = json_decode( wp_remote_retrieve_body( $res ), true ); if ( $code !== 200 || empty( $json['trial_token'] ) ) { wp_send_json_error( [ 'msg' => aish_error_text( $json['error'] ?? null, 'Registration failed.' ) ] ); } $opts = get_option( AISH_OPT_KEY, [] ); $opts['trial_token'] = sanitize_text_field( $json['trial_token'] ); $opts['trial_remaining'] = (int) ( $json['remaining'] ?? 25 ); update_option( AISH_OPT_KEY, $opts ); wp_send_json_success( [ 'trial_token' => $opts['trial_token'], 'remaining' => $opts['trial_remaining'], ] ); } add_action( 'wp_ajax_aish_trial_register', 'aish_ajax_trial_register' ); /* Save BYOK key AJAX */ function aish_ajax_save_byok_key() { check_ajax_referer( 'aish_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'msg' => 'Unauthorized' ] ); } $key = sanitize_text_field( wp_unslash( $_POST['openai_key'] ?? '' ) ); if ( ! $key || strpos( $key, 'sk-' ) !== 0 ) { wp_send_json_error( [ 'msg' => 'Invalid API key. Must start with sk-' ] ); } $opts = get_option( AISH_OPT_KEY, [] ); $opts['openai_key'] = $key; update_option( AISH_OPT_KEY, $opts ); wp_send_json_success( [ 'masked' => substr( $key, 0, 7 ) . '...' . substr( $key, -4 ) ] ); } add_action( 'wp_ajax_aish_save_byok_key', 'aish_ajax_save_byok_key' ); /* Remove BYOK key AJAX */ function aish_ajax_remove_byok_key() { check_ajax_referer( 'aish_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'msg' => 'Unauthorized' ] ); } $opts = get_option( AISH_OPT_KEY, [] ); unset( $opts['openai_key'] ); update_option( AISH_OPT_KEY, $opts ); wp_send_json_success(); } add_action( 'wp_ajax_aish_remove_byok_key', 'aish_ajax_remove_byok_key' ); /* Remove trial AJAX */ function aish_ajax_remove_trial() { check_ajax_referer( 'aish_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'msg' => 'Unauthorized' ] ); } $opts = get_option( AISH_OPT_KEY, [] ); unset( $opts['trial_token'], $opts['trial_remaining'] ); update_option( AISH_OPT_KEY, $opts ); wp_send_json_success(); } add_action( 'wp_ajax_aish_remove_trial', 'aish_ajax_remove_trial' ); /* OPTIONAL Rank Math metabox button (kept commented intentionally) */ // add_action( 'rank_math/metabox/before', 'aish_add_rm_button' ); // function aish_add_rm_button( $metabox ) { // if ( $metabox->id !== 'rank_math_metabox' ) return; // echo '
'; // echo ' '; // echo ' '; // echo '
'; // } /* SINGLE POST AJAX */ function aish_ajax_generate_single() { check_ajax_referer( 'aish_nonce', 'nonce' ); $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0; if ( ! $post_id ) { wp_send_json_error( [ 'msg' => 'ID missing' ] ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_send_json_error( [ 'msg' => 'Insufficient permissions.' ] ); } try { $data = aish_generate_snippet_for_post( $post_id, true ); if ( isset( $data['score'] ) ) { $data['score'] = (int) $data['score']; } wp_send_json_success( $data ); } catch ( Exception $e ) { wp_send_json_error( [ 'msg' => $e->getMessage() ] ); } } add_action( 'wp_ajax_aish_generate_single', 'aish_ajax_generate_single' ); /* Bulk job storage (transient-backed) */ function aish_job_key($id){ return 'aish_job_'.$id; } function aish_get_job($id){ return get_transient(aish_job_key($id)); } function aish_save_job($id,$data){ set_transient(aish_job_key($id), $data, HOUR_IN_SECONDS); } function aish_delete_job($id){ delete_transient(aish_job_key($id)); } /* Create bulk job descriptor */ function aish_create_bulk_job(array $ids, string $site_token, string $model, array $opts){ $job_id = wp_generate_password(12, false, false); aish_save_job($job_id, [ 'id' => $job_id, 'ids' => array_values($ids), 'i' => 0, 'total' => count($ids), 's' => 0, 'f' => 0, 'flags' => ['zerotok'=>false,'insuff'=>false], 'site_token' => $site_token, 'model' => strtolower($model ?: 'gpt-4-turbo'), 'opts' => $opts, ]); return $job_id; } /* AJAX: process one item per tick */ function aish_ajax_bulk_tick(){ check_ajax_referer('aish_nonce','nonce'); if ( ! current_user_can('edit_posts') ) { wp_send_json_error(['msg'=>'Unauthorized']); } $jid = sanitize_text_field( wp_unslash( $_POST['job'] ?? '' ) ); if ( ! $jid ) wp_send_json_error(['msg'=>'Missing job id']); $job = aish_get_job($jid); if ( ! is_array($job) ) wp_send_json_error(['msg'=>'Job not found']); if ( $job['i'] >= $job['total'] ) { $args = [ 'aish_bsuccess' => (int)$job['s'], 'aish_bfailed' => (int)$job['f'], ]; if ( !empty($job['flags']['zerotok']) ) $args['aish_bzerotok']=1; if ( !empty($job['flags']['insuff']) ) $args['aish_binsufficient']=1; aish_delete_job($jid); $base = wp_get_referer(); if ( ! $base ) $base = admin_url('edit.php'); $redir = add_query_arg($args, remove_query_arg('aish_job', $base)); wp_send_json_success(['done'=>true,'redirect'=>$redir]); } $id = (int)$job['ids'][ $job['i'] ]; $opts = $job['opts']; $site = $job['site_token']; $mdl = $job['model']; $item = aish_prepare_snippet_item_for_post( $id, $opts ); $ok=false; $err=''; $zerotok=false; $insuff=false; try{ $res = aish_call_rankpilot_api_bulk( $site, [$item], $mdl, $opts ); $list = $res['items'] ?? []; if ( array_keys($list) === range(0, count($list)-1) ){ $row = $list[0] ?? []; } else { $row = $list[$id] ?? []; } if ( ! empty($row['error']) ){ $txt = aish_error_text( $row['error'], 'Request failed.' ); if ( stripos($txt,'No tokens left') !== false ) $zerotok = true; if ( stripos($txt,'Not enough tokens') !== false ) $insuff = true; $err = $txt; } else { $ok = aish_apply_snippet_result_for_post( $id, $row, $opts ); } } catch (\Exception $ex){ $err = $ex->getMessage(); if ( stripos($err,'No tokens left') !== false ) $zerotok = true; if ( stripos($err,'Not enough tokens') !== false ) $insuff = true; } $job['i']++; if ($ok) $job['s']++; else $job['f']++; if ($zerotok) $job['flags']['zerotok'] = true; if ($insuff) $job['flags']['insuff'] = true; aish_save_job($jid,$job); wp_send_json_success([ 'done' => false, 'i' => (int)$job['i'], 'total' => (int)$job['total'], 's' => (int)$job['s'], 'f' => (int)$job['f'], 'flags' => $job['flags'], 'lastId' => $id, 'error' => $ok ? '' : $err, ]); } /* AJAX: cancel bulk job */ function aish_ajax_bulk_cancel(){ check_ajax_referer('aish_nonce','nonce'); if ( ! current_user_can('edit_posts') ) { wp_send_json_error(['msg'=>'Unauthorized']); } $jid = sanitize_text_field( wp_unslash( $_POST['job'] ?? '' ) ); if ($jid) aish_delete_job($jid); wp_send_json_success(); } /* MAIN GENERATOR (single post) — routes by active mode */ function aish_generate_snippet_for_post( int $post_id, bool $return = false ) { $opts = get_option( AISH_OPT_KEY, [] ); if ( ! isset( $opts['auto_slug'] ) ) $opts['auto_slug'] = 'no'; $mode = aish_active_mode(); if ( $mode === 'none' ) { throw new Exception( 'Please configure the plugin in Settings first.' ); } $title = get_the_title( $post_id ); $content = wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ); $excerpt = wp_trim_words( $content, 50, '...' ); $focus_kw = get_post_meta( $post_id, 'rank_math_focus_keyword', true ); if ( $opts['auto_slug'] !== 'yes' && empty( $focus_kw ) ) { $slug_now = get_post_field( 'post_name', $post_id ); $focus_kw = preg_replace( '/[-_]+/', ' ', $slug_now ); } $post_data = [ 'context_type' => 'post', 'post_title' => $title, 'post_excerpt' => $excerpt, 'post_type' => get_post_type( $post_id ), 'focus_keyword' => $focus_kw, 'slug' => get_post_field( 'post_name', $post_id ), ]; $remaining = null; /* ── Route by mode ── */ if ( $mode === 'pro' ) { $site_token = $opts['site_token'] ?? ''; $model = strtolower( $opts['model_choice'] ?? 'gpt-4-turbo' ); $need = aish_model_cost( $model ); $status = function_exists('aish_check_token_status') ? aish_check_token_status( $site_token ) : aish_get_token_status( $site_token, 'ai-snippet-seo-helper' ); $remaining = aish_status_remaining( $status ); if ( $remaining <= 0 ) throw new Exception( 'No tokens left. Please upgrade.' ); if ( $remaining < $need ) throw new Exception( 'Not enough tokens for the selected model.' ); $payload = array_merge( $post_data, [ 'model' => $model, 'custom_prompt' => trim( (string) ( $opts['custom_prompt'] ?? '' ) ), 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ), 'plugin' => 'ai-snippet-seo-helper', 'auto_slug' => ( $opts['auto_slug'] ?? 'no' ) === 'yes', ] ); $res = aish_call_rankpilot_api( $site_token, $payload ); } elseif ( $mode === 'byok' ) { $api_key = $opts['openai_key'] ?? ''; if ( ! $api_key ) throw new Exception( 'OpenAI API key missing.' ); $res = aish_call_openai_direct( $api_key, $post_data ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- delivered as JSON to admin.js and shown with alert(); never echoed as HTML. if ( ! empty( $res['error'] ) ) throw new Exception( $res['error'] ); } elseif ( $mode === 'trial' ) { $trial_token = $opts['trial_token'] ?? ''; if ( ! $trial_token ) throw new Exception( 'Trial token missing.' ); $res = aish_call_trial_api( $trial_token, $post_data ); if ( ! empty( $res['error'] ) ) { $msg = $res['error']; if ( ! empty( $res['exhausted'] ) ) $msg = 'Free trial tokens exhausted. Enter an OpenAI API key or upgrade to Pro.'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- delivered as JSON to admin.js and shown with alert(); never echoed as HTML. throw new Exception( $msg ); } $remaining = $res['remaining'] ?? null; if ( $remaining !== null ) { $opts['trial_remaining'] = (int) $remaining; update_option( AISH_OPT_KEY, $opts ); } } $kw = $res['keyword'] ?? ''; $ti = $res['title'] ?? ''; $desc = $res['description'] ?? ''; $slug = $res['slug'] ?? ''; /* Apply Rank Math fields */ update_post_meta( $post_id, 'rank_math_focus_keyword', $focus_kw ?: $kw ); if ( $ti ) update_post_meta( $post_id, 'rank_math_title', $ti ); if ( $desc ) update_post_meta( $post_id, 'rank_math_description', $desc ); /* Optional slug update (Pro only) */ $auto = ( $mode === 'pro' ) ? ( $opts['auto_slug'] ?? 'no' ) : 'no'; if ( $auto === 'yes' && $slug ) { $san = sanitize_title( $slug ); $current_slug = get_post_field( 'post_name', $post_id ); if ( $san && $san !== $current_slug ) { add_post_meta( $post_id, '_wp_old_slug', $current_slug ); wp_update_post( [ 'ID' => $post_id, 'post_name' => $san ] ); update_post_meta( $post_id, '_edit_last', get_current_user_id() ); } } /* Score */ $final_slug = get_post_field( 'post_name', $post_id ); $used_kw = $focus_kw ?: $kw; $sc = aish_calc_score( $used_kw, $ti, $desc, $final_slug ); update_post_meta( $post_id, '_aish_snippet_score', $sc['score'] ); update_post_meta( $post_id, '_aish_score', $sc['score'] ); if ( $return ) { $out = [ 'keyword' => $kw, 'title' => $ti, 'desc' => $desc, 'slug' => $final_slug, 'score' => $sc['score'], ]; if ( $remaining !== null ) $out['remaining'] = $remaining; return $out; } } /* BULK-ACTION SUPPORT (Pro only) */ function aish_register_bulk_actions() { if ( aish_active_mode() !== 'pro' ) return; $pts = get_post_types( [ 'public' => true ], 'names' ); foreach ( $pts as $pt ) { add_filter( "bulk_actions-edit-{$pt}", function( $acts ) { $acts['aish_generate_ai'] = 'Generate with AI Snippet SEO Helper'; return $acts; } ); add_filter( "handle_bulk_actions-edit-{$pt}", function( $redirect, $action, $post_ids ) { if ( $action !== 'aish_generate_ai' ) return $redirect; $opts = get_option( AISH_OPT_KEY, [] ); $site_token = $opts['site_token'] ?? ''; if ( ! $site_token ) { return add_query_arg( 'aish_berror', 'notoken', $redirect ); } $model_sel = strtolower( $opts['model_choice'] ?? 'gpt-4-turbo' ); $ids = []; foreach ( (array) $post_ids as $pid ) { $pid = (int) $pid; if ( $pid > 0 && current_user_can('edit_post', $pid) ) $ids[] = $pid; } if ( empty($ids) ) return $redirect; $st = aish_check_token_status( $site_token ); if ( isset($st['error']) ) { return add_query_arg( 'aish_berror', 'tokencheck', $redirect ); } $remaining = aish_status_remaining( $st ); $need_each = aish_model_cost( $model_sel ); $need_all = $need_each * count($ids); if ( $remaining <= 0 ) { return add_query_arg(['aish_bsuccess'=>0,'aish_bfailed'=>count($ids),'aish_bzerotok'=>1], $redirect); } if ( $remaining < $need_all ) { return add_query_arg(['aish_bsuccess'=>0,'aish_bfailed'=>count($ids),'aish_binsufficient'=>1], $redirect); } $job_id = aish_create_bulk_job( $ids, $site_token, $model_sel, $opts ); return add_query_arg(['aish_job'=>$job_id], $redirect); }, 10, 3 ); } } add_action( 'admin_init', 'aish_register_bulk_actions' ); /* ADMIN NOTICES (bulk progress + results) */ add_action('admin_notices', function (){ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( ! empty($_GET['aish_job']) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. $jid = sanitize_text_field( wp_unslash( $_GET['aish_job'] ) ); echo '
' . '

AI Snippet SEO Helper: Running bulk job… ' . 'starting

' . '

' . '
'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( isset($_GET['aish_berror']) && $_GET['aish_berror'] === 'notoken' ) { echo '

AI Snippet SEO Helper: No Site Key configured.

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( ! empty($_GET['aish_bzerotok']) ) { echo '

AI Snippet SEO Helper: Failed – no tokens left. ' . 'Buy Tokens to continue.

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( ! empty($_GET['aish_binsufficient']) ) { echo '

AI Snippet SEO Helper: Not enough tokens for the selected model. ' . 'Please switch to a cheaper model (e.g. GPT-4 Turbo) from ' . 'SEO Settings ' . 'or Buy Tokens.

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( isset($_GET['aish_bsuccess']) || isset($_GET['aish_bfailed']) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- read-only display parameter, no state change; cast to int is the sanitization, absint() would flip a negative and change which notice renders. $s = (int) wp_unslash( $_GET['aish_bsuccess'] ?? 0 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- read-only display parameter, no state change; cast to int is the sanitization, absint() would flip a negative and change which notice renders. $f = (int) wp_unslash( $_GET['aish_bfailed'] ?? 0 ); if ( $s > 0 ) { echo '

AI Snippet SEO Helper: Generated for ' . intval($s) . ' item(s).

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. if ( $f > 0 && empty($_GET['aish_bzerotok']) ) { echo '

AI Snippet SEO Helper: Failed for ' . intval($f) . ' item(s).

'; } } }); /* PLUGIN LIST: Settings link */ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), function ( $links ) { $url = admin_url( 'admin.php?page=ai-snippet-seo-helper' ); $label = esc_html__( 'Settings', 'ai-snippet-seo-helper' ); array_unshift( $links, '' . $label . '' ); return $links; } ); /* MENU */ add_action( 'admin_menu', function () { add_menu_page( 'AI Snippet SEO Helper', 'AI Snippet SEO Helper', 'manage_options', 'ai-snippet-seo-helper', 'aish_render_settings_page', plugin_dir_url(__FILE__) . 'admin/assets/img/ai-snippet-seo-helper.svg', 25 ); } ); /* SIDE METABOX: button + live score badge */ add_action( 'add_meta_boxes', function () { foreach ( get_post_types( ['public' => true], 'names' ) as $pt ) { add_meta_box( 'aish_seo_helper', 'AI Snippet SEO Helper', function ( $post ) { ?>
ID, '_aish_snippet_score', true ); if ($raw === '' || $raw === null) { $raw = get_post_meta( $post->ID, '_aish_score', true ); } $sc = (int)$raw; $col = ($sc >= 100) ? '#2980b9' : (($sc >= 66) ? '#f39c12' : '#e74c3c'); ?>
SEO Snippet Score: / 100
= 100 ) ? '#2980b9' : ( ( $sc >= 66 ) ? '#f39c12' : '#e74c3c' ); $style = sprintf( 'display:inline-block;padding:2px 6px;color:#fff;border-radius:3px;background:%s', $colr ); echo '' . intval( $sc ) . ' / 100'; }, 10, 2 ); } /* LIST FILTERS: FK and Score buckets */ add_action('restrict_manage_posts', function () { $screen = get_current_screen(); if ($screen->base !== 'edit') return; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only display parameter, no state change. $val = isset($_GET['aish_filter']) ? sanitize_text_field( wp_unslash( $_GET['aish_filter'] ) ) : ''; echo ''; }); add_action('pre_get_posts', function($q){ if (!is_admin() || !$q->is_main_query()) return; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only filter parameter, no state change. $f = isset($_GET['aish_filter']) ? sanitize_text_field( wp_unslash( $_GET['aish_filter'] ) ) : ''; if (!$f) return; $score_keys = ['_aish_snippet_score','_aish_score']; if ($f === 'has_kw') { $q->set('meta_query', [[ 'key' => 'rank_math_focus_keyword', 'value' => '', 'compare' => '!=', ]]); return; } if ($f === 'missing_kw') { $q->set('meta_query', [ 'relation' => 'OR', [ 'key' => 'rank_math_focus_keyword', 'compare' => 'NOT EXISTS' ], [ 'key' => 'rank_math_focus_keyword', 'value' => '', 'compare' => '=' ], ]); return; } if ($f === 'score_poor') { $vals = [0,33]; $mq = ['relation'=>'OR']; foreach ($score_keys as $k) { $mq[] = ['key'=>$k,'value'=>$vals,'compare'=>'IN','type'=>'NUMERIC']; } $q->set('meta_query', $mq); return; } if ($f === 'score_fair' || $f === 'score_excellent') { $target = ($f === 'score_fair') ? 66 : 100; $mq = ['relation'=>'OR']; foreach ($score_keys as $k) { $mq[] = ['key'=>$k,'value'=>$target,'compare'=>'=','type'=>'NUMERIC']; } $q->set('meta_query', $mq); return; } });