diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java index 7d6eddd378d..81b7878430b 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java @@ -16,6 +16,8 @@ public interface CommonApplicationConstants extends Constants { String emailUser(); + String exportCsv(); + @DefaultStringValue("") // Use annotation and not a properties key to leave it out of translations String empty(); diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/uicommon/model/SearchableTableModelProvider.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/uicommon/model/SearchableTableModelProvider.java index a23feceb083..76176ce5ba9 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/uicommon/model/SearchableTableModelProvider.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/uicommon/model/SearchableTableModelProvider.java @@ -12,4 +12,11 @@ * List model type. */ public interface SearchableTableModelProvider extends SearchableModelProvider, ActionTableDataProvider { + /** + * Returns base file name for exported CSV content. Or null if CSV export is not supported by the table + * @return base file name or null + */ + default String csvExportFilenameBase() { + return null; + } } diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/SimpleActionTable.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/SimpleActionTable.java index 463a6881f0c..6befc0d0337 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/SimpleActionTable.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/SimpleActionTable.java @@ -85,11 +85,11 @@ public SimpleActionTable(final SearchableTableModelProvider dataProvider, setLoadingState(LoadingState.LOADING); }); - createActionKebab(); + createActionKebab(dataProvider); showActionKebab(); } - private void createActionKebab() { + private void createActionKebab(SearchableTableModelProvider dataProvider) { ActionButton changeBtn = new ActionAnchorListItem(constants.changeColumnsVisibilityOrder()); changeBtn.addClickHandler(event -> showColumnModificationDialog(event)); actionKebab.addMenuItem(changeBtn); @@ -97,6 +97,13 @@ private void createActionKebab() { ActionButton resetBtn = new ActionAnchorListItem(constants.resetGridSettings()); resetBtn.addClickHandler(event -> resetGridSettings()); actionKebab.addMenuItem(resetBtn); + + String csvFilenameBase = dataProvider.csvExportFilenameBase(); + if (csvFilenameBase != null) { + ActionButton csvExportBtn = new ActionAnchorListItem(constants.exportCsv()); + csvExportBtn.addClickHandler(event -> exportCsv(csvFilenameBase)); + actionKebab.addMenuItem(csvExportBtn); + } } private void showColumnModificationDialog(ClickEvent event) { @@ -108,6 +115,11 @@ private void resetGridSettings() { table.resetGridSettings(); } + private void exportCsv(String filenameBase) { + TableCsvExporter csvExporter = new TableCsvExporter<>(filenameBase, getDataProvider(), table); + csvExporter.generateCsv(); + } + public void showActionKebab() { actionKebab.setVisible(true); } diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporter.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporter.java new file mode 100644 index 00000000000..a3770fd347d --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporter.java @@ -0,0 +1,258 @@ +package org.ovirt.engine.ui.common.widget.table; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.ovirt.engine.core.common.businessentities.BusinessEntityWithStatus; +import org.ovirt.engine.ui.common.uicommon.model.SearchableTableModelProvider; +import org.ovirt.engine.ui.common.widget.table.column.AbstractColumn; +import org.ovirt.engine.ui.uicommonweb.models.ListModel; +import org.ovirt.engine.ui.uicompat.EnumTranslator; +import org.ovirt.engine.ui.uicompat.Event; +import org.ovirt.engine.ui.uicompat.EventArgs; +import org.ovirt.engine.ui.uicompat.IEventListener; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.cellview.client.AbstractCellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.Header; + +/** + * A utility class that allows to export content of any {@link ActionCellTable} to CSV + *

+ * This class allows to export content of any {@link ActionCellTable} with {@link SearchableTableModelProvider} to CSV: + *

    + *
  • It takes into account current columns visibility. Only visible columns are exported
  • + *
  • Current sorting configuration would be applied to the exported content
  • + *
  • Current filtering configuration would be applied to the exported content
  • + *
  • It generates exported CSV file name in the following way: filenameBase.currentDateAndTime.csv, where the + * filenameBase is provided by {@link SearchableTableModelProvider}, see csvExportFilenameBase method; + * currentDateAndTime is the current date and time in the yyyy-MM-dd.HH-mm format
  • + *
  • It initiates automatic download of the generated CSV file
  • + *
  • The generated CSV file is limited by 10000 rows
  • + *
+ * @param + * Table row data type. + **/ +public class TableCsvExporter { + private static final int LINES_LIMIT = 10000; + private static final String HTML_TAG_PATTERN = "<[^>]*>";//$NON-NLS-1$ + private static final String EMPTY = "";//$NON-NLS-1$ + private static final char SPACE = ' ';//$NON-NLS-1$ + private static final char NEW_LINE = '\n';//$NON-NLS-1$ + private static final char SEPARATOR = ',';//$NON-NLS-1$ + private static final char DOT = '.';//$NON-NLS-1$ + private static final char SINGLE_QUOTE = '\'';//$NON-NLS-1$ + private static final char DOUBLE_QUOTE = '"';//$NON-NLS-1$ + private static final String DOUBLE_QUOTE_STR = "\"";//$NON-NLS-1$ + private static final String DOUBLE_DOUBLE_QUOTE_STR = "\"\"";//$NON-NLS-1$ + private static final String FILE_EXT = ".csv";//$NON-NLS-1$ + private static final String FILE_CURRENT_DATE_AND_TIME_FORMAT = "yyyy-MM-dd.HH-mm";//$NON-NLS-1$ + + private final String filenameBase; + private final SearchableTableModelProvider modelProvider; + private final AbstractCellTable table; + private final ColumnController columnController; + private final boolean testMode; + private final StringBuilder csv; + private int pageOffset; + private int linesExported = -1; + + public TableCsvExporter(String filenameBase, SearchableTableModelProvider modelProvider, ActionCellTable table) { + this(filenameBase, modelProvider, table, table); + } + + TableCsvExporter(String filenameBase, SearchableTableModelProvider modelProvider, AbstractCellTable table, ColumnController columnController) { + this.filenameBase = filenameBase; + this.modelProvider = modelProvider; + this.table = table; + this.columnController = columnController; + this.csv = new StringBuilder(); + this.pageOffset = 0; + this.testMode = table != columnController; // For unit tests + } + + public void generateCsv() { + // Header + int colCount = table.getColumnCount(); + List> columns = new ArrayList<>(); + boolean firstInLine = true; + for (int i = 0; i < colCount; i++) { + Column col = table.getColumn(i); + if (columnController.isColumnVisible(col) && + col instanceof AbstractColumn) { + String colName = ((AbstractColumn)col).getContextMenuTitle(); + if (colName == null || colName.isEmpty()) { + Header header = table.getHeader(i); + colName = csvValue(header.getValue()); + } + if (colName != null && !colName.isEmpty()) { + columns.add((AbstractColumn) col); + firstInLine = appendItem(firstInLine, colName); + } + } + } + newLine(); + + // Content + // Note that in order to export content of the table we need to scroll to the first page, then export the content + // by moving forward page by page till the end (or till the 10000 rows limit is reached). And then return to the + // page where the export functionality was initiated. + ListModel model = modelProvider.getModel(); + Event itemsChangedEvent = model.getItemsChangedEvent(); + if (modelProvider.canGoBack()) { + // If we are not on the first page then let's move to the first page + itemsChangedEvent.addListener(new IEventListener() { + @Override + public void eventRaised(Event ev, Object sender, EventArgs args) { + if (modelProvider.canGoBack()) { + // We are still not on the first page + pageOffset--; + modelProvider.goBack(); + } else { + // The first page was reached. Let's generate the CSV file + itemsChangedEvent.removeListener(this); + generateContent(columns); + } + } + }); + pageOffset--; + modelProvider.goBack(); + } else { + // We are on the first page already. Let's generate the CSV file + generateContent(columns); + } + } + + private void generateContent(List> columns) { + ListModel model = modelProvider.getModel(); + // Export current page to CSV ... + generatePage(columns, model.getItems()); + if (hasMoreData()) { + // ... and then move to the next page if any + Event itemsChangedEvent = model.getItemsChangedEvent(); + itemsChangedEvent.addListener(new IEventListener() { + @Override + public void eventRaised(Event ev, Object sender, EventArgs args) { + // When the next page was loaded continue the export + itemsChangedEvent.removeListener(this); + generateContent(columns); + } + }); + pageOffset++; + modelProvider.goForward(); + } else { + // All the content was exported, so move to the initial page and initiate download of the exported CSV file + restorePageAndFinish(); + } + } + + private void restorePageAndFinish() { + // Before initiating the download of the exported content we want to return to the initial page of the table + if (pageOffset > 0 && modelProvider.canGoBack()) { + // We still are not on the initial page. Let move towards it + ListModel model = modelProvider.getModel(); + Event itemsChangedEvent = model.getItemsChangedEvent(); + itemsChangedEvent.addListener(new IEventListener() { + @Override + public void eventRaised(Event ev, Object sender, EventArgs args) { + itemsChangedEvent.removeListener(this); + restorePageAndFinish(); + } + }); + pageOffset--; + modelProvider.goBack(); + } else { + // We reached the initial page, let's initiate automatic download of the generated CSV file + if (!testMode) { // disabled for unit tests + downloadCsv(getFileName(), getGeneratedCsv()); + } + } + } + + private void generatePage(List> columns, Collection items) { + boolean firstInLine = true; + for (T item : items) { + for (AbstractColumn col : columns) { + String cellValue = csvValue(col.getValue(item)); + if (cellValue == null || cellValue.isEmpty()) { + cellValue = csvValue(col.getTooltip(item)); + } + firstInLine = appendItem(firstInLine, cellValue); + } + firstInLine = newLine(); + } + } + + private boolean hasMoreData() { + return modelProvider.canGoForward() && linesExported < LINES_LIMIT; + } + + private String csvValue(Object tableValue) { + String result = null; + if (tableValue instanceof String) { + result = (String) tableValue; + } else if (tableValue instanceof SafeHtml) { + result = ((SafeHtml) tableValue).asString(); + } else if (tableValue instanceof BusinessEntityWithStatus) { + result = translateEnum(((BusinessEntityWithStatus) tableValue).getStatus()); + } + + if (result != null) { + // Sometimes content of a cell contains images (encoded in HTML tags). Let's remove the images. Just leave a + // text of the cell + result = result.replaceAll(HTML_TAG_PATTERN, EMPTY).trim(); + } + + return result; + } + + private String translateEnum(Enum key) { + return testMode ? key.name() : EnumTranslator.getInstance().translate(key); + } + + private boolean appendItem(boolean firstInLine, String item) { + if (!firstInLine) { + csv.append(SEPARATOR); + } + if (item != null) { + csv.append(escapeSpecialCharacters(item)); + } + return false; + } + + private boolean newLine() { + csv.append(NEW_LINE); + linesExported++; + return true; + } + + private String escapeSpecialCharacters(String data) { + String escapedData = data.replace(NEW_LINE, SPACE); + if (escapedData.indexOf(SEPARATOR) >= 0 || + escapedData.indexOf(SINGLE_QUOTE) >= 0 || + escapedData.indexOf(DOUBLE_QUOTE) >= 0) { + escapedData = DOUBLE_QUOTE + escapedData.replace(DOUBLE_QUOTE_STR, DOUBLE_DOUBLE_QUOTE_STR) + DOUBLE_QUOTE; + } + return escapedData; + } + + String getFileName() { + String dt = DateTimeFormat.getFormat(FILE_CURRENT_DATE_AND_TIME_FORMAT).format(new Date());//$NON-NLS-1$ + return filenameBase + DOT + dt + FILE_EXT; + } + String getGeneratedCsv() { + return csv.toString(); + } + + private native void downloadCsv(String filename, String text)/*-{ + var pom = document.createElement('a'); + pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + pom.setAttribute('download', filename); + document.body.appendChild(pom); + pom.click(); + document.body.removeChild(pom); }-*/; +} diff --git a/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/ui/common/CommonApplicationConstants.properties b/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/ui/common/CommonApplicationConstants.properties index 676d9def608..5007b22e4be 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/ui/common/CommonApplicationConstants.properties +++ b/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/ui/common/CommonApplicationConstants.properties @@ -979,6 +979,7 @@ ppcChipset=pseries s390xChipset=zseries resetGridSettings=Reset settings changeColumnsVisibilityOrder=Change columns visibility/order +exportCsv=Export to CSV typeToSearchPlaceHolder=Type to search configChangesPending=Configuration changes may be pending. Unplug and replug to apply. permissionFilter=Permission Filters diff --git a/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporterTest.java b/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporterTest.java new file mode 100644 index 00000000000..b1005e0ebf4 --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/test/java/org/ovirt/engine/ui/common/widget/table/TableCsvExporterTest.java @@ -0,0 +1,221 @@ +package org.ovirt.engine.ui.common.widget.table; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.VMStatus; +import org.ovirt.engine.ui.common.uicommon.model.SearchableTableModelProvider; +import org.ovirt.engine.ui.common.widget.table.column.AbstractColumn; +import org.ovirt.engine.ui.uicommonweb.models.SearchableListModel; +import org.ovirt.engine.ui.uicompat.Event; +import org.ovirt.engine.ui.uicompat.EventArgs; + +import com.google.gwt.junit.GWTMockUtilities; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.cellview.client.AbstractCellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.Header; + +@ExtendWith(MockitoExtension.class) +public class TableCsvExporterTest { + private final List> headers = new ArrayList<>(); + private final List> columns = new ArrayList<>(); + private final List> visibleColumns = new ArrayList<>(); + private final List> pages = new ArrayList<>(); + + private final Event itemsChangedEvent = new Event<>("Name", TableCsvExporterTest.class);//$NON-NLS-1$ + + private int currentPage; + + private TableCsvExporter exporter; + + @BeforeEach + public void init() { + GWTMockUtilities.disarm(); + SearchableTableModelProvider modelProvider = mock(SearchableTableModelProvider.class); + AbstractCellTable table = mock(AbstractCellTable.class); + SearchableListModel model = mock(SearchableListModel.class); + ColumnController columnController = mock(ColumnController.class); + + AbstractColumn statusIconCol = mockColumn(null); + when(statusIconCol.getContextMenuTitle()).thenReturn("Status Icon");//$NON-NLS-1$ + lenient().when(statusIconCol.getValue(any(VM.class))).thenReturn(null); + lenient().when(statusIconCol.getTooltip(any(VM.class))).thenAnswer(invocationOnMock -> { + VM vm = invocationOnMock.getArgument(0, VM.class); + return mockHtml("
" + vm.getStatus().name() + "
");//$NON-NLS-1$ //$NON-NLS-2$ + }); + AbstractColumn nameCol = mockColumn("Name");//$NON-NLS-1$ + lenient().when(nameCol.getValue(any(VM.class))).thenAnswer(invocationOnMock -> { + VM vm = invocationOnMock.getArgument(0, VM.class); + return vm.getName(); + }); + AbstractColumn descriptionCol = mockColumn("Description");//$NON-NLS-1$ + lenient().when(descriptionCol.getValue(any(VM.class))).thenAnswer(invocationOnMock -> { + VM vm = invocationOnMock.getArgument(0, VM.class); + return vm.getDescription(); + }); + AbstractColumn statusCol = mockColumn("Status");//$NON-NLS-1$ + lenient().when(statusCol.getValue(any(VM.class))).thenAnswer(invocationOnMock -> invocationOnMock.getArgument(0, VM.class)); + + when(table.getColumnCount()).thenReturn(columns.size()); + when(table.getColumn(anyInt())).thenAnswer(invocationOnMock -> { + int index = invocationOnMock.getArgument(0, Integer.class); + return columns.get(index); + }); + when(columnController.isColumnVisible(any())).thenAnswer(invocationOnMock -> { + Column column = invocationOnMock.getArgument(0, Column.class); + return visibleColumns.contains(column); + }); + when(table.getHeader(anyInt())).thenAnswer(invocationOnMock -> { + int index = invocationOnMock.getArgument(0, Integer.class); + return headers.get(index); + }); + + when(modelProvider.getModel()).thenReturn(model); + when(model.getItemsChangedEvent()).thenReturn(itemsChangedEvent); + when(model.getItems()).thenAnswer(invocationOnMock -> { + if (currentPage < 0 || currentPage >= pages.size()) { + return Collections.emptyList(); + } + return pages.get(currentPage); + }); + when(modelProvider.canGoBack()).thenAnswer(invocationOnMock -> currentPage > 0); + when(modelProvider.canGoForward()).thenAnswer(invocationOnMock -> currentPage < pages.size() - 1); + lenient().doAnswer(invocationOnMock -> { + currentPage--; + lenient().when(model.getItems()).thenReturn(pages.get(currentPage)); + itemsChangedEvent.raise(null, null); + return null; + }).when(modelProvider).goBack(); + lenient().doAnswer(invocationOnMock -> { + currentPage++; + lenient().when(model.getItems()).thenReturn(pages.get(currentPage)); + itemsChangedEvent.raise(null, null); + return null; + }).when(modelProvider).goForward(); + + exporter = new TableCsvExporter<>("vms", modelProvider, table, columnController);//$NON-NLS-1$ + } + + @AfterEach + public void tearDown() { + GWTMockUtilities.restore(); + } + + private AbstractColumn mockColumn(String header) { + AbstractColumn column = mock(AbstractColumn.class); + columns.add(column); + visibleColumns.add(column); + Header hdr = mock(Header.class); + if (header != null) { + lenient().when(hdr.getValue()).thenReturn(header); + } + headers.add(hdr); + return column; + } + + private VM mockItem(VMStatus status, String name, String description) { + VM item = mock(VM.class); + lenient().when(item.getStatus()).thenReturn(status); + lenient().when(item.getName()).thenReturn(name); + lenient().when(item.getDescription()).thenReturn(description); + return item; + } + + private SafeHtml mockHtml(String html) { + SafeHtml result = mock(SafeHtml.class); + when(result.asString()).thenReturn(html); + return result; + } + + private void mockPage() { + pages.add(Arrays.asList( + mockItem(VMStatus.Down, "vm1", " descr1 with \n spec '\" symbols "), //$NON-NLS-1$ //$NON-NLS-2$ + mockItem(VMStatus.Up, "vm2", ""), //$NON-NLS-1$ //$NON-NLS-2$ + mockItem(VMStatus.Paused, "vm3", null)//$NON-NLS-1$ + )); + } + + @Test + public void exportEmptyTableTest() { + exporter.generateCsv(); + assertGeneratedCsv("Status Icon,Name,Description,Status\n");//$NON-NLS-1$ + } + + @Test + public void exportOnePageTableTest() { + mockPage(); + exporter.generateCsv(); + assertGeneratedCsv("Status Icon,Name,Description,Status\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n"); //$NON-NLS-1$ + assertEquals(0, currentPage); + } + + @Test + public void exportMultiplePagesTableTest() { + mockPage(); + mockPage(); + exporter.generateCsv(); + assertGeneratedCsv("Status Icon,Name,Description,Status\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n"); //$NON-NLS-1$ + assertEquals(0, currentPage); + } + + @Test + public void exportMultiplePagesTableFromNonFirtsPageTest() { + mockPage(); + mockPage(); + mockPage(); + currentPage = 2; + exporter.generateCsv(); + assertGeneratedCsv("Status Icon,Name,Description,Status\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n" + //$NON-NLS-1$ + "Down,vm1,\"descr1 with spec '\"\" symbols\",Down\n" + //$NON-NLS-1$ + "Up,vm2,,Up\n" + //$NON-NLS-1$ + "Paused,vm3,,Paused\n"); //$NON-NLS-1$ + assertEquals(2, currentPage); + } + + @Test + public void exportTableWithInvisibleColumnsTest() { + visibleColumns.remove(2); + mockPage(); + exporter.generateCsv(); + assertGeneratedCsv("Status Icon,Name,Status\n" + //$NON-NLS-1$ + "Down,vm1,Down\n" + //$NON-NLS-1$ + "Up,vm2,Up\n" + //$NON-NLS-1$ + "Paused,vm3,Paused\n"); //$NON-NLS-1$ + assertEquals(0, currentPage); + } + + private void assertGeneratedCsv(String expectedCsv) { + assertEquals(expectedCsv, exporter.getGeneratedCsv()); + } +} diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DataCenterModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DataCenterModule.java index 94c8fa3e36d..129172419de 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DataCenterModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DataCenterModule.java @@ -103,6 +103,11 @@ public MainModelProvider getDataCenterListProv return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "dataCenters";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DiskModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DiskModule.java index bce478daae9..bca58ed83ad 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DiskModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/DiskModule.java @@ -87,6 +87,11 @@ public MainModelProvider getDiskListProvider(EventBus event return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "disks";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/EventModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/EventModule.java index 084df600a79..969f1207349 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/EventModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/EventModule.java @@ -41,6 +41,11 @@ public MainModelProvider> getEventListProvider(Ev return super.getModelPopup(source, lastExecutedCommand, windowModel); } } + + @Override + public String csvExportFilenameBase() { + return "events";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/HostModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/HostModule.java index 81ad69d3d24..8cf21897e86 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/HostModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/HostModule.java @@ -138,6 +138,11 @@ public MainModelProvider> getHostListProvider(EventBus return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "hosts";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/NetworkModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/NetworkModule.java index 03dda4ba3c8..46e63247e50 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/NetworkModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/NetworkModule.java @@ -106,6 +106,10 @@ public MainModelProvider getNetworkListProvider(E } } + @Override + public String csvExportFilenameBase() { + return "networks";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/PoolModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/PoolModule.java index 4c5f221ff1b..44da4fb8d33 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/PoolModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/PoolModule.java @@ -75,6 +75,11 @@ public MainModelProvider getPoolListProvider(EventBus eve return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "pools";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/StorageModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/StorageModule.java index 3dd58418157..d48cbb8ec05 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/StorageModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/StorageModule.java @@ -113,6 +113,11 @@ public MainModelProvider getStorageListProvider return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "storageDomains";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/TemplateModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/TemplateModule.java index b455acb473f..ebcb8985409 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/TemplateModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/TemplateModule.java @@ -132,6 +132,11 @@ public MainModelProvider getTemplateListProvider( return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "templates";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VirtualMachineModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VirtualMachineModule.java index 301def8794c..89ca5781733 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VirtualMachineModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VirtualMachineModule.java @@ -194,6 +194,11 @@ public MainModelProvider> getVmListProvider(EventBus event return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "vms";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VolumeModule.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VolumeModule.java index 0ea3e014e0f..c7e72037464 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VolumeModule.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/gin/uicommon/VolumeModule.java @@ -109,6 +109,11 @@ public MainModelProvider getVolumeListProv return super.getConfirmModelPopup(source, lastExecutedCommand); } } + + @Override + public String csvExportFilenameBase() { + return "volumes";//$NON-NLS-1$ + } }; result.setModelProvider(modelProvider); return result; diff --git a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/uicommon/model/EventModelProvider.java b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/uicommon/model/EventModelProvider.java index a279c06d88c..637e66ebc4c 100644 --- a/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/uicommon/model/EventModelProvider.java +++ b/frontend/webadmin/modules/webadmin/src/main/java/org/ovirt/engine/ui/webadmin/uicommon/model/EventModelProvider.java @@ -34,4 +34,9 @@ protected void initializeModelHandlers(final EventListModel model) { protected boolean handleItemsChangedEvent() { return false; } + + @Override + public String csvExportFilenameBase() { + return "events";//$NON-NLS-1$ + } }