'', 'business_name' => get_bloginfo( 'name' ), 'business_type' => 'LocalBusiness', 'description' => get_bloginfo( 'description' ), 'url' => esc_url( home_url() ), 'phone' => '', 'email' => sanitize_email( get_option( 'admin_email' ) ), 'street' => '', 'town' => '', 'county' => '', 'postcode' => '', 'founded' => '', 'price_range' => '££', 'ch_number' => '', 'sameas_google' => '', 'sameas_linkedin'=> '', 'sameas_facebook'=> '', 'services' => '', 'areas' => '', 'schema_enabled' => '1', 'llms_enabled' => '1', 'faq_enabled' => '1', 'last_score' => '', 'last_updated' => '', ); foreach ( $defaults as $key => $value ) { if ( false === get_option( AENIMA_PREFIX . $key ) ) { update_option( AENIMA_PREFIX . $key, $value ); } } if ( ! wp_next_scheduled( 'aenima_monthly_refresh' ) ) { wp_schedule_event( time(), 'aenima_monthly', 'aenima_monthly_refresh' ); } flush_rewrite_rules(); } register_deactivation_hook( __FILE__, 'aenima_deactivate' ); function aenima_deactivate() { wp_clear_scheduled_hook( 'aenima_monthly_refresh' ); flush_rewrite_rules(); } // ── DEACTIVATION WARNING ─────────────────────────────────────────────────── add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'aenima_plugin_action_links' ); function aenima_plugin_action_links( $links ) { $settings_link = '' . esc_html__( 'Settings', 'aenima-ai-search-visibility') . ''; array_unshift( $links, $settings_link ); return $links; } add_action( 'admin_footer', 'aenima_deactivation_warning' ); function aenima_deactivation_warning() { $screen = get_current_screen(); if ( ! $screen || 'plugins' !== $screen->id ) { return; } $msg = implode( "\n", array( 'Deactivating Aenima - important:', '', 'Your Schema JSON-LD and llms.txt files will remain on your site.', 'They will no longer update automatically each month.', 'Your AI visibility will gradually become outdated.', '', 'Your Aenima subscription will continue to be billed unless you cancel at aenima.co.uk.', '', 'Are you sure you want to deactivate?', ) ); $js = 'jQuery(function($){' . 'var l = $("[data-plugin=\'aenima/aenima.php\'] .deactivate a");' . 'if(!l.length)return;' . 'l.on("click",function(e){if(!confirm(' . wp_json_encode( $msg ) . ')){e.preventDefault();}});' . '});'; wp_add_inline_script( 'jquery', $js ); } // ── CUSTOM CRON SCHEDULE — MONTHLY ──────────────────────────────────────── add_filter( 'cron_schedules', 'aenima_add_monthly_schedule' ); function aenima_add_monthly_schedule( $schedules ) { $schedules['aenima_monthly'] = array( 'interval' => 30 * DAY_IN_SECONDS, 'display' => esc_html__( 'Once a month', 'aenima-ai-search-visibility'), ); return $schedules; } // ── LICENCE VERIFICATION ─────────────────────────────────────────────────── /** * Check whether a token has been entered. * The API validates the token server-side on each call — no phone-home licence check. */ function aenima_has_token() { return ! empty( get_option( AENIMA_PREFIX . 'api_token', '' ) ); } // ── SCORE CHECK VIA AJAX ─────────────────────────────────────────────────── add_action( 'wp_ajax_aenima_run_score', 'aenima_ajax_run_score' ); function aenima_ajax_run_score() { check_ajax_referer( 'aenima_score_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( 'Unauthorised' ); } $domain = sanitize_text_field( get_option( AENIMA_PREFIX . 'url', home_url() ) ); $domain = preg_replace( '#^https?://#', '', $domain ); $domain = rtrim( $domain, '/' ); $response = wp_remote_post( AENIMA_API_BASE . '/check-domain', array( 'timeout' => 20, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'domain' => $domain ) ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( 'Could not reach Aenima API. Please try again.' ); } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $body['overall'] ) && $body['overall'] !== 0 ) { wp_send_json_error( 'Score check failed. Please try again.' ); } // Cache the score update_option( AENIMA_PREFIX . 'last_score', intval( $body['overall'] ) ); update_option( AENIMA_PREFIX . 'last_issues', wp_json_encode( $body['issues'] ?? array() ) ); update_option( AENIMA_PREFIX . 'last_updated', date_i18n( get_option( 'date_format' ) ) ); wp_send_json_success( $body ); } // ── ENQUEUE ADMIN SCRIPTS ────────────────────────────────────────────────── add_action( 'admin_enqueue_scripts', 'aenima_settings_styles' ); function aenima_settings_styles( $hook ) { if ( 'settings_page_aenima' !== $hook ) { return; } $css = '#aenima-wrap { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; } #aenima-wrap * { box-sizing:border-box; } .ae-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:16px; margin-bottom:16px; } .ae-row-2 { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; } .ae-row-full { margin-bottom:16px; } .ae-card { background:#fff; border:1px solid #e0e0e0; border-radius:10px; padding:22px; } .ae-card h2 { font-size:16px; font-weight:700; color:#111; margin:0 0 4px; } .ae-card p.ae-sub { font-size:14px; color:#444; margin:0 0 16px; line-height:1.6; } .ae-label { display:block; font-size:13px; font-weight:600; color:#222; margin-bottom:5px; } .ae-input { width:100%; padding:9px 12px; border:1px solid #ccc; border-radius:6px; font-size:14px; font-family:inherit; color:#111; } .ae-input:focus { outline:none; border-color:#F05A22; box-shadow:0 0 0 2px rgba(221,88,39,0.15); } .ae-check-row { display:flex; align-items:flex-start; gap:10px; margin-bottom:12px; } .ae-check-row input[type=checkbox] { width:17px; height:17px; margin-top:2px; flex-shrink:0; accent-color:#F05A22; } .ae-check-row .ae-check-text strong { font-size:14px; color:#111; display:block; margin-bottom:2px; } .ae-check-row .ae-check-text span { font-size:13px; color:#444; line-height:1.5; } .ae-save-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:12px; margin-bottom:16px; } .ae-save-btn { display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:14px 16px; border-radius:8px; font-family:inherit; font-weight:700; cursor:pointer; text-decoration:none; border:none; min-height:64px; font-size:14px; line-height:1.3; } .ae-save-btn-primary { background:#F05A22; color:#fff; } .ae-save-btn-primary:hover { background:#C04820; color:#fff; } .ae-save-btn-secondary { background:#fff; color:#333; border:2px solid #ddd; } .ae-save-btn-secondary:hover { border-color:#F05A22; color:#F05A22; } .ae-save-btn small { font-size:11px; font-weight:400; opacity:0.75; display:block; margin-top:3px; } .ae-badge { font-size:12px; font-weight:700; padding:3px 10px; border-radius:20px; display:inline-block; } .ae-section-title { font-size:11px; font-weight:700; letter-spacing:2px; text-transform:uppercase; color:#777; margin:0 0 12px; }'; wp_add_inline_style( 'wp-admin', $css ); } add_action( 'admin_enqueue_scripts', 'aenima_admin_scripts' ); function aenima_admin_scripts( $hook ) { if ( 'settings_page_aenima' !== $hook ) { return; } wp_localize_script( 'jquery', 'aenima_ajax', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aenima_score_nonce' ), 'init_score' => get_option( AENIMA_PREFIX . 'last_score', '' ), ) ); wp_add_inline_style( 'wp-admin', '.ae-faq details{border-bottom:1px solid #eee;padding:8px 0}.ae-faq details:last-child{border-bottom:none}.ae-faq summary{font-size:13px;font-weight:700;color:#333;cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center}.ae-faq summary::after{content:"+";color:#F05A22;font-size:16px;flex-shrink:0}.ae-faq details[open] summary::after{content:"-"}.ae-faq details p{font-size:13px;color:#666;line-height:1.6;margin:8px 0 0;padding-left:2px}' ); } // ── SUPPRESS THIRD-PARTY NOTICES ON OUR PAGE ────────────────────────────── add_action( 'admin_head', 'aenima_suppress_notices' ); function aenima_suppress_notices() { $screen = get_current_screen(); if ( ! $screen || 'settings_page_aenima' !== $screen->id ) { return; } // Remove all admin notices from other plugins/themes on our page remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); // Re-add only ours add_action( 'admin_notices', 'aenima_admin_notices' ); } // ── HIGHLIGHT AENIMA IN SETTINGS MENU ───────────────────────────────────── add_action( 'admin_enqueue_scripts', 'aenima_menu_highlight_style' ); function aenima_menu_highlight_style() { $css = '#adminmenu a[href="options-general.php?page=aenima"]{background:#F05A22 !important;color:#FFFFFF !important;font-weight:700 !important;border-radius:4px !important}#adminmenu a[href="options-general.php?page=aenima"]:hover{background:#C04820 !important}'; wp_add_inline_style( 'wp-admin', $css ); } add_action( 'admin_init', 'aenima_register_settings' ); function aenima_register_settings() { $fields = array( 'api_token', 'business_name', 'business_type', 'description', 'url', 'phone', 'email', 'street', 'town', 'county', 'postcode', 'founded', 'price_range', 'services', 'areas', 'ch_number', 'sameas_google', 'sameas_linkedin', 'sameas_facebook', 'schema_enabled', 'llms_enabled', 'faq_enabled', ); foreach ( $fields as $field ) { register_setting( 'aenima_settings_group', AENIMA_PREFIX . $field, array( 'sanitize_callback' => 'sanitize_textarea_field' ) ); } } // ── ADMIN MENU ───────────────────────────────────────────────────────────── add_action( 'admin_menu', 'aenima_admin_menu' ); function aenima_admin_menu() { add_options_page( esc_html__( 'Aenima — AI Visibility', 'aenima-ai-search-visibility'), esc_html__( 'Aenima', 'aenima-ai-search-visibility'), 'manage_options', 'aenima', 'aenima_settings_page' ); } // ── DASHBOARD WIDGET ─────────────────────────────────────────────────────── add_action( 'wp_dashboard_setup', 'aenima_register_dashboard_widget' ); function aenima_register_dashboard_widget() { wp_add_dashboard_widget( 'aenima_score_widget', esc_html__( 'AI Visibility — Aenima', 'aenima-ai-search-visibility'), 'aenima_dashboard_widget_render' ); } function aenima_dashboard_widget_render() { $score = get_option( AENIMA_PREFIX . 'last_score', '' ); $updated = get_option( AENIMA_PREFIX . 'last_updated', '' ); $next_run = wp_next_scheduled( 'aenima_monthly_refresh' ); $settings = admin_url( 'options-general.php?page=aenima' ); if ( 'active' !== $status ) { echo '
✓
✓ No critical issues — your AI visibility is clean.
';} else{ var h=''; iss.slice(0,5).forEach(function(i){ var bg=i.p==='critical'?'rgba(204,34,0,0.2)':'rgba(204,102,0,0.2)',cl=i.p==='critical'?'#FF6B6B':'#FFB347'; h+=''+(r.data||'Check failed — please try again.')+'
';} }catch(e){list.innerHTML='Unexpected error. Please try again.
';} }; xhr.onerror=function(){btn.disabled=false;btn.innerHTML='↻ RE-CHECKNetwork error. Please try again.
';}; xhr.send('action=aenima_run_score&nonce='+aenima_ajax.nonce); } window.addEventListener('DOMContentLoaded',function(){if(aenimaInitScore!==null)setTimeout(function(){aenimaSetDisc(aenimaInitScore);},300);}); AENIMA_JS ); ?>([^<]{20,})/s', $content, $m ); if ( ! empty( $m[1] ) ) { foreach ( $m[1] as $i => $q ) { $a = wp_strip_all_tags( $m[2][ $i ] ); if ( $q && strlen( $a ) > 20 ) { $faqs[] = array( 'q' => trim( $q ), 'a' => trim( $a ) ); } } } if ( empty( $faqs ) ) { return; } $faq_schema = array( '@context' => 'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => array_map( function( $f ) { return array( '@type' => 'Question', 'name' => $f['q'], 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $f['a'] ), ); }, array_slice( $faqs, 0, 10 ) ), ); $json = wp_json_encode( $faq_schema, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); if ( ! $json ) { return; } echo '\n"; } // ── LLMS.TXT BUILDER ─────────────────────────────────────────────────────── function aenima_build_llms() { $get = function( $key, $default = '' ) { return sanitize_text_field( get_option( AENIMA_PREFIX . $key, $default ) ); }; $name = $get( 'business_name', get_bloginfo( 'name' ) ); $desc = $get( 'description' ); $url = esc_url( get_option( AENIMA_PREFIX . 'url', home_url() ) ); $phone = $get( 'phone' ); $email = sanitize_email( get_option( AENIMA_PREFIX . 'email', '' ) ); $town = $get( 'town' ); $county = $get( 'county' ); $type = $get( 'business_type', 'LocalBusiness' ); $founded = $get( 'founded' ); $today = gmdate( 'Y-m-d' ); $base_url = rtrim( $url, '/' ); $services = array_filter( array_map( 'sanitize_text_field', explode( "\n", get_option( AENIMA_PREFIX . 'services', '' ) ) ) ); $areas = array_filter( array_map( 'sanitize_text_field', explode( "\n", get_option( AENIMA_PREFIX . 'areas', '' ) ) ) ); $location = implode( ', ', array_filter( array( $town, $county, 'UK' ) ) ); $first_area = ! empty( $areas ) ? reset( $areas ) : $location; // ── Header ───────────────────────────────────────────────────────────── $txt = "# {$name}\n"; $txt .= "# {$base_url}\n"; $txt .= "# Last updated: {$today}\n\n"; // ── Gemini signal 5: Quick Summary FRONT-LOADED ──────────────────────── // AI models chunk text — critical facts MUST be in first 100 tokens $txt .= "## Quick Summary\n\n"; $txt .= "{$name} is a {$type} based in {$first_area}."; if ( ! empty( $services ) ) { $top = implode( ', ', array_slice( array_values( $services ), 0, 3 ) ); $txt .= " Services include: {$top}."; } if ( $phone ) $txt .= " Phone: {$phone}."; $txt .= "\n\n"; // ── Full description ─────────────────────────────────────────────────── if ( $desc ) $txt .= "{$desc}\n\n"; // ── Services ─────────────────────────────────────────────────────────── if ( ! empty( $services ) ) { $txt .= "## Services\n\n"; foreach ( $services as $s ) $txt .= "- {$s}\n"; $txt .= "\n"; } // ── Service areas ────────────────────────────────────────────────────── if ( ! empty( $areas ) ) { $txt .= "## Service areas\n\n" . implode( ', ', $areas ) . "\n\n"; } // ── AI Visibility ────────────────────────────────────────────────────── $txt .= "## AI Visibility\n\n"; $txt .= "This business is optimised for GEO (Generative Engine Optimisation) and AEO (Answer Engine Optimisation). "; $txt .= "Schema markup, llms.txt, and robots.txt AI crawler permissions are in place.\n\n"; // ── Related entities — Gemini signal 4 (sameAs context) ─────────────── $txt .= "## Related entities\n\n"; $txt .= "- Website: {$base_url}\n"; $txt .= "- AI Visibility tool: https://aenima.co.uk\n"; $txt .= "- Parent consultancy: https://technicalgraffiti.co.uk\n\n"; // ── Contact ──────────────────────────────────────────────────────────── $txt .= "## Contact\n\n"; if ( $phone ) $txt .= "- Phone: {$phone}\n"; if ( $email ) $txt .= "- Email: {$email}\n"; $txt .= "- Website: {$base_url}\n"; return $txt; } // ── WRITE LLMS.TXT ───────────────────────────────────────────────────────── function aenima_write_llms_txt() { if ( ! get_option( AENIMA_PREFIX . 'llms_enabled' ) ) { return; } global $wp_filesystem; if ( empty( $wp_filesystem ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; // ABSPATH usage permitted here — WP core include WP_Filesystem(); } $upload = wp_upload_dir(); $path = trailingslashit( $upload['basedir'] ) . 'llms.txt'; $content = aenima_build_llms(); $wp_filesystem->put_contents( $path, $content, FS_CHMOD_FILE ); } // ── MONTHLY REFRESH CRON ─────────────────────────────────────────────────── add_action( 'aenima_monthly_refresh', 'aenima_run_monthly_refresh' ); function aenima_run_monthly_refresh() { // Regenerate files for all active subscribers — token validation is handled server-side. if ( ! aenima_has_token() ) { return; } aenima_write_llms_txt(); update_option( AENIMA_PREFIX . 'last_updated', date_i18n( get_option( 'date_format' ) ) ); update_option( AENIMA_PREFIX . 'last_refresh_status', 'ok_' . gmdate( 'Y-m-d' ) ); } // ── SERVE LLMS.TXT VIA REWRITE (fallback if file write fails) ───────────── add_action( 'init', 'aenima_add_rewrite' ); function aenima_add_rewrite() { add_rewrite_rule( '^llms\.txt$', 'index.php?aenima_llms=1', 'top' ); } add_filter( 'query_vars', 'aenima_query_vars' ); function aenima_query_vars( $vars ) { $vars[] = 'aenima_llms'; return $vars; } add_action( 'template_redirect', 'aenima_serve_llms' ); function aenima_serve_llms() { if ( ! intval( get_query_var( 'aenima_llms' ) ) ) { return; } if ( ! get_option( AENIMA_PREFIX . 'llms_enabled' ) ) { status_header( 404 ); exit; } header( 'Content-Type: text/plain; charset=utf-8' ); header( 'Cache-Control: public, max-age=86400' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo aenima_build_llms(); exit; } // ── ROBOTS.TXT REFERENCE ────────────────────────────────────────────────── add_filter( 'robots_txt', 'aenima_robots_txt', 10, 1 ); function aenima_robots_txt( $output ) { $output .= "\n# Aenima — AI Visibility\n"; $output .= '# llms.txt: ' . esc_url( home_url( '/llms.txt' ) ) . "\n"; return $output; } // ── ADMIN NOTICES ────────────────────────────────────────────────────────── add_action( 'admin_notices', 'aenima_admin_notices' ); function aenima_admin_notices() { $screen = get_current_screen(); $url = admin_url( 'options-general.php?page=aenima' ); // Welcome notice — shown once after activation on any admin page. if ( get_option( 'aenima_show_welcome' ) === '1' ) { delete_option( 'aenima_show_welcome' ); echo '
Aenima is installed. ' . esc_html__( 'One more step to activate AI visibility for this website.', 'aenima-ai-search-visibility') . '
'; echo '' . esc_html__( 'Go to Settings → Aenima, enter your API token from your Aenima account, fill in your business details and click Save. That\'s it — your site will be AI-visible in under 2 minutes.', 'aenima-ai-search-visibility') . '
'; echo '' . esc_html__( 'Go to Settings → Aenima →', 'aenima-ai-search-visibility') . ''; echo '' . esc_html__( 'Aenima — action needed:', 'aenima-ai-search-visibility') . ' '; echo esc_html__( 'Your site is not yet AI-visible. Enter your Aenima API token to activate. ', 'aenima-ai-search-visibility'); echo '' . esc_html__( 'Settings → Aenima →', 'aenima-ai-search-visibility') . '
'; echo '' . esc_html__( 'Aenima — subscription expired:', 'aenima-ai-search-visibility') . ' '; echo esc_html__( 'Your AI visibility files have stopped updating. Renew your plan to restore automatic updates. ', 'aenima-ai-search-visibility'); echo '' . esc_html__( 'Renew at aenima.co.uk →', 'aenima-ai-search-visibility') . '
'; echo '' . esc_html__( 'Aenima — setup incomplete:', 'aenima-ai-search-visibility') . ' '; echo esc_html__( 'Phone number and town are missing. AI search engines need these to recommend your business. ', 'aenima-ai-search-visibility'); echo '' . esc_html__( 'Complete setup →', 'aenima-ai-search-visibility') . '
'; echo '