array( $this, 'sanitize_options' ) ) ); } /** * Redirect to settings page after first activation. */ public function maybe_redirect_after_activation() { if ( ! get_transient( 'cookieframe_activation_redirect' ) ) { return; } delete_transient( 'cookieframe_activation_redirect' ); if ( wp_doing_ajax() || isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } wp_safe_redirect( admin_url( 'options-general.php?page=cookieframe' ) ); exit; } /** * Resolve stored excluded post IDs to title/type objects for JS initialization. * * @return array */ private function get_excluded_posts_data() { $options = CookieFrame::get_options(); if ( empty( $options['excluded_posts'] ) ) { return array(); } $ids = array_filter( array_map( 'intval', explode( ',', $options['excluded_posts'] ) ) ); $data = array(); foreach ( $ids as $id ) { $post = get_post( $id ); if ( $post ) { $data[] = array( 'id' => $id, 'title' => html_entity_decode( get_the_title( $id ), ENT_QUOTES, 'UTF-8' ), 'type' => $post->post_type, ); } } return $data; } /** * AJAX: Search published posts and pages by title. */ public function ajax_search_posts() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( null, 403 ); return; } $search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : ''; if ( strlen( $search ) < 2 ) { wp_send_json_success( array() ); return; } $posts = get_posts( array( 'post_type' => array( 'post', 'page' ), 'post_status' => 'publish', 's' => $search, 'posts_per_page' => 10, 'fields' => 'ids', ) ); $results = array_map( function( $id ) { return array( 'id' => $id, 'title' => html_entity_decode( get_the_title( $id ), ENT_QUOTES, 'UTF-8' ), 'type' => get_post_type( $id ), ); }, $posts ); wp_send_json_success( array_values( $results ) ); } /** * Enqueue admin JS and CSS only on our settings page. * * @param string $hook_suffix The current admin page hook. */ public function enqueue_assets( $hook_suffix ) { if ( 'settings_page_cookieframe' !== $hook_suffix ) { return; } wp_enqueue_script( 'cookieframe-admin', COOKIEFRAME_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery' ), COOKIEFRAME_VERSION, true ); wp_localize_script( 'cookieframe-admin', 'cookieframeAdmin', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'cookieframe_admin_nonce' ), 'isConnected' => ! empty( CookieFrame::get_options()['domain_id'] ), 'hasApiKey' => ! empty( CookieFrame::get_api_key() ), 'excludedPosts' => $this->get_excluded_posts_data(), 'i18n' => array( 'validating' => __( 'Validating...', 'cookieframe' ), 'registering' => __( 'Creating account...', 'cookieframe' ), 'invalidId' => __( 'Please enter a valid 8-character Domain ID.', 'cookieframe' ), 'fullNameRequired' => __( 'Please enter your full name.', 'cookieframe' ), 'invalidEmail' => __( 'Please enter a valid email address.', 'cookieframe' ), 'connected' => __( 'Successfully connected!', 'cookieframe' ), 'disconnectConfirm' => __( 'Are you sure you want to disconnect this domain?', 'cookieframe' ), 'validate' => __( 'Connect', 'cookieframe' ), 'register' => __( 'Create Account & Connect', 'cookieframe' ), 'error' => __( 'Something went wrong. Please try again.', 'cookieframe' ), 'cookies' => __( 'Cookies', 'cookieframe' ), 'consents' => __( 'Consents', 'cookieframe' ), 'lastScan' => __( 'Last Scan', 'cookieframe' ), 'noData' => __( 'No data yet', 'cookieframe' ), 'categories' => __( 'Categories', 'cookieframe' ), 'uncategorized' => __( 'uncategorized', 'cookieframe' ), 'installed' => __( 'Connected', 'cookieframe' ), 'notInstalled' => __( 'Not installed', 'cookieframe' ), 'unreachable' => __( 'Unreachable', 'cookieframe' ), 'pendingVerification' => __( 'Pending', 'cookieframe' ), 'installationError' => __( 'Error', 'cookieframe' ), 'bannerActive' => __( 'Active', 'cookieframe' ), 'bannerDisabled' => __( 'Disabled', 'cookieframe' ), 'resendingEmail' => __( 'Sending...', 'cookieframe' ), 'resendEmail' => __( 'Resend Email', 'cookieframe' ), 'verificationSent' => __( 'Verification email sent! Check your inbox.', 'cookieframe' ), 'verifyEmailPrompt' => __( 'Check your inbox to verify your email address.', 'cookieframe' ), ), ) ); wp_enqueue_style( 'cookieframe-admin', COOKIEFRAME_PLUGIN_URL . 'assets/css/admin.css', array(), COOKIEFRAME_VERSION ); } /** * Show admin notice when no Domain ID is configured. */ public function show_setup_notice() { $options = CookieFrame::get_options(); if ( ! empty( $options['domain_id'] ) ) { return; } $screen = get_current_screen(); if ( $screen && 'settings_page_cookieframe' === $screen->id ) { return; } printf( '

%s %s

', esc_html__( 'Just one more step to make your website compliant!', 'cookieframe' ), esc_url( admin_url( 'options-general.php?page=cookieframe' ) ), esc_html__( 'Complete setup to activate your cookie consent banner.', 'cookieframe' ) ); } /** * Determine whether the review reminder should be shown. * * @return bool */ private function should_show_review_notice() { $options = CookieFrame::get_options(); if ( empty( $options['domain_id'] ) ) { return false; } $review = get_option( 'cookieframe_review_notice' ); if ( ! is_array( $review ) || empty( $review['activated_at'] ) ) { // Existing installations that connected before this option was introduced: // initialize it now so the 14-day timer starts from today. $review = array( 'activated_at' => time(), 'status' => 'pending', ); update_option( 'cookieframe_review_notice', $review, false ); return false; // Not ready yet — will show after 14 days. } $status = isset( $review['status'] ) ? $review['status'] : 'pending'; if ( 'dismissed' === $status ) { return false; } // Must be active for at least 14 days. $days_active = ( time() - (int) $review['activated_at'] ) / DAY_IN_SECONDS; if ( $days_active < 14 ) { return false; } if ( 'snoozed' === $status ) { $snoozed_at = isset( $review['snoozed_at'] ) ? (int) $review['snoozed_at'] : 0; $days_since_snooze = ( time() - $snoozed_at ) / DAY_IN_SECONDS; if ( $days_since_snooze < 30 ) { return false; } } return true; } /** * Render the review reminder card HTML. */ private function render_review_notice() { if ( ! $this->should_show_review_notice() ) { return; } ?>

__( 'Permission denied.', 'cookieframe' ) ) ); } $domain_id = isset( $_POST['domain_id'] ) ? strtolower( sanitize_text_field( wp_unslash( $_POST['domain_id'] ) ) ) : ''; $is_short_id = (bool) preg_match( '/^[0-9a-f]{8}$/', $domain_id ); $is_full_uuid = (bool) preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/', $domain_id ); if ( ! $is_short_id && ! $is_full_uuid ) { wp_send_json_error( array( 'message' => __( 'Invalid Domain ID format.', 'cookieframe' ) ) ); } $response = wp_remote_get( COOKIEFRAME_API_URL . '/api/integrations/wordpress/validate/' . $domain_id, array( 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 200 !== $code || empty( $body['domain'] ) ) { $error = isset( $body['error'] ) ? $body['error'] : __( 'Domain not found. Please check your Domain ID.', 'cookieframe' ); wp_send_json_error( array( 'message' => $error ) ); } $options = CookieFrame::get_options(); $options['domain_id'] = $domain_id; $options['domain_name'] = sanitize_text_field( $body['domain'] ); update_option( CookieFrame::OPTION_NAME, $options ); wp_send_json_success( array( 'domain_name' => $options['domain_name'] ) ); } /** * AJAX: Register a new account via the CookieFrame WordPress integration API. */ public function ajax_register() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cookieframe' ) ) ); } $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; $email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : ''; $site_url = isset( $_POST['site_url'] ) ? esc_url_raw( wp_unslash( $_POST['site_url'] ) ) : ''; if ( ! is_email( $email ) ) { wp_send_json_error( array( 'message' => __( 'Please enter a valid email address.', 'cookieframe' ) ) ); } if ( empty( $site_url ) ) { wp_send_json_error( array( 'message' => __( 'Site URL is required.', 'cookieframe' ) ) ); } // Extract the domain from the site URL. $parsed = wp_parse_url( $site_url ); $domain = isset( $parsed['host'] ) ? $parsed['host'] : ''; $request_headers = array( 'Content-Type' => 'application/json' ); $body = array( 'email' => $email, 'domain' => $domain, 'siteUrl' => $site_url, 'returnUrl' => admin_url( 'options-general.php?page=cookieframe' ), ); if ( ! empty( $name ) ) { $body['name'] = $name; } $response = wp_remote_post( COOKIEFRAME_API_URL . '/api/integrations/wordpress/register', array( 'timeout' => 30, 'headers' => $request_headers, 'body' => wp_json_encode( $body ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( $code < 200 || $code >= 300 || empty( $body['domainId'] ) ) { $error = isset( $body['error'] ) ? $body['error'] : __( 'Registration failed. Please try again or create an account at cookieframe.com.', 'cookieframe' ); wp_send_json_error( array( 'message' => $error ) ); } $options = CookieFrame::get_options(); $options['domain_id'] = strtolower( sanitize_text_field( $body['domainId'] ) ); $options['domain_name'] = ! empty( $body['domain'] ) ? sanitize_text_field( $body['domain'] ) : $domain; if ( ! empty( $body['apiKey'] ) ) { $options['api_key'] = sanitize_text_field( $body['apiKey'] ); } update_option( CookieFrame::OPTION_NAME, $options ); wp_send_json_success( array( 'domain_id' => $options['domain_id'], 'domain_name' => $options['domain_name'], 'email_verified' => ! empty( $body['emailVerified'] ), ) ); } /** * AJAX: Fetch live domain status from the CookieFrame API. */ public function ajax_fetch_status() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cookieframe' ) ) ); } $options = CookieFrame::get_options(); $domain_id = $options['domain_id']; if ( empty( $domain_id ) ) { wp_send_json_error( array( 'message' => __( 'Not connected.', 'cookieframe' ) ) ); } $api_key = CookieFrame::get_api_key(); if ( empty( $api_key ) ) { wp_send_json_error( array( 'message' => __( 'No API key configured. Please re-register your domain.', 'cookieframe' ) ) ); } $response = wp_remote_get( COOKIEFRAME_API_URL . '/api/integrations/wordpress/status/' . $domain_id, array( 'timeout' => 10, 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 200 !== $code || ! is_array( $body ) ) { $error = isset( $body['error'] ) ? $body['error'] : __( 'Failed to fetch status.', 'cookieframe' ); wp_send_json_error( array( 'message' => $error ) ); } wp_send_json_success( $body ); } /** * AJAX: Resend the email verification email via the CookieFrame API. */ public function ajax_resend_verification() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cookieframe' ) ) ); } $options = CookieFrame::get_options(); $domain_id = $options['domain_id']; $api_key = CookieFrame::get_api_key(); if ( empty( $domain_id ) || empty( $api_key ) ) { wp_send_json_error( array( 'message' => __( 'Not connected.', 'cookieframe' ) ) ); } $response = wp_remote_post( COOKIEFRAME_API_URL . '/api/integrations/wordpress/resend-verification', array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ), 'body' => wp_json_encode( array( 'domainId' => $domain_id, ) ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( $code < 200 || $code >= 300 ) { $error = isset( $body['error'] ) ? $body['error'] : __( 'Failed to send verification email.', 'cookieframe' ); wp_send_json_error( array( 'message' => $error ) ); } wp_send_json_success(); } /** * AJAX: Disconnect the domain by clearing stored options. */ public function ajax_disconnect() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cookieframe' ) ) ); } $defaults = CookieFrame::get_defaults(); update_option( CookieFrame::OPTION_NAME, $defaults ); wp_send_json_success(); } /** * AJAX: Dismiss the review reminder notice. */ public function ajax_dismiss_review() { check_ajax_referer( 'cookieframe_admin_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cookieframe' ) ) ); } $dismiss_action = isset( $_POST['dismiss_action'] ) ? sanitize_text_field( wp_unslash( $_POST['dismiss_action'] ) ) : ''; $allowed = array( 'review', 'later', 'already' ); if ( ! in_array( $dismiss_action, $allowed, true ) ) { wp_send_json_error( array( 'message' => __( 'Invalid action.', 'cookieframe' ) ) ); } $review = get_option( 'cookieframe_review_notice', array() ); if ( 'later' === $dismiss_action ) { $review['status'] = 'snoozed'; $review['snoozed_at'] = time(); $review['snooze_count'] = isset( $review['snooze_count'] ) ? (int) $review['snooze_count'] + 1 : 1; // Auto-dismiss after second snooze. if ( $review['snooze_count'] >= 2 ) { $review['status'] = 'dismissed'; } } else { $review['status'] = 'dismissed'; } update_option( 'cookieframe_review_notice', $review ); wp_send_json_success(); } /** * Render the settings page. */ public function render_settings_page() { $options = CookieFrame::get_options(); $is_connected = ! empty( $options['domain_id'] ); ?>

CookieFrame v
render_review_notice(); ?>

—
—
—
—

' . esc_html__( 'dashboard', 'cookieframe' ) . '' ); ?>

first_name . ' ' . $current_user->last_name ); if ( empty( $prefill_name ) ) { $prefill_name = $current_user->display_name; } ?>

/> />

    [cookieframe-preferences]

    [cookieframe-preferences text="Cookie Settings" class="my-link"]' ); ?>


    [cookieframe-do-not-sell]

    [cookieframe-do-not-sell class="footer-link"]' ); ?>

    __( 'Permission denied.', 'cookieframe' ) ) ); } $valid_reasons = array( 'couldnt_get_it_to_work', 'theme_plugin_conflict', 'found_alternative', 'missing_features', 'temporary', 'other', ); $reason = isset( $_POST['reason'] ) ? sanitize_text_field( wp_unslash( $_POST['reason'] ) ) : ''; if ( ! in_array( $reason, $valid_reasons, true ) ) { wp_send_json_error( array( 'message' => __( 'Invalid reason.', 'cookieframe' ) ) ); } $details = isset( $_POST['details'] ) ? sanitize_textarea_field( wp_unslash( $_POST['details'] ) ) : ''; $options = CookieFrame::get_options(); $body = array( 'reason' => $reason, 'siteUrl' => site_url(), 'wpVersion' => get_bloginfo( 'version' ), 'pluginVersion' => COOKIEFRAME_VERSION, ); if ( ! empty( $details ) ) { $body['details'] = substr( $details, 0, 1000 ); } if ( ! empty( $options['domain_id'] ) ) { $body['domainId'] = $options['domain_id']; } $response = wp_remote_post( COOKIEFRAME_API_URL . '/api/integrations/wordpress/feedback', array( 'timeout' => 10, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( $body ), ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } wp_send_json_success(); } /** * Render deactivation feedback modal on the plugins page. */ public function render_deactivation_modal() { $deactivate_url = wp_nonce_url( admin_url( 'plugins.php?action=deactivate&plugin=' . rawurlencode( COOKIEFRAME_PLUGIN_BASENAME ) ), 'deactivate-plugin_' . COOKIEFRAME_PLUGIN_BASENAME ); ?>