prefix . 'nano_banana_history'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table_name} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, user_id bigint(20) unsigned NOT NULL, prompt text NOT NULL, model varchar(100) NOT NULL, image_count smallint NOT NULL DEFAULT 1, action_type varchar(20) NOT NULL DEFAULT 'generate', created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY user_id (user_id), KEY created_at (created_at) ) {$charset_collate};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); } /** * Log a generation event. * * @param string $prompt The prompt used. * @param string $model Model ID. * @param int $image_count Number of images generated. * @param string $action_type 'generate' or 'edit'. */ public static function log_generation( $prompt, $model, $image_count = 1, $action_type = 'generate' ) { global $wpdb; $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->prefix . 'nano_banana_history', array( 'user_id' => get_current_user_id(), 'prompt' => sanitize_textarea_field( $prompt ), 'model' => sanitize_text_field( $model ), 'image_count' => absint( $image_count ), 'action_type' => sanitize_text_field( $action_type ), 'created_at' => current_time( 'mysql' ), ), array( '%d', '%s', '%s', '%d', '%s', '%s' ) ); wp_cache_delete( 'nano_banana_history_total', 'nano_banana' ); } /** * Get history entries. * * @param int $per_page Items per page. * @param int $page Current page. * @return array { 'items' => array, 'total' => int } */ public static function get_history( $per_page = 20, $page = 1 ) { global $wpdb; $offset = ( $page - 1 ) * $per_page; $total = wp_cache_get( 'nano_banana_history_total', 'nano_banana' ); if ( false === $total ) { $total = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching "SELECT COUNT(*) FROM {$wpdb->prefix}nano_banana_history" ); wp_cache_set( 'nano_banana_history_total', $total, 'nano_banana', HOUR_IN_SECONDS ); } $cache_key = 'nano_banana_history_' . $page . '_' . $per_page; $items = wp_cache_get( $cache_key, 'nano_banana' ); if ( false === $items ) { $items = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT h.*, u.display_name FROM {$wpdb->prefix}nano_banana_history h LEFT JOIN {$wpdb->users} u ON h.user_id = u.ID ORDER BY h.created_at DESC LIMIT %d OFFSET %d", $per_page, $offset ) ); wp_cache_set( $cache_key, $items, 'nano_banana', HOUR_IN_SECONDS ); } return array( 'items' => $items, 'total' => (int) $total, ); } /** * Render the history page. */ public function render_page() { if ( ! current_user_can( 'upload_files' ) ) { return; } $page = max( 1, absint( isset( $_GET['paged'] ) ? $_GET['paged'] : 1 ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- pagination on admin page, no state change $per_page = 20; $history = self::get_history( $per_page, $page ); $pages = ceil( $history['total'] / $per_page ); ?>

created_at ) ) ); ?> display_name ); ?> prompt, 15 ) ); ?> model ] ?? $entry->model ); ?> action_type ) ); ?> image_count ); ?>
1 ) : ?>
add_query_arg( 'paged', '%#%' ), 'format' => '', 'current' => $page, 'total' => $pages, ) ) ); ?>
delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prefix . 'nano_banana_history', array( 'id' => $id ), array( '%d' ) ); wp_cache_delete( 'nano_banana_history_total', 'nano_banana' ); wp_send_json_success(); } }