prefix . self::TABLE_NAME; $charset_cs = $wpdb->get_charset_collate(); $wpdb->query( "CREATE TABLE IF NOT EXISTS {$table} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, attachment_id BIGINT UNSIGNED NOT NULL DEFAULT 0, file_path VARCHAR(500) NOT NULL DEFAULT '', operation VARCHAR(20) NOT NULL DEFAULT '', status ENUM('success','error','skipped') NOT NULL DEFAULT 'success', error_msg TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_attachment_id (attachment_id), KEY idx_created_at (created_at) ) {$charset_cs}" ); } /** * Insert one log row. * * @param int $attachment_id WP attachment post ID. * @param string $file_path Relative file path (e.g. 2025/11/photo.jpg). * @param string $operation 'migrate' | 'remove-local'. * @param string $status One of the STATUS_* constants. * @param string $error_msg Human-readable error on failure; empty otherwise. * @return void * Side effects: inserts a row into wp_pr_r2_log. */ public static function write( int $attachment_id, string $file_path, string $operation, string $status, string $error_msg = '' ): void { global $wpdb; $wpdb->insert( $wpdb->prefix . self::TABLE_NAME, [ 'attachment_id' => $attachment_id, 'file_path' => $file_path, 'operation' => $operation, 'status' => $status, 'error_msg' => $error_msg, 'created_at' => current_time( 'mysql' ), ], [ '%d', '%s', '%s', '%s', '%s', '%s' ] ); } /** * Return the most recent log rows for display on the admin page. * * @param int $limit Number of rows to return (default 20). * @return array */ public static function get_recent( int $limit = 20 ): array { global $wpdb; return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . self::TABLE_NAME . ' ORDER BY id DESC LIMIT %d', $limit ) ); } /** * Delete all rows from the log table. * * @return void * Side effects: truncates wp_pr_r2_log. */ public static function clear(): void { global $wpdb; $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . self::TABLE_NAME ); } /** * Count rows by status for a given operation. * * @param string $operation 'migrate' | 'remove-local' | '' for all. * @return array{success:int,error:int,skipped:int} */ public static function count_by_status( string $operation = '' ): array { global $wpdb; $table = $wpdb->prefix . self::TABLE_NAME; $where = $operation ? $wpdb->prepare( 'WHERE operation = %s', $operation ) : ''; $rows = $wpdb->get_results( "SELECT status, COUNT(*) AS cnt FROM {$table} {$where} GROUP BY status" ); $out = [ 'success' => 0, 'error' => 0, 'skipped' => 0 ]; foreach ( $rows as $row ) { $out[ $row->status ] = (int) $row->cnt; } return $out; } }