wp_create_nonce(self::NONCE_ACTION), 'ajaxUrl' => admin_url('admin-ajax.php'), ]); } /* ── Actions (form submissions) ───────────────────────────── */ public function handle_actions(): void { if (!isset($_POST['itk_action']) || !check_admin_referer(self::NONCE_ACTION)) return; if (!current_user_can('manage_options')) wp_die('Unauthorized'); switch ($_POST['itk_action']) { case 'clear_bot_log': ITK_Database::clear_bot_log(); $this->redirect(['tab' => 'bot-logs', 'cleared' => 1]); break; case 'clear_honeypot_log': ITK_Database::clear_honeypot_log(); $this->redirect(['tab' => 'honeypot-logs', 'cleared' => 1]); break; case 'save_settings_security': $this->save_settings_form('itk_security', [ 'response_code', 'redirect_url', 'custom_message', ], [ 'log_blocked_attempts', ]); $this->redirect(['tab' => 'bot-blocker', 'saved' => 1]); break; case 'save_settings_login': $this->save_settings_form('itk_security', [ 'custom_login_slug', ], [ 'enable_custom_login', ]); $this->redirect(['tab' => 'protection', 'saved' => 1]); break; case 'save_settings_honeypot': $this->save_settings_form('itk_honeypot', [ 'min_time', 'max_time', 'retain_days', ], []); $this->redirect(['tab' => 'honeypot', 'saved' => 1]); break; } } /** * Save a subset of fields from $_POST['itk_*'] into a WP option. * $text_fields = scalar fields (sanitize_text_field) * $toggle_fields = checkbox fields (0 or 1) */ private function save_settings_form(string $option, array $text_fields, array $toggle_fields): void { $opts = get_option($option, []); $posted = $_POST[$option] ?? []; foreach ($text_fields as $key) { if (isset($posted[$key])) { $opts[$key] = sanitize_text_field(wp_unslash($posted[$key])); } } foreach ($toggle_fields as $key) { $opts[$key] = !empty($posted[$key]) ? 1 : 0; } update_option($option, $opts); } private function redirect(array $args): void { wp_redirect(add_query_arg(array_merge(['page' => self::MENU_SLUG], $args), admin_url('options-general.php'))); exit; } /* ── AJAX: save single toggle setting ─────────────────────── */ public function ajax_save_setting(): void { check_ajax_referer(self::NONCE_ACTION, 'nonce'); if (!current_user_can('manage_options')) wp_send_json_error('unauthorized'); $option = sanitize_key($_POST['option'] ?? ''); $setting = sanitize_key($_POST['setting'] ?? ''); $value = (int)($_POST['value'] ?? 0); $allowed = ['itk_security','itk_optimization','itk_honeypot']; if (!in_array($option, $allowed, true) || empty($setting)) { wp_send_json_error('invalid'); } $opts = get_option($option, []); $opts[$setting] = $value; update_option($option, $opts); wp_send_json_success(); } /* ── AJAX: save config file ───────────────────────────────── */ public function ajax_save_config_file(): void { check_ajax_referer(self::NONCE_ACTION, 'nonce'); if (!current_user_can('manage_options')) wp_send_json_error('unauthorized'); $file = sanitize_key($_POST['file'] ?? ''); $content = wp_unslash($_POST['content'] ?? ''); $allowed = [ 'badbots' => ITK_PATH . 'config/badbots.conf', 'goodbots' => ITK_PATH . 'config/goodbots.conf', 'referrers' => ITK_PATH . 'config/referrers.conf', 'networks' => ITK_PATH . 'config/networks.conf', 'allowed-ips'=> ITK_PATH . 'config/allowed-ips.conf', ]; if (!isset($allowed[$file])) wp_send_json_error('invalid file'); $result = file_put_contents($allowed[$file], sanitize_textarea_field($content)); if ($result === false) { wp_send_json_error('write failed'); } // Clear transient caches delete_transient('itk_bots_list'); delete_transient('itk_referrers_list'); delete_transient('itk_networks_list'); delete_transient('itk_goodbots_list'); wp_send_json_success(); } /* ── Main page render ─────────────────────────────────────── */ public function render_page(): void { if (!current_user_can('manage_options')) return; $tab = sanitize_key($_GET['tab'] ?? 'dashboard'); ?>

InformatiQ Toolkit

Logs cleared successfully.

Settings saved.

$this->tab_dashboard(), 'bot-blocker' => $this->tab_bot_blocker(), 'protection' => $this->tab_protection(), 'optimization' => $this->tab_optimization(), 'honeypot' => $this->tab_honeypot(), 'bot-logs' => $this->tab_bot_logs(), 'honeypot-logs' => $this->tab_honeypot_logs(), 'config-files' => $this->tab_config_files(), default => $this->tab_dashboard(), }; ?>
■ BOT ACTIVITY MONITOR LIVE
Total Blocked
Today
Rate Limited
Honeypot Catches
Honeypot Today
TOP THREAT SOURCES
cnt); foreach ($bot_stats['top_bot_types'] as $row): $pct = round(($row->cnt / $max) * 100); ?>
bot_type ?: 'Unknown') ?>
cnt) ?>
HONEYPOT – TOP TARGETED FORMS
cnt); foreach ($hp_stats['top_forms'] as $row): $pct = round(($row->cnt / $max) * 100); ?>
form_type) ?>
cnt) ?>
TOP OFFENDER IPs
IP AddressHits
ip_address) ?>  [lookup] cnt) ?>
ACTIVE MODULES
!empty($sec['block_malicious_bots']), 'OpenAI Block' => !empty($sec['block_openai_bots']), 'Good Bot Rate Limit'=> !empty($sec['rate_limit_good_bots']), 'Network Block' => !empty($sec['block_bad_networks']), 'Login Protection' => !empty($sec['protect_wp_login']), 'Security Headers' => !empty($sec['add_security_headers']), 'Block XML-RPC' => !empty($sec['block_xmlrpc']), 'Honeypot' => !empty($hp['enabled']), 'Remove WP Version' => !empty($opt['remove_wp_version']), 'Disable Emoji' => !empty($opt['remove_emoji']), 'Admin Branding' => !empty($opt['admin_branding']), ]; foreach ($modules as $label => $active): ?>

Bot Blocking

['OpenAI / GPT Bots', 'Block GPTBot, ChatGPT-User, OAI-SearchBot'], 'block_malicious_bots' => ['Malicious Bots', 'Block bots listed in badbots.conf'], 'block_bad_referrers' => ['Bad Referrers', 'Block requests from spam referrer domains'], 'block_bad_networks' => ['Bad Networks', 'Block IP ranges listed in networks.conf'], 'rate_limit_good_bots' => ['Rate-Limit Good Bots', 'Apply crawl-rate limits to Googlebot, Bingbot, etc. (configurable in goodbots.conf)'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_security', $key, $label, $desc, $opts); endforeach; ?>

Response Settings

Response Code
Redirect URL
Custom Message
Log Blocked Attempts

WordPress Protection

['WP Login IP Whitelist', 'Restrict wp-login.php to IPs in allowed-ips.conf'], 'add_security_headers' => ['Security Headers', 'Add X-Content-Type-Options, X-Frame-Options, X-XSS-Protection headers'], 'protect_wp_includes' => ['Protect WP Core Files', 'Block direct access to wp-includes, wp-admin/includes'], 'protect_uploads' => ['Block PHP in Uploads', 'Deny PHP file access and uploads in /wp-content/uploads/'], 'block_xmlrpc' => ['Block XML-RPC', 'Deny all access to xmlrpc.php'], 'block_malicious_queries' => ['Block Malicious Queries', 'Detect and block SQLi, XSS, and command injection in query strings'], 'block_author_scans' => ['Block Author Scans', 'Redirect ?author=N requests to prevent username enumeration'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_security', $key, $label, $desc, $opts); endforeach; ?>

Custom Login URL

render_toggle('itk_security', 'enable_custom_login', 'Enable Custom Login URL', 'Replace /wp-login.php with a custom slug', $opts); ?>
Login Slug

Characters: letters, numbers, dashes only.

Performance & Cleanup

['Remove WP Version', 'Strip WordPress version from and all enqueued assets'], 'remove_script_versions' => ['Remove Asset Versions', 'Remove ?ver= query string from CSS/JS URLs'], 'remove_emoji' => ['Remove Emojis', 'Disable WordPress emoji scripts and styles'], 'deregister_wp_embed' => ['Remove WP Embed', 'Deregister the wp-embed script'], 'remove_wp_head_noise' => ['Clean WP Head', 'Remove RSD, wlwmanifest, feed links, and adjacent post links from '], 'limit_revisions' => ['Limit Revisions', 'Keep only 3 post revisions and autosave every 5 minutes'], 'defer_js' => ['Defer JavaScript', 'Add defer attribute to non-critical scripts'], 'limit_heartbeat' => ['Limit Heartbeat', 'Restrict WordPress heartbeat to post editor pages only'], 'stop_empty_search_redirect'=> ['Fix Empty Search', 'Prevent redirect loop on empty search queries'], 'use_google_jquery' => ['Use Google jQuery', 'Load jQuery from Google CDN instead of local'], 'dns_prefetch' => ['DNS Prefetch', 'Enable DNS prefetching via meta header'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_optimization', $key, $label, $desc, $opts); endforeach; ?>

Security Tweaks

['Hide Login Errors', 'Replace specific login error messages with a generic one'], 'remove_author_class' => ['Remove Author Class', 'Strip admin username from comment CSS classes'], 'remove_default_userfields'=> ['Remove User Fields', 'Remove AIM, Jabber, YIM from user profiles'], 'clean_bad_content' => ['Clean Bad Content', 'Remove empty tags, inline styles, and font tags on save'], 'change_author_base' => ['Change Author URL Base', "Change /author/ to /writer/ in author archive URLs"], 'disable_xml_rpc' => ['Disable XML-RPC (via filter)', 'Filter-based XML-RPC disable (additional to the blocker)'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_optimization', $key, $label, $desc, $opts); endforeach; ?>

UI / Branding

['Disable Core Widgets', 'Remove WordPress News and WPEngine news dashboard widgets'], 'unregister_default_widgets'=> ['Unregister Sidebar Widgets', 'Remove Calendar, Archives, Meta, Search, Tag Cloud widgets'], 'disable_comments_url' => ['Remove Comment URL Field', 'Hide the website URL field from comment forms'], 'remove_admin_bar_links' => ['Clean Admin Bar', 'Remove WordPress logo, links, and noisy items from the toolbar'], 'admin_branding' => ['InformatiQ Branding', 'Add InformatiQ logo widget, toolbar link, admin notice, and custom footer'], 'disable_floc' => ['Disable FLoC', 'Add Permissions-Policy: interest-cohort=() header'], 'lightbox_images' => ['Lightbox Images', 'Add rel="lightbox" to image links in post content'], 'featured_image_rss' => ['Featured Image in RSS', 'Include featured image in RSS feed entries'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_optimization', $key, $label, $desc, $opts); endforeach; ?>

Honeypot Fields

['Enable Honeypot', 'Inject invisible honeypot fields into all protected forms'], 'protect_comments' => ['Comments', 'Protect comment forms'], 'protect_login' => ['Login Form', 'Protect wp-login.php login'], 'protect_register' => ['Registration', 'Protect user registration'], 'protect_lost_password' => ['Lost Password', 'Protect lost password form'], 'protect_woocommerce' => ['WooCommerce', 'Protect WooCommerce checkout and registration'], 'protect_cf7' => ['Contact Form 7', 'Protect CF7 forms'], 'protect_elementor' => ['Elementor Forms', 'Protect Elementor Pro form widget'], 'protect_gravity' => ['Gravity Forms', 'Protect Gravity Forms'], 'protect_search' => ['Search Form', 'Protect the WordPress search form'], ]; foreach ($toggles as $key => [$label, $desc]): $this->render_toggle('itk_honeypot', $key, $label, $desc, $opts); endforeach; ?>

Timing Rules

Minimum Submit Time (seconds)

Block submissions faster than this (bots submit instantly).

Maximum Submit Time (seconds)

Block submissions older than this (stale/replayed forms).

Retain Logs (days)

Automatically prune logs older than this many days.

self::PER_PAGE, 'offset' => $offset]; if ($search) $args['search'] = $search; if ($filter_ip) $args['ip'] = $filter_ip; if ($filter_bt) $args['bot_type'] = $filter_bt; if ($filter_ac) $args['action'] = $filter_ac; $rows = ITK_Database::get_bot_rows($args); $total = ITK_Database::count_bot_rows($args); $bot_types = ITK_Database::get_bot_types(); $total_pages= max(1, (int)ceil($total / self::PER_PAGE)); $base_url = admin_url('options-general.php?page=' . self::MENU_SLUG . '&tab=bot-logs'); ?>
Reset

Showing of result(s)

action === 'rate_limited' ? 'itk-badge-warn' : 'itk-badge-block'; ?>
Date / TimeIPBot Type ActionReasonURIUser Agent
No bot activity logged yet.
logged_at) ?> ip_address) ?> [filter] [lookup] bot_type) ?> action) ?> reason) ?> request_uri, 0, 80)) ?> user_agent, 0, 100)) ?>
render_pager($paged, $total_pages, $base_url); ?>
self::PER_PAGE, 'offset' => $offset]; if ($search) $args['search'] = $search; if ($filter_ip) $args['ip'] = $filter_ip; if ($filter_form) $args['form'] = $filter_form; $rows = ITK_Database::get_honeypot_rows($args); $total = ITK_Database::count_honeypot_rows($args); $form_types = ITK_Database::get_honeypot_form_types(); $total_pages = max(1, (int)ceil($total / self::PER_PAGE)); $base_url = admin_url('options-general.php?page=' . self::MENU_SLUG . '&tab=honeypot-logs'); ?>
Reset

Showing of result(s)

Date / TimeIPForm Type ReasonURIUser Agent
No honeypot catches yet.
blocked_at) ?> ip_address) ?> [filter] [lookup] form_type) ?> reason) ?> request_uri, 0, 80)) ?> user_agent, 0, 100)) ?>
render_pager($paged, $total_pages, $base_url); ?>
['Bad Bots', 'config/badbots.conf', 'One bot user-agent substring per line. Lines starting with # are comments.'], 'goodbots' => ['Good Bots', 'config/goodbots.conf', 'Format: BotName|rate_per_minute (0 = always block)'], 'referrers' => ['Bad Referrers', 'config/referrers.conf', 'One domain substring per line.'], 'networks' => ['Bad Networks', 'config/networks.conf', 'One IP or CIDR range per line (e.g. 1.2.3.0/24).'], 'allowed-ips' => ['Allowed IPs', 'config/allowed-ips.conf','IPs/CIDRs allowed to access wp-login.php (one per line).'], ]; $active_file = sanitize_key($_GET['file'] ?? 'badbots'); if (!isset($files[$active_file])) $active_file = 'badbots'; [$title, $path, $desc] = $files[$active_file]; $full_path = ITK_PATH . $path; $content = file_exists($full_path) ? file_get_contents($full_path) : ''; ?>
[$label]): ?>

'; if ($paged > 1) { echo '« Prev'; } echo '' . sprintf('Page %d of %d', $paged, $total_pages) . ''; if ($paged < $total_pages) { echo 'Next »'; } echo ''; } }