WP_REST_Server::CREATABLE, 'callback' => 'harvify_cookie_rest_scan', 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); register_rest_route( 'harvify-cookie/v1', '/stats', array( 'methods' => WP_REST_Server::READABLE, 'callback' => 'harvify_cookie_rest_stats', 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); } } if ( ! function_exists( 'harvify_cookie_rest_scan' ) ) { /** * Categorise detected + inferred cookies. * * @param WP_REST_Request $request Request. * @return WP_REST_Response */ function harvify_cookie_rest_scan( $request ) { $params = $request->get_json_params(); $detected = isset( $params['cookies'] ) && is_array( $params['cookies'] ) ? $params['cookies'] : array(); $known = array(); $unknown = array(); foreach ( $detected as $raw_name ) { $name = sanitize_text_field( (string) $raw_name ); if ( '' === $name ) { continue; } $match = harvify_cookie_match_cookie( $name ); if ( $match ) { $known[ $match['name'] ] = $match; } else { $unknown[ $name ] = array( 'name' => $name, 'provider' => '', 'purpose' => '', 'category' => 'necessary', 'expiry' => '', ); } } // Merge environment-inferred cookies the browser may not have surfaced. foreach ( harvify_cookie_inferred_cookies() as $inf ) { $known[ $inf['name'] ] = $inf; } return rest_ensure_response( array( 'success' => true, 'known' => array_values( $known ), 'unknown' => array_values( $unknown ), ) ); } } if ( ! function_exists( 'harvify_cookie_rest_stats' ) ) { /** * Aggregate the consent log into headline statistics. * * @return WP_REST_Response */ function harvify_cookie_rest_stats() { global $wpdb; $table = $wpdb->prefix . 'harvify_consent_log'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ); if ( $exists !== $table ) { return rest_ensure_response( array( 'success' => true, 'available' => false, ) ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $by_type = $wpdb->get_results( "SELECT consent_type, COUNT(*) AS cnt FROM {$table} GROUP BY consent_type", ARRAY_A ); $types = array( 'full' => 0, 'partial' => 0, 'rejected' => 0, ); foreach ( (array) $by_type as $row ) { $t = $row['consent_type']; if ( isset( $types[ $t ] ) ) { $types[ $t ] = (int) $row['cnt']; } } // Per-category opt-ins (categories column is a CSV of granted categories). $cat_counts = array( 'analytics' => 0, 'marketing' => 0, 'preferences' => 0, ); foreach ( array_keys( $cat_counts ) as $cat ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $cat_counts[ $cat ] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE FIND_IN_SET(%s, categories)", $cat ) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from $wpdb->prefix. ); } $rate = function ( $n ) use ( $total ) { return $total > 0 ? round( $n / $total * 100, 1 ) : 0.0; }; return rest_ensure_response( array( 'success' => true, 'available' => true, 'total' => $total, 'types' => $types, 'rates' => array( 'full' => $rate( $types['full'] ), 'partial' => $rate( $types['partial'] ), 'rejected' => $rate( $types['rejected'] ), 'accepted' => $rate( $types['full'] + $types['partial'] ), ), 'categories' => array( 'analytics' => array( 'count' => $cat_counts['analytics'], 'rate' => $rate( $cat_counts['analytics'] ), ), 'marketing' => array( 'count' => $cat_counts['marketing'], 'rate' => $rate( $cat_counts['marketing'] ), ), 'preferences' => array( 'count' => $cat_counts['preferences'], 'rate' => $rate( $cat_counts['preferences'] ), ), ), ) ); } }