diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java index de84f9fc4593..af61f8422c44 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/categories/CategoriesResource.java @@ -164,7 +164,8 @@ public final Response getCategories(@Context final HttpServletRequest httpReques @QueryParam(PaginationUtil.PER_PAGE) final int perPage, @DefaultValue("category_name") @QueryParam(PaginationUtil.ORDER_BY) final String orderBy, @DefaultValue("ASC") @QueryParam(PaginationUtil.DIRECTION) final String direction, - @QueryParam("showChildrenCount") final boolean showChildrenCount) { + @QueryParam("showChildrenCount") final boolean showChildrenCount, + @QueryParam("allLevels") final boolean allLevels) { final InitDataObject initData = webResource.init(null, httpRequest, httpResponse, true, null); @@ -178,6 +179,7 @@ public final Response getCategories(@Context final HttpServletRequest httpReques final Map extraParams = new HashMap<>(); extraParams.put("childrenCategories", false); + extraParams.put("searchInAllLevels", allLevels); try { response = showChildrenCount == false ? this.paginationUtil.getPage(httpRequest, user, filter, page, perPage, orderBy, diff --git a/dotCMS/src/main/java/com/dotcms/util/pagination/CategoriesPaginator.java b/dotCMS/src/main/java/com/dotcms/util/pagination/CategoriesPaginator.java index a606feec76ed..2760c46692fa 100644 --- a/dotCMS/src/main/java/com/dotcms/util/pagination/CategoriesPaginator.java +++ b/dotCMS/src/main/java/com/dotcms/util/pagination/CategoriesPaginator.java @@ -33,11 +33,38 @@ public CategoriesPaginator(){ this(APILocator.getCategoryAPI()); } + /** + * + * Return {@link Category} by pagiunation, you can et 3 parameters to set the way the search is done: + * + * - searchInAllLevels: A Boolean value. If TRUE, the search will look for the category at any level, + * and the childrenCategories parameter is ignored. + * - childrenCategories: A Boolean value. If FALSE, the search is limited to the top-level category. If TRUE, + * the search is confined to the children of the category specified by the inode parameter. + * - inode: If set and childrenCategories is TRUE, the search is limited to the children of the category with this inode. + * + * @param user The {@link User} that is using this method. + * @param filter Allows you to add more conditions to the query via SQL code. It's very + * important that you always sanitize the code in order to avoid SQL + * injection attacks. The complexity of the allows SQL code will depend on + * how the Paginator class is implemented. + * @param limit The maximum number of returned items in the result set, for pagination + * purposes. + * @param offset The requested page number of the result set, for pagination purposes. + * @param orderby The order-by clause, which must always be sanitized as well. + * @param direction The direction of the order-by clause for the results. + * @param extraParams A {@link Map} including any extra parameters that may be required by the + * implementation class that are not part of this method's signature. + * + * @return + */ @Override public PaginatedArrayList getItems(final User user, final String filter, final int limit, final int offset, final String orderby, final OrderDirection direction, final Map extraParams) { boolean childrenCategories = extraParams.containsKey("childrenCategories") ? (Boolean)extraParams.get("childrenCategories") : false; + boolean searchInAllLevels = extraParams.containsKey("searchInAllLevels") ? (Boolean)extraParams.get("searchInAllLevels") : false; + String inode = extraParams.containsKey("inode") ? String.valueOf(extraParams.get("inode")) : StringPool.BLANK; try { String categoriesSort = null; @@ -47,10 +74,8 @@ public PaginatedArrayList getItems(final User user, final String filte } final PaginatedArrayList result = new PaginatedArrayList<>(); - final PaginatedCategories categories = childrenCategories == false ? - categoryAPI.findTopLevelCategories(user, false, offset, limit, filter, categoriesSort) - : categoryAPI.findChildren(user, extraParams.containsKey("inode") ? String.valueOf(extraParams.get("inode")) : StringPool.BLANK, - false, offset, limit, filter, categoriesSort); + final PaginatedCategories categories = searchInAllLevels ? searchAllLevels(user, filter, limit, offset) : + searchInOneLevel(user, filter, inode, limit, offset, childrenCategories, categoriesSort); result.setTotalResults(categories.getTotalCount()); @@ -63,4 +88,43 @@ public PaginatedArrayList getItems(final User user, final String filte throw new DotRuntimeException(e); } } + + /** + * Search for categories at every level. + * + * @param user to check Permission + * @param filter Search Filter to apply in key, name or variable's name + * @param limit The maximum number of returned items in the result set, for pagination purposes. + * @param offset The requested page number of the result set, for pagination purposes. + * @return + * @throws DotDataException + * @throws DotSecurityException + */ + private PaginatedCategories searchAllLevels(User user, String filter, int limit, int offset) + throws DotDataException, DotSecurityException { + return categoryAPI.findAll(filter, user, false, limit, offset); + } + + /** + * Search for a category at a single level. This could be either the top level or within the immediate children of any category. + * + * @param user to Check permission + * @param filter Search Filter to apply in key, name or variable's name + * @param inode If the parent inode is set, the search will be conducted only among the children of this category. + * Otherwise, the search will include only the top-level categories. + * @param limit The maximum number of returned items in the result set, for pagination purposes. + * @param offset The requested page number of the result set, for pagination purposes. + * @param childrenCategories If it is false then Search only on the Top Level + * @param orderby Field to order by + * @return + * @throws DotDataException + * @throws DotSecurityException + */ + private PaginatedCategories searchInOneLevel(final User user, final String filter, final String inode, final int limit, + final int offset, final boolean childrenCategories, + final String orderby) throws DotDataException, DotSecurityException { + return childrenCategories == false ? + categoryAPI.findTopLevelCategories(user, false, offset, limit, filter, orderby) : + categoryAPI.findChildren(user, inode, false, offset, limit, filter, orderby); + } } diff --git a/dotcms-postman/src/main/resources/postman/Category.postman_collection.json b/dotcms-postman/src/main/resources/postman/Category.postman_collection.json index a3095cb162e0..30739ad6490d 100644 --- a/dotcms-postman/src/main/resources/postman/Category.postman_collection.json +++ b/dotcms-postman/src/main/resources/postman/Category.postman_collection.json @@ -1,8 +1,9 @@ { "info": { - "_postman_id": "cd61fd14-c09a-45be-8d96-1759b1e0b294", + "_postman_id": "6101d86a-acc3-42d5-a3cb-266d9c1e7a72", "name": "Category", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "30436704" }, "item": [ { @@ -1421,12 +1422,7 @@ "type": "file", "src": "/home/hmb-g8/Desktop/categories_10_6_2022.csv" } - ], - "options": { - "raw": { - "language": "json" - } - } + ] }, "url": { "raw": "{{serverURL}}/api/v1/categories/_import", @@ -1514,12 +1510,7 @@ "type": "file", "src": "/home/hmb-g8/Desktop/categories_10_6_2022.csv" } - ], - "options": { - "raw": { - "language": "json" - } - } + ] }, "url": { "raw": "{{serverURL}}/api/v1/categories/_import", @@ -2425,12 +2416,340 @@ "value": false, "type": "boolean" } - ] + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=&page=0&per_page=5&orderby=modDate&direction=DESC", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "per_page", + "value": "5" + }, + { + "key": "orderby", + "value": "modDate" + }, + { + "key": "direction", + "value": "DESC" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Getting Category Children", + "item": [ + { + "name": "Create Top Level category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.entity.categoryName).to.eql(\"PostmanTest Top level Category\");", + "});", + "", + "pm.collectionVariables.set(\"topCategoryInode\", jsonData.entity.inode);", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"PostmanTest TOP_LEVEL\",\n \"categoryName\" : \"PostmanTest Top level Category\",\n \"categoryVelocityVarName\" : \"PostmanTest_childrenTopLevel\",\n \"sortOrder\" : 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create First child Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.entity.categoryName).to.eql(\"PostmanTest Child 1\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"PostmanTest Child 1\",\n \"categoryName\" : \"PostmanTest Child 1\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"PostmanTest_ChildTestKey_1\",\n \"parent\" : \"{{topCategoryInode}}\"\n}\n\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Second child Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.entity.categoryName).to.eql(\"PostmanTest Child 2\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"PostmanTest Child 2\",\n \"categoryName\" : \"PostmanTest Child 2\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"PostmanTest_ChildTestKey_2\",\n \"parent\" : \"{{topCategoryInode}}\"\n}\n\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Create Third child Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.entity.categoryName).to.eql(\"Child 3\");", + "});", + "", + "", + "pm.collectionVariables.set(\"child3Inode\", jsonData.entity.inode);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"Child 3\",\n \"categoryName\" : \"Child 3\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"ChildTestKey_3\",\n \"parent\" : \"{{topCategoryInode}}\"\n}\n\n", + "options": { + "raw": { + "language": "json" + } + } }, - "method": "GET", - "header": [], "url": { - "raw": "{{serverURL}}/api/v1/categories?filter=&page=0&per_page=5&orderby=modDate&direction=DESC", + "raw": "{{serverURL}}/api/v1/categories", "host": [ "{{serverURL}}" ], @@ -2438,28 +2757,6 @@ "api", "v1", "categories" - ], - "query": [ - { - "key": "filter", - "value": "" - }, - { - "key": "page", - "value": "0" - }, - { - "key": "per_page", - "value": "5" - }, - { - "key": "orderby", - "value": "modDate" - }, - { - "key": "direction", - "value": "DESC" - } ] } }, @@ -2467,6 +2764,255 @@ } ] }, + { + "name": "Create Grandchild Category", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Information Saved Correctly\", function () {", + " ", + " pm.expect(jsonData.entity.categoryName).to.eql(\"PostmanTest Grandchild 1\");", + "});", + "", + "", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key\":\"PostmanTest ChiGrandchildld 1\",\n \"categoryName\" : \"PostmanTest Grandchild 1\",\n \"keywords\":\"This is a child test category\",\n \"categoryVelocityVarName\" : \"PostmanTest_GrandcßhildTestKey_1\",\n \"parent\" : \"{{child3Inode}}\"\n}\n\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/categories", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Get categories by filter AND including Children", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});", + "", + "pm.expect(jsonData.entity.length).to.have.equals(4);", + "", + "pm.test('get the right categories', () => { ", + " let data = [];", + "", + " for (let i = 0; i < jsonData.entity.length; i++)", + " { ", + " data.push(jsonData.entity[i]['categoryName']); ", + " }", + "", + " data.includes(\"PostmanTest Top level Category\");", + " data.includes(\"PostmanTest Child 1\");", + " data.includes(\"PostmanTest Child 2\");", + " data.includes(\"PostmanTest Grandchild 1\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=PostmanTest&allLevels=true", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "PostmanTest" + }, + { + "key": "allLevels", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "Sorting top level | Get categories by filter AND not including Children", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code should be ok 200\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "var jsonData = pm.response.json();", + "", + "pm.test(\"Fetch successfully without errors\", function () {", + " pm.expect(jsonData.errors).to.have.lengthOf(0);", + "});", + "", + "pm.expect(jsonData.entity.length).to.have.equals(1);", + "", + "pm.test('get the right categories', () => { ", + " let data = [];", + "", + " for (let i = 0; i < jsonData.entity.length; i++)", + " { ", + " data.push(jsonData.entity[i]['categoryName']); ", + " }", + "", + " data.includes(\"PostmanTest Top level Category\");", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "admin", + "type": "string" + }, + { + "key": "username", + "value": "admin@dotcms.com", + "type": "string" + }, + { + "key": "saveHelperData", + "type": "any" + }, + { + "key": "showPassword", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{serverURL}}/api/v1/categories?filter=PostmanTest", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "categories" + ], + "query": [ + { + "key": "filter", + "value": "PostmanTest" + } + ] + } + }, + "response": [] + }, { "name": "Create New Category without parent", "event": [ @@ -4807,6 +5353,14 @@ { "key": "categoryKey", "value": "" + }, + { + "key": "topCategoryInode", + "value": "" + }, + { + "key": "child3Inode", + "value": "" } ] } \ No newline at end of file