';
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 ' Generate Snippet with AI ';
// 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
'
. '
Cancel
'
. '
';
}
// 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 ) { ?>
Generate with AI Snippet SEO Helper
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 ''
. 'AI Snippet SEO Helper '
. 'Has Focus Keyword '
. 'Missing Focus Keyword '
. 'Score: Poor (0/33) '
. 'Score: Fair (66/100) '
. 'Score: Excellent (100/100) '
. ' ';
});
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;
}
});