From f570779473994aaa57d42259642ebffbf984edfa Mon Sep 17 00:00:00 2001 From: Hiroyuki Wada Date: Tue, 23 Jun 2026 21:07:09 +0900 Subject: [PATCH] MID-11535 Add encoding and utf-8-bom support for GUI CSV export --- .../button/CsvDownloadInlineMenuItem.java | 17 ++++++++++- .../provider/StreamingCsvDataExporter.java | 28 ++++++++++++++++++- .../xml/ns/public/common/common-gui-3.xsd | 15 ++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/button/CsvDownloadInlineMenuItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/button/CsvDownloadInlineMenuItem.java index 93dce73206a..d1ed3f9064c 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/button/CsvDownloadInlineMenuItem.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/component/button/CsvDownloadInlineMenuItem.java @@ -11,6 +11,7 @@ import java.util.List; import com.evolveum.midpoint.gui.impl.component.data.provider.StreamingCsvDataExporter; +import com.evolveum.midpoint.model.api.authentication.CompiledGuiProfile; import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractDataExporter; import org.apache.wicket.extensions.markup.html.repeater.data.table.export.IExportableColumn; @@ -48,7 +49,8 @@ protected IModel getConfirmationMessage(final Long exportSizeLimit) { @Override protected AbstractDataExporter getDataExporter() { - return new StreamingCsvDataExporter(component.getPageBase()) { + String encoding = resolveExportEncoding(); + return new StreamingCsvDataExporter(component.getPageBase(), encoding) { @Serial private static final long serialVersionUID = 1L; @Override @@ -82,4 +84,17 @@ protected IModel wrapModel(IModel model) { } }; } + + private String resolveExportEncoding() { + try { + CompiledGuiProfile profile = WebComponentUtil.getPageBase(component).getCompiledGuiProfile(); + if (profile.getDefaultExportSettings() != null + && profile.getDefaultExportSettings().getEncoding() != null) { + return profile.getDefaultExportSettings().getEncoding(); + } + } catch (Exception ex) { + LOGGER.warn("Unable to get export encoding setting, falling back to utf-8", ex); + } + return "utf-8"; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/provider/StreamingCsvDataExporter.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/provider/StreamingCsvDataExporter.java index 50e1a601504..ced1736aaf4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/provider/StreamingCsvDataExporter.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/provider/StreamingCsvDataExporter.java @@ -41,10 +41,31 @@ public class StreamingCsvDataExporter extends CSVDataExporter { private static final String DOT_CLASS = StreamingCsvDataExporter.class.getName() + "."; private static final String OPERATION_EXPORT_DATA = DOT_CLASS + "exportData"; + private static final String UTF_8_BOM_ENCODING = "utf-8-bom"; + private static final byte[] UTF8_BOM = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }; + private final PageBase pageBase; + private final String configuredEncoding; - public StreamingCsvDataExporter(PageBase pageBase) { + public StreamingCsvDataExporter(PageBase pageBase, String encoding) { this.pageBase = pageBase; + this.configuredEncoding = encoding; + setCharacterSet(resolveEffectiveCharset(encoding)); + } + + public StreamingCsvDataExporter(PageBase pageBase) { + this(pageBase, null); + } + + private static String resolveEffectiveCharset(String encoding) { + if (encoding == null || UTF_8_BOM_ENCODING.equalsIgnoreCase(encoding)) { + return "utf-8"; + } + return encoding; + } + + private boolean isUtf8Bom() { + return UTF_8_BOM_ENCODING.equalsIgnoreCase(configuredEncoding); } @Override @@ -52,6 +73,11 @@ public void exportData(IDataProvider dataProvider, List> columns, OutputStream outputStream) throws IOException { + if (isUtf8Bom()) { + outputStream.write(UTF8_BOM); + outputStream.flush(); + } + if (!(dataProvider instanceof IterativeExportSupport) || !((IterativeExportSupport) dataProvider).supportsIterativeExport()) { // Fall back to standard export if provider doesn't support iterative export diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd index 8fb381cb3a0..f9d5942ad0e 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd @@ -3871,6 +3871,21 @@ + + + + Character encoding for exported CSV files. + Default is "utf-8". Use "utf-8-bom" to prepend a UTF-8 BOM + (byte order mark) to the exported file, which helps applications + like Microsoft Excel on non-UTF-8 locale systems correctly + recognize the file as UTF-8. + + + GuiExportSettingsType.encoding + 4.10.4 + + +