diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml index a3c727467e4e8399d1e4f54558e20d1fc16b3c0d..2baa0ead4b47104883f5ee3d72225316a09c4593 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/pom.xml @@ -31,7 +31,7 @@ <artifactId>xwiki-platform-livedata-api</artifactId> <name>XWiki Platform - Live Data - API</name> <properties> - <xwiki.jacoco.instructionRatio>0.70</xwiki.jacoco.instructionRatio> + <xwiki.jacoco.instructionRatio>0.69</xwiki.jacoco.instructionRatio> <checkstyle.suppressions.location>${basedir}/src/checkstyle/checkstyle-suppressions.xml</checkstyle.suppressions.location> <!-- Name to display by the Extension Manager --> <xwiki.extension.name>Live Data API</xwiki.extension.name> diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/DefaultLiveDataConfigurationResolver.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/DefaultLiveDataConfigurationResolver.java index 793ff0cb5ac1562ffe9f6f4f88f578e92fee811b..24c90debefbe98f8ba0bd9f17e7436277c368a00 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/DefaultLiveDataConfigurationResolver.java +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/DefaultLiveDataConfigurationResolver.java @@ -22,7 +22,10 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -42,6 +45,7 @@ import org.xwiki.livedata.LiveDataException; import org.xwiki.livedata.LiveDataLayoutDescriptor; import org.xwiki.livedata.LiveDataMeta; +import org.xwiki.livedata.LiveDataPaginationConfiguration; import org.xwiki.livedata.LiveDataPropertyDescriptor.FilterDescriptor; import org.xwiki.livedata.LiveDataPropertyDescriptor.OperatorDescriptor; import org.xwiki.livedata.LiveDataQuery.Source; @@ -113,11 +117,43 @@ private LiveDataConfiguration mergeBaseConfig(LiveDataConfiguration config) thro mergedConfig.initialize(); handleLayouts(config.getMeta().getLayouts(), mergedConfig.getMeta()); + handlePageSizes(mergedConfig); // Translate using the context locale. return translate(mergedConfig); } + /** + * If the pagination sizes are missing the limit defined in the query, add it to the allowed page limits. + * + * @param mergedConfiguration the live data configuration + */ + private void handlePageSizes(LiveDataConfiguration mergedConfiguration) + { + Integer limit = mergedConfiguration.getQuery().getLimit(); + if (limit != null) { + LiveDataMeta meta = mergedConfiguration.getMeta(); + if (meta == null) { + meta = new LiveDataMeta(); + mergedConfiguration.setMeta(meta); + } + LiveDataPaginationConfiguration pagination = meta.getPagination(); + if (pagination == null) { + pagination = new LiveDataPaginationConfiguration(); + meta.setPagination(pagination); + } + List<Integer> pageSizes = pagination.getPageSizes(); + if (pageSizes == null) { + pageSizes = new ArrayList<>(); + pagination.setPageSizes(pageSizes); + } + if (!pageSizes.contains(limit)) { + pageSizes.add(limit); + Collections.sort(pageSizes); + } + } + } + /** * Filters and updates the layouts in the merged configuration based on the layout descriptors provided by the * initial configuration. diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/LiveDataRendererConfiguration.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/LiveDataRendererConfiguration.java index 3b42a0f5b89ce00cda24bc4e57c7bd1e3651fbc6..a11cba6c033d7c92baf06249ca1c193b7228da96 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/LiveDataRendererConfiguration.java +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/main/java/org/xwiki/livedata/internal/LiveDataRendererConfiguration.java @@ -184,7 +184,7 @@ private LiveDataMeta getMeta(LiveDataRendererParameters parameters) LiveDataMeta meta = new LiveDataMeta(); List<LiveDataLayoutDescriptor> layouts = getLayouts(parameters); meta.setLayouts(layouts); - // If it exists, use the id of the first layout as the default layout. + // If it exists, use the id of the first layout as the default layout. Optional.ofNullable(layouts) .flatMap(ls -> ls.stream().findFirst().map(BaseDescriptor::getId)) .ifPresent(meta::setDefaultLayout); diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/DefaultLiveDataConfigurationResolver.test b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/DefaultLiveDataConfigurationResolver.test index 667b1a405ee2169c9941b9971b6811cdb003ad67..e32f2f128e3ff40522cd21d1c6b642b33e611b8f 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/DefaultLiveDataConfigurationResolver.test +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-api/src/test/resources/DefaultLiveDataConfigurationResolver.test @@ -4,7 +4,8 @@ { "id":"defaultConfigResolverTest", "query":{ - "source":"test" + "source":"test", + "limit": 17 }, "meta":{ "propertyDescriptors":[ @@ -45,7 +46,7 @@ "filters":[], "sort":[], "offset":0, - "limit":15 + "limit":17 }, "data":{ "count":0, @@ -160,7 +161,7 @@ "defaultDisplayer":"text", "pagination":{ "maxShownPages":10, - "pageSizes":[15,25,50,100], + "pageSizes":[15,17,25,50,100], "showEntryRange":true, "showNextPrevious":true }, diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-macro/src/test/java/org/xwiki/livedata/internal/macro/LiveDataMacroTest.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-macro/src/test/java/org/xwiki/livedata/internal/macro/LiveDataMacroTest.java index 5a687e41ce8457b24ee05d5c4dec5d0d7e9e33d0..65b5c928f659b0577de04f034a847ab2504947e2 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-macro/src/test/java/org/xwiki/livedata/internal/macro/LiveDataMacroTest.java +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-macro/src/test/java/org/xwiki/livedata/internal/macro/LiveDataMacroTest.java @@ -19,9 +19,10 @@ */ package org.xwiki.livedata.internal.macro; -import java.util.ArrayList; import java.util.List; +import java.util.Map; +import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.text.StringEscapeUtils; @@ -29,16 +30,26 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.component.manager.ComponentManager; import org.xwiki.configuration.internal.RestrictedConfigurationSourceProvider; import org.xwiki.context.internal.DefaultExecution; +import org.xwiki.icon.IconManager; +import org.xwiki.livedata.LiveData; +import org.xwiki.livedata.LiveDataActionDescriptor; import org.xwiki.livedata.LiveDataConfiguration; -import org.xwiki.livedata.LiveDataConfigurationResolver; +import org.xwiki.livedata.LiveDataEntryDescriptor; +import org.xwiki.livedata.LiveDataLayoutDescriptor; +import org.xwiki.livedata.LiveDataMeta; +import org.xwiki.livedata.LiveDataPaginationConfiguration; +import org.xwiki.livedata.LiveDataPropertyDescriptor; import org.xwiki.livedata.LiveDataQuery; -import org.xwiki.livedata.LiveDataQuery.Filter; -import org.xwiki.livedata.LiveDataQuery.SortEntry; +import org.xwiki.livedata.LiveDataSelectionConfiguration; +import org.xwiki.livedata.internal.DefaultLiveDataConfigurationResolver; import org.xwiki.livedata.internal.LiveDataRenderer; import org.xwiki.livedata.internal.LiveDataRendererConfiguration; +import org.xwiki.livedata.internal.StringLiveDataConfigurationResolver; import org.xwiki.livedata.macro.LiveDataMacroParameters; +import org.xwiki.localization.ContextualLocalizationManager; import org.xwiki.rendering.block.Block; import org.xwiki.rendering.internal.renderer.html5.HTML5Renderer; import org.xwiki.rendering.internal.renderer.html5.HTML5RendererFactory; @@ -46,12 +57,13 @@ import org.xwiki.rendering.internal.renderer.xhtml.image.DefaultXHTMLImageTypeRenderer; import org.xwiki.rendering.internal.renderer.xhtml.link.DefaultXHTMLLinkRenderer; import org.xwiki.rendering.internal.renderer.xhtml.link.DefaultXHTMLLinkTypeRenderer; -import org.xwiki.rendering.internal.transformation.DefaultRenderingContext; import org.xwiki.rendering.renderer.PrintRendererFactory; import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.rendering.transformation.RenderingContext; import org.xwiki.rendering.transformation.TransformationContext; import org.xwiki.security.authorization.ContextualAuthorizationManager; import org.xwiki.skinx.SkinExtension; +import org.xwiki.test.annotation.BeforeComponent; import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; @@ -64,7 +76,10 @@ import org.xwiki.xml.internal.html.SVGDefinitions; import org.xwiki.xml.internal.html.SecureHTMLElementSanitizer; -import static org.mockito.ArgumentMatchers.any; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import static org.mockito.Mockito.when; import static org.xwiki.rendering.test.integration.junit5.BlockAssert.assertBlocks; @@ -92,7 +107,8 @@ DefaultExecution.class, LiveDataRendererConfiguration.class, LiveDataRenderer.class, - DefaultRenderingContext.class + DefaultLiveDataConfigurationResolver.class, + StringLiveDataConfigurationResolver.class }) class LiveDataMacroTest { @@ -100,87 +116,221 @@ class LiveDataMacroTest private LiveDataMacro liveDataMacro; @MockComponent - private LiveDataConfigurationResolver<LiveDataConfiguration> defaultConfigResolver; + private DocumentAccessBridge documentAccessBridge; @MockComponent - private LiveDataConfigurationResolver<String> stringConfigResolver; + private ContextualAuthorizationManager contextualAuthorizationManager; @MockComponent - private DocumentAccessBridge documentAccessBridge; + private IconManager iconManager; @MockComponent - private ContextualAuthorizationManager contextualAuthorizationManager; + private ContextualLocalizationManager contextualLocalizationManager; + + @MockComponent + private RenderingContext renderingContext; @MockComponent @Named("jsfx") private SkinExtension jsfx; + @Inject + @Named("html/5.0") private PrintRendererFactory rendererFactory; @Mock - private MacroTransformationContext context; + private MacroTransformationContext macroTransformationContext; @Mock private TransformationContext transformationContext; - @BeforeEach - void before(MockitoComponentManager componentManager) throws Exception + private LiveDataConfiguration liveDataConfiguration; + + @BeforeComponent + void beforeComponent(MockitoComponentManager componentManager) throws Exception { - this.rendererFactory = componentManager.getInstance(PrintRendererFactory.class, "html/5.0"); + componentManager.registerComponent(ComponentManager.class, "context", componentManager); + + // setup default LD configuration + this.liveDataConfiguration = new LiveDataConfiguration(); + LiveDataQuery liveDataQuery = new LiveDataQuery(); + liveDataQuery.setLimit(15); + liveDataQuery.setProperties(List.of()); + liveDataQuery.setSource(new LiveDataQuery.Source()); + liveDataQuery.setFilters(List.of()); + liveDataQuery.setSort(List.of()); + liveDataQuery.setOffset(0L); + this.liveDataConfiguration.setQuery(liveDataQuery); + + LiveData liveData = new LiveData(); + liveData.setCount(0); + this.liveDataConfiguration.setData(liveData); + + LiveDataMeta meta = new LiveDataMeta(); + LiveDataLayoutDescriptor tableLayout = new LiveDataLayoutDescriptor("table"); + tableLayout.setName("table"); + tableLayout.setIcon(Map.of()); + LiveDataLayoutDescriptor cardsLayout = new LiveDataLayoutDescriptor("cards"); + cardsLayout.setName("cards"); + cardsLayout.setIcon(Map.of()); + meta.setLayouts(List.of( + tableLayout, + cardsLayout + )); + meta.setDefaultLayout(tableLayout.getId()); + + LiveDataPropertyDescriptor.OperatorDescriptor contains = + new LiveDataPropertyDescriptor.OperatorDescriptor("contains", "contains"); + LiveDataPropertyDescriptor.OperatorDescriptor equals = + new LiveDataPropertyDescriptor.OperatorDescriptor("equals", "equals"); + LiveDataPropertyDescriptor.OperatorDescriptor startsWith = + new LiveDataPropertyDescriptor.OperatorDescriptor("startsWith", "startsWith"); + LiveDataPropertyDescriptor.FilterDescriptor textFilter = + new LiveDataPropertyDescriptor.FilterDescriptor("text"); + textFilter.setOperators(List.of(contains, startsWith, equals)); + textFilter.setDefaultOperator("contains"); + + LiveDataPropertyDescriptor.OperatorDescriptor equalsNumber = + new LiveDataPropertyDescriptor.OperatorDescriptor("equals", "="); + LiveDataPropertyDescriptor.OperatorDescriptor less = + new LiveDataPropertyDescriptor.OperatorDescriptor("less", "<"); + LiveDataPropertyDescriptor.OperatorDescriptor greater = + new LiveDataPropertyDescriptor.OperatorDescriptor("greater", ">"); + LiveDataPropertyDescriptor.FilterDescriptor numberFilter = + new LiveDataPropertyDescriptor.FilterDescriptor("number"); + numberFilter.setOperators(List.of(equalsNumber, less, greater)); + numberFilter.setDefaultOperator("equals"); + + LiveDataPropertyDescriptor.FilterDescriptor booleanFilter = + new LiveDataPropertyDescriptor.FilterDescriptor("boolean"); + booleanFilter.setOperators(List.of(equals)); + booleanFilter.setDefaultOperator("equals"); + + LiveDataPropertyDescriptor.OperatorDescriptor between = + new LiveDataPropertyDescriptor.OperatorDescriptor("between", "between"); + LiveDataPropertyDescriptor.OperatorDescriptor before = + new LiveDataPropertyDescriptor.OperatorDescriptor("before", "before"); + LiveDataPropertyDescriptor.OperatorDescriptor after = + new LiveDataPropertyDescriptor.OperatorDescriptor("after", "after"); + LiveDataPropertyDescriptor.FilterDescriptor dateFilter = + new LiveDataPropertyDescriptor.FilterDescriptor("date"); + dateFilter.setOperators(List.of(between, before, after, contains)); + dateFilter.setDefaultOperator("between"); + dateFilter.setParameter("dateFormat", "yyyy/MM/dd HH:mm"); + + LiveDataPropertyDescriptor.OperatorDescriptor empty = + new LiveDataPropertyDescriptor.OperatorDescriptor("empty", "empty"); + LiveDataPropertyDescriptor.FilterDescriptor listFilter = + new LiveDataPropertyDescriptor.FilterDescriptor("list"); + listFilter.setOperators(List.of(equals, startsWith, contains, empty)); + listFilter.setDefaultOperator("contains"); + meta.setFilters(List.of( + textFilter, + numberFilter, + booleanFilter, + dateFilter, + listFilter + )); + meta.setDefaultFilter("text"); + + meta.setDisplayers(List.of( + new LiveDataPropertyDescriptor.DisplayerDescriptor("text"), + new LiveDataPropertyDescriptor.DisplayerDescriptor("link"), + new LiveDataPropertyDescriptor.DisplayerDescriptor("html"), + new LiveDataPropertyDescriptor.DisplayerDescriptor("actions"), + new LiveDataPropertyDescriptor.DisplayerDescriptor("boolean") + )); + meta.setDefaultDisplayer("text"); - when(this.defaultConfigResolver.resolve(any(LiveDataConfiguration.class))) - .thenAnswer(invocation -> invocation.getArgument(0)); + LiveDataPaginationConfiguration paginationConfiguration = new LiveDataPaginationConfiguration(); + paginationConfiguration.setMaxShownPages(10); + paginationConfiguration.setPageSizes(List.of(15,25,50,100)); + paginationConfiguration.setShowEntryRange(true); + paginationConfiguration.setShowNextPrevious(true); + meta.setPagination(paginationConfiguration); - when(this.stringConfigResolver.resolve("{}")).thenReturn(new LiveDataConfiguration()); - when(this.context.getTransformationContext()).thenReturn(this.transformationContext); + meta.setPropertyDescriptors(List.of()); + meta.setPropertyTypes(List.of()); + meta.setEntryDescriptor(new LiveDataEntryDescriptor()); + + LiveDataActionDescriptor view = new LiveDataActionDescriptor("view"); + view.setName("view"); + view.setIcon(Map.of()); + + LiveDataActionDescriptor edit = new LiveDataActionDescriptor("edit"); + edit.setName("edit"); + edit.setIcon(Map.of()); + + LiveDataActionDescriptor delete = new LiveDataActionDescriptor("delete"); + delete.setName("delete"); + delete.setIcon(Map.of("cssClass", "text-danger")); + + LiveDataActionDescriptor copy = new LiveDataActionDescriptor("copy"); + copy.setName("copy"); + copy.setIcon(Map.of()); + + LiveDataActionDescriptor rename = new LiveDataActionDescriptor("rename"); + rename.setName("rename"); + rename.setIcon(Map.of()); + + LiveDataActionDescriptor rights = new LiveDataActionDescriptor("rights"); + rights.setName("rights"); + rights.setIcon(Map.of()); + meta.setActions(List.of(view, edit, delete, copy, rename, rights)); + meta.setSelection(new LiveDataSelectionConfiguration()); + this.liveDataConfiguration.setMeta(meta); + } + + @BeforeEach + void before() throws Exception + { + when(this.macroTransformationContext.getTransformationContext()).thenReturn(this.transformationContext); } @Test void executeWithoutParams() throws Exception { - String expectedConfig = json("{'query':{'source':{}},'meta':{'pagination':{}}}"); - String expected = "<div class=\"liveData loading\" data-config=\"" + escapeXML(expectedConfig) + "\" " - + "data-config-content-trusted=\"true\"></div>"; - List<Block> blocks = this.liveDataMacro.execute(new LiveDataMacroParameters(), null, this.context); + String expected = + String.format("<div class=\"liveData loading\" data-config=\"%s\" " + + "data-config-content-trusted=\"true\"></div>", + escapeXML(json(this.liveDataConfiguration))); + + List<Block> blocks = + this.liveDataMacro.execute(new LiveDataMacroParameters(), null, this.macroTransformationContext); assertBlocks(expected, blocks, this.rendererFactory); } @Test void execute() throws Exception { - StringBuilder expectedConfig = new StringBuilder(); - expectedConfig.append("{"); - expectedConfig.append(" 'id':'test',".trim()); - expectedConfig.append(" 'query':{".trim()); - expectedConfig.append(" 'properties':['avatar','firstName','lastName','position'],".trim()); - expectedConfig.append(" 'source':{'id':'users','wiki':'dev','group':'apps'},".trim()); - expectedConfig.append(" 'filters':[".trim()); - expectedConfig.append(" {'property':'firstName','constraints':[{'value':'m'}]},".trim()); - expectedConfig.append(" {'property':'position','constraints':[{'value':'lead'}]}".trim()); - expectedConfig.append(" ],".trim()); - expectedConfig.append(" 'sort':[".trim()); - expectedConfig.append(" {'property':'firstName'},".trim()); - expectedConfig.append(" {'property':'lastName','descending':true},".trim()); - expectedConfig.append(" {'property':'position'}],".trim()); - expectedConfig.append(" 'offset':20,".trim()); - expectedConfig.append(" 'limit':10".trim()); - expectedConfig.append(" },".trim()); - expectedConfig.append(" 'meta':{".trim()); - expectedConfig.append(" 'layouts':[".trim()); - expectedConfig.append(" {'id':'table'},".trim()); - expectedConfig.append(" {'id':'cards'}".trim()); - expectedConfig.append(" ],".trim()); - expectedConfig.append(" 'defaultLayout':'table',".trim()); - expectedConfig.append(" 'pagination':{".trim()); - expectedConfig.append(" 'pageSizes':[15,25,50],".trim()); - expectedConfig.append(" 'showPageSizeDropdown':true".trim()); - expectedConfig.append(" },'description':'A description'".trim()); - expectedConfig.append(" }".trim()); - expectedConfig.append("}"); + this.liveDataConfiguration.setId("test"); + LiveDataQuery query = this.liveDataConfiguration.getQuery(); + query.setProperties(List.of("avatar", "firstName", "lastName", "position")); + LiveDataQuery.Source source = new LiveDataQuery.Source("users"); + source.setParameter("wiki", "dev"); + source.setParameter("group", "apps"); + query.setSource(source); + query.setFilters(List.of( + new LiveDataQuery.Filter("firstName", "m"), + new LiveDataQuery.Filter("position", "lead") + )); + query.setSort(List.of( + new LiveDataQuery.SortEntry("firstName"), + new LiveDataQuery.SortEntry("lastName", true), + new LiveDataQuery.SortEntry("position") + )); + query.setOffset(20L); + query.setLimit(10); + + LiveDataPaginationConfiguration pagination = this.liveDataConfiguration.getMeta().getPagination(); + pagination.setPageSizes(List.of(10,15,25,50)); + pagination.setShowPageSizeDropdown(true); + + this.liveDataConfiguration.getMeta().setDescription("A description"); String expected = String.format("<div class=\"liveData loading\" id=\"test\" data-config=\"%s\" " - + "data-config-content-trusted=\"true\"></div>", escapeXML(json(expectedConfig.toString()))); + + "data-config-content-trusted=\"true\"></div>", escapeXML(json(this.liveDataConfiguration))); LiveDataMacroParameters parameters = new LiveDataMacroParameters(); parameters.setId("test"); @@ -196,7 +346,7 @@ void execute() throws Exception parameters.setPageSizes("15, 25, 50"); parameters.setDescription("A description"); - List<Block> blocks = this.liveDataMacro.execute(parameters, null, this.context); + List<Block> blocks = this.liveDataMacro.execute(parameters, null, this.macroTransformationContext); assertBlocks(expected, blocks, this.rendererFactory); } @@ -210,39 +360,38 @@ void executeWithContent() throws Exception parameters.setSort("firstName"); parameters.setLimit(10); - LiveDataConfiguration advancedConfig = new LiveDataConfiguration(); - advancedConfig.setQuery(new LiveDataQuery()); - advancedConfig.getQuery().setFilters(new ArrayList<>()); - advancedConfig.getQuery().getFilters().add(new Filter("position", "R&D")); - advancedConfig.getQuery().setSort(new ArrayList<>()); - advancedConfig.getQuery().getSort().add(new SortEntry("lastName", true)); - advancedConfig.getQuery().setLimit(15); - - when(this.stringConfigResolver.resolve("{...}")).thenReturn(advancedConfig); - - StringBuilder expectedConfig = new StringBuilder(); - expectedConfig.append("{"); - expectedConfig.append(" 'id':'test',".trim()); - expectedConfig.append(" 'query':{".trim()); - expectedConfig.append(" 'properties':['avatar','firstName','lastName','position'],".trim()); - expectedConfig.append(" 'source':{'id':'users'},".trim()); - expectedConfig.append(" 'filters':[".trim()); - expectedConfig.append(" {'property':'position','constraints':[{'value':'R&D'}]}".trim()); - expectedConfig.append(" ],".trim()); - expectedConfig.append(" 'sort':[".trim()); - expectedConfig.append(" {'property':'firstName'}".trim()); - expectedConfig.append(" ],".trim()); - expectedConfig.append(" 'limit':10".trim()); - expectedConfig.append(" },".trim()); - expectedConfig.append(" 'meta':{".trim()); - expectedConfig.append(" 'pagination':{}".trim()); - expectedConfig.append(" }".trim()); - expectedConfig.append("}"); + StringBuilder advancedConfig = new StringBuilder(); + advancedConfig.append("{"); + advancedConfig.append(" 'query': {".trim()); + advancedConfig.append(" 'filters': [".trim()); + advancedConfig.append(" {'property': 'position', 'constraints':[{'value':'R&D'}]}".trim()); + advancedConfig.append(" ],".trim()); + advancedConfig.append(" 'sort': [{'property': 'lastName', 'descending':true}],".trim()); + advancedConfig.append(" 'limit': 15".trim()); + advancedConfig.append(" }".trim()); + advancedConfig.append("}"); + + this.liveDataConfiguration.setId("test"); + LiveDataQuery query = this.liveDataConfiguration.getQuery(); + query.setProperties(List.of("avatar", "firstName", "lastName", "position")); + LiveDataQuery.Source source = new LiveDataQuery.Source("users"); + query.setSource(source); + query.setFilters(List.of( + new LiveDataQuery.Filter("position", "R&D") + )); + query.setSort(List.of( + new LiveDataQuery.SortEntry("firstName") + )); + query.setLimit(10); + + LiveDataPaginationConfiguration pagination = this.liveDataConfiguration.getMeta().getPagination(); + pagination.setPageSizes(List.of(10,15,25,50,100)); String expected = String.format("<div class=\"liveData loading\" id=\"test\" data-config=\"%s\" " - + "data-config-content-trusted=\"false\"></div>", escapeXML(json(expectedConfig.toString()))); + + "data-config-content-trusted=\"false\"></div>", escapeXML(json(this.liveDataConfiguration))); - List<Block> blocks = this.liveDataMacro.execute(parameters, "{...}", this.context); + List<Block> blocks = this.liveDataMacro.execute(parameters, json(advancedConfig.toString()), + this.macroTransformationContext); assertBlocks(expected, blocks, this.rendererFactory); } @@ -251,6 +400,13 @@ private String json(String text) return text.replace('\'', '"'); } + private String json(LiveDataConfiguration liveDataConfiguration) throws JsonProcessingException + { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return objectMapper.writeValueAsString(liveDataConfiguration); + } + private String escapeXML(String value) { return StringEscapeUtils.escapeXml10(value).replace("{", "{"); diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-docker/src/test/it/org/xwiki/livedata/test/ui/LiveDataIT.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-docker/src/test/it/org/xwiki/livedata/test/ui/LiveDataIT.java index e8edacd52759ed94fb18a6f9fb6f063da2bcb33f..054750f66c670a50f216c3ddff0e7017b86e7ae5 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-docker/src/test/it/org/xwiki/livedata/test/ui/LiveDataIT.java +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-docker/src/test/it/org/xwiki/livedata/test/ui/LiveDataIT.java @@ -157,6 +157,14 @@ void livedataLivetableTableLayout(TestUtils testUtils, TestReference testReferen // Test the Live Data content. assertEquals(3, tableLayout.countRows()); + + assertEquals(List.of(5, 15, 25, 50, 100), liveDataElement.getPaginationPageSizes()); + + // Check that the list of page sizes is preserved when we select a standard page size (see XWIKI-20650) + liveDataElement = liveDataElement.setPagination(15); + assertEquals(3, liveDataElement.getTableLayout().countRows()); + assertEquals(List.of(5, 15, 25, 50, 100), liveDataElement.getPaginationPageSizes()); + tableLayout.assertRow(DOC_TITLE_COLUMN, "O1"); tableLayout.assertRow(DOC_TITLE_COLUMN, "O2 1"); tableLayout.assertRow(DOC_TITLE_COLUMN, "O3 1"); @@ -425,6 +433,7 @@ private static void createClassNameLiveDataPage(TestUtils testUtils, TestReferen + "{{liveData\n" + " id=\"test\"\n" + " properties=\"" + String.join(",", properties) + "\"\n" + + " limit=\"5\"\n" + " source=\"liveTable\"\n" + " sourceParameters=\"translationPrefix=&className=" + testUtils.serializeReference( testReference.getLocalDocumentReference()) + "\"\n" diff --git a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-pageobjects/src/main/java/org/xwiki/livedata/test/po/LiveDataElement.java b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-pageobjects/src/main/java/org/xwiki/livedata/test/po/LiveDataElement.java index ebb8af055b8f3db2b5f30756d64fc51d50328ba8..03b82e55277db7faada11b83d37ad411e55dd688 100644 --- a/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-pageobjects/src/main/java/org/xwiki/livedata/test/po/LiveDataElement.java +++ b/xwiki-platform-core/xwiki-platform-livedata/xwiki-platform-livedata-test/xwiki-platform-livedata-test-pageobjects/src/main/java/org/xwiki/livedata/test/po/LiveDataElement.java @@ -144,6 +144,22 @@ public LiveDataElement setPagination(int paginationNumber) return this; } + /** + * + * @return the possible pagination sizes. + * @since 16.5.0 + */ + public List<Integer> getPaginationPageSizes() + { + WebElement element = getRootElement().findElement(By.cssSelector(".pagination-page-size select")); + return new Select(element) + .getOptions() + .stream() + .map(WebElement::getText) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } + public void waitUntilReady() { getDriver().waitUntilCondition(input -> isVueLoaded());