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(
'
',
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'] );
?>
__( '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
);
?>
__( 'I couldn\'t get it to work', 'cookieframe' ),
'theme_plugin_conflict' => __( 'It conflicted with my theme or another plugin', 'cookieframe' ),
'found_alternative' => __( 'I found a better alternative', 'cookieframe' ),
'missing_features' => __( 'Missing features I needed', 'cookieframe' ),
'temporary' => __( 'I\'m only deactivating temporarily', 'cookieframe' ),
'other' => __( 'Other', 'cookieframe' ),
);
foreach ( $reasons as $value => $label ) :
?>