'integer', 'sanitize_callback' => 'absint', 'default' => 1, ]); // Label-Stil: 'custom' = eigenes Badge mit EU-Basis-Piktogramm (Default, // damit bestehende Seiten sich beim Update nicht veraendern), // 'official' = vollstaendiges EU-Label der Kommission 1:1. register_setting('aiactify_settings_group', 'aiactify_label_style', [ 'type' => 'string', 'sanitize_callback' => static function ($value) { return in_array($value, ['custom', 'official'], true) ? $value : 'custom'; }, 'default' => 'custom', ]); register_setting('aiactify_settings_group', 'aiactify_chatbot_disclosure', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 0, ]); register_setting('aiactify_settings_group', 'aiactify_chatbot_text', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ]); // Optional platform connection register_setting('aiactify_settings_group', 'aiactify_site_id', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ]); register_setting('aiactify_settings_group', 'aiactify_api_key', [ 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => '', ]); register_setting('aiactify_settings_group', 'aiactify_is_active', [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 1, ]); } add_action('admin_init', 'aiactify_register_settings'); /** * Add Settings Menu */ function aiactify_add_admin_menu() { add_menu_page( __('AIActify Settings', 'aiactify-compliance'), 'AIActify', 'manage_options', 'aiactify-compliance', 'aiactify_settings_page', 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNiA0TDYgOHY4YzAgNS41IDQuMyAxMC43IDEwIDEyIDUuNy0xLjMgMTAtNi41IDEwLTEyVjhMMTYgNHogTTExLjMgMTUuM0wxNC41IDE4LjVMMjAuNyAxMi4zTDE5LjMgMTAuOUwxNC41IDE1LjdMMTIuNyAxMy45WiIgZmlsbD0iIzNFQ0Y4RSIvPjwvc3ZnPg==', 80 ); } add_action('admin_menu', 'aiactify_add_admin_menu'); /** * Enqueue Admin CSS */ function aiactify_admin_enqueue_scripts($hook) { if ($hook !== 'toplevel_page_aiactify-compliance') { return; } wp_enqueue_style('aiactify-admin-style', AIACTIFY_PLUGIN_URL . 'assets/admin-style.css', array(), AIACTIFY_VERSION); wp_enqueue_script('aiactify-admin-script', AIACTIFY_PLUGIN_URL . 'assets/admin-script.js', array('jquery'), AIACTIFY_VERSION, true); // Pass AJAX URL to script wp_localize_script('aiactify-admin-script', 'aiactify_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aiactify_editor_nonce') )); } add_action('admin_enqueue_scripts', 'aiactify_admin_enqueue_scripts'); /** * Handle AJAX request to generate editor token and get redirect URL */ function aiactify_ajax_open_editor() { check_ajax_referer('aiactify_editor_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Unauthorized', 'aiactify-compliance')); } $site_id = get_option('aiactify_site_id'); $api_key = get_option('aiactify_api_key'); if (empty($site_id) || empty($api_key)) { wp_send_json_error(__('API Key and Site Key must be configured and saved first.', 'aiactify-compliance')); } // Call AIActify Backend $response = wp_remote_post(AIACTIFY_API_BASE . '/auth/onsite-editor-token', array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ), 'body' => wp_json_encode(array( 'siteId' => $site_id )), 'timeout' => 15 )); if (is_wp_error($response)) { wp_send_json_error(__('Failed to connect to AIActify servers.', 'aiactify-compliance')); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); $status_code = wp_remote_retrieve_response_code($response); if ($status_code !== 200 || empty($data['editorToken'])) { wp_send_json_error(isset($data['error']) ? $data['error'] : __('Invalid API Key or Site Key.', 'aiactify-compliance')); } // Success! Generate frontend redirect URL // The snippet expects ?aiactify_edit=TOKEN (single parameter format) $frontend_url = home_url('/?aiactify_edit=' . urlencode($data['editorToken'])); wp_send_json_success(array('url' => $frontend_url)); } add_action('wp_ajax_aiactify_open_editor', 'aiactify_ajax_open_editor'); /** * Render Settings Page */ function aiactify_settings_page() { if (!current_user_can('manage_options')) { return; } $local_labeling = get_option('aiactify_local_labeling', 1); $chatbot_disclosure = get_option('aiactify_chatbot_disclosure', 0); $chatbot_text = get_option('aiactify_chatbot_text', ''); $site_id = get_option('aiactify_site_id', ''); $api_key = get_option('aiactify_api_key', ''); $is_active = get_option('aiactify_is_active', 1); // default active ?>
%s %s %s
', esc_html__('AIActify: labelling is not active.', 'aiactify-compliance'), esc_html__('The configured Site Key is invalid, so no AI labels are being shown on your website. The Site Key has 16 characters (letters a–f and digits) and can be copied from the AIActify dashboard under your website → Integration.', 'aiactify-compliance'), esc_url(admin_url('admin.php?page=aiactify-compliance')), esc_html__('Open settings', 'aiactify-compliance') ); } add_action('admin_notices', 'aiactify_admin_notice_invalid_key'); /** * Serverseitige Pruefung: kennt die Plattform diesen Site Key ueberhaupt? * * Die Formatpruefung oben faengt nur offensichtlichen Unsinn ab (E-Mail, UUID). * Ein Key, der formal stimmt, aber falsch ist — aus einer anderen Website * kopiert, Site im Dashboard geloescht, Staging-Key auf der Produktivseite, * Tippfehler innerhalb von a–f — sieht korrekt aus und liefert trotzdem * KEINE einzige Kennzeichnung. Sichtbar war das bisher ausschliesslich als * console.error in den Entwicklertools des Besuchers. Genau diese Fehlerklasse * hat eine echte Kundeninstallation vier Tage lang unbemerkt lahmgelegt. * * Geprueft wird beim Speichern der Einstellungen und danach einmal taeglich. * Das Ergebnis liegt in einer Option, damit die Anzeige keinen Request kostet. */ function aiactify_verify_site_key($site_id = null) { if (null === $site_id) { $site_id = get_option('aiactify_site_id'); } if (empty($site_id) || !preg_match('/^[0-9a-f]{16}$/i', $site_id)) { delete_option('aiactify_site_key_check'); return; } $response = wp_remote_get( AIACTIFY_API_BASE . '/sites/config/' . rawurlencode($site_id), array('timeout' => 5) ); if (is_wp_error($response)) { // Netzwerkfehler ist KEIN Beweis fuer einen falschen Key — nicht als // Fehler melden, sonst warnen wir bei jedem Aussetzer grundlos. $state = 'unreachable'; } else { $code = (int) wp_remote_retrieve_response_code($response); $state = (404 === $code) ? 'unknown' : (($code >= 200 && $code < 300) ? 'ok' : 'unreachable'); } update_option('aiactify_site_key_check', array( 'state' => $state, 'site_id' => $site_id, 'time' => time(), ), false); } // Beim Speichern der Einstellungen pruefen (auch wenn der Wert gleich bleibt). add_action('update_option_aiactify_site_id', function ($old, $new) { aiactify_verify_site_key($new); }, 10, 2); add_action('add_option_aiactify_site_id', function ($option, $value) { aiactify_verify_site_key($value); }, 10, 2); // Taeglich nachpruefen — eine geloeschte Site faellt sonst nie auf. add_action('aiactify_daily_key_check', 'aiactify_verify_site_key'); register_activation_hook(__FILE__, function () { if (!wp_next_scheduled('aiactify_daily_key_check')) { wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'aiactify_daily_key_check'); } }); register_deactivation_hook(__FILE__, function () { wp_clear_scheduled_hook('aiactify_daily_key_check'); }); /** * Hinweis, wenn die Plattform den (formal gueltigen) Site Key nicht kennt. */ function aiactify_admin_notice_unknown_key() { if (!current_user_can('manage_options')) { return; } $check = get_option('aiactify_site_key_check'); if (empty($check['state']) || 'unknown' !== $check['state']) { return; } // Nur melden, wenn die Pruefung zum aktuell gespeicherten Key gehoert. if (!isset($check['site_id']) || $check['site_id'] !== get_option('aiactify_site_id')) { return; } printf( '%s %s %s