prefix . 'agolegal_consents'; } public static function create_table(): void { global $wpdb; $table = self::table(); $table = self::table(); $charset = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, ip_address varchar(45) DEFAULT '', user_agent varchar(500) DEFAULT '', consent_data text DEFAULT '', entry_hash varchar(64) DEFAULT '', created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY entry_hash (entry_hash), KEY created_at (created_at) ) {$charset};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); update_option( 'agolegal_consents_db_version', self::DB_VERSION ); } /** SHA-256 of canonical (ip|ua|json|timestamp). Stored to verify integrity. */ public static function compute_hash( string $ip, string $ua, string $json, string $created_at ): string { return hash( 'sha256', $ip . '|' . $ua . '|' . $json . '|' . $created_at ); } public static function insert( array $data, string $ip, string $ua ): int { global $wpdb; $created = current_time( 'mysql' ); $json = wp_json_encode( $data ); $hash = self::compute_hash( $ip, substr( $ua, 0, 500 ), $json, $created ); $wpdb->insert( self::table(), [ 'ip_address' => $ip, 'user_agent' => substr( $ua, 0, 500 ), 'consent_data' => $json, 'entry_hash' => $hash, 'created_at' => $created, ] ); return (int) $wpdb->insert_id; } public static function get_list( int $page = 1, int $per_page = 20 ): array { global $wpdb; $table = self::table(); $table = self::table(); $table = self::table(); $offset = ( $page - 1 ) * $per_page; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table; query is safe. $total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table; query is safe. $items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d OFFSET %d", $per_page, $offset ) ); foreach ( $items as &$item ) { $item->consent_data = json_decode( $item->consent_data, true ); } return [ 'items' => $items, 'total' => $total, 'page' => $page, 'per_page' => $per_page, 'pages' => (int) ceil( $total / $per_page ), ]; } public static function export_csv(): string { global $wpdb; $table = self::table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table; query is safe. $items = $wpdb->get_results( "SELECT * FROM {$table} ORDER BY created_at DESC" ); $csv = "ID,IP,User Agent,Necessary,Analytics,Marketing,Functional,Date,Hash\n"; foreach ( $items as $item ) { $data = json_decode( $item->consent_data, true ) ?: []; $csv .= sprintf( "%d,%s,%s,%s,%s,%s,%s,%s,%s\n", $item->id, Csv::cell( (string) $item->ip_address ), Csv::cell( substr( (string) $item->user_agent, 0, 100 ) ), ! empty( $data['necessary'] ) ? 'yes' : 'no', ! empty( $data['analytics'] ) ? 'yes' : 'no', ! empty( $data['marketing'] ) ? 'yes' : 'no', ! empty( $data['functional'] ) ? 'yes' : 'no', $item->created_at, $item->entry_hash ); } return $csv; } /** Audit-ready HTML report with all hashes plus batch hash at the end. */ public static function export_html_report(): string { global $wpdb; $table = self::table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table; query is safe. $items = $wpdb->get_results( "SELECT * FROM {$table} ORDER BY created_at DESC" ); $batch_str = ''; foreach ( $items as $i ) $batch_str .= $i->entry_hash; $batch_hash = hash( 'sha256', $batch_str ); $site = esc_html( get_bloginfo( 'name' ) ); $generated = current_time( 'mysql' ); ob_start(); ?>
| ID | Date | IP | Nec | Ana | Mkt | Fun | SHA-256 |
|---|---|---|---|---|---|---|---|
| id; ?> | created_at ); ?> | ip_address ); ?> | entry_hash ); ?> |