diff --git a/commafeed-client/src/pages/app/CategoryDetailsPage.tsx b/commafeed-client/src/pages/app/CategoryDetailsPage.tsx index ec91d82c..b3cebc29 100644 --- a/commafeed-client/src/pages/app/CategoryDetailsPage.tsx +++ b/commafeed-client/src/pages/app/CategoryDetailsPage.tsx @@ -37,6 +37,9 @@ export function CategoryDetailsPage() { category = query.result && flattenCategoryTree(query.result.data).find(c => c.id === id) } + // a category cannot be moved under itself or one of its own descendants + const excludedCategoryIds = category ? flattenCategoryTree(category).map(c => c.id) : [id] + const form = useForm() const { setValues } = form @@ -125,7 +128,7 @@ export function CategoryDetailsPage() { label={Parent Category} {...form.getInputProps("parentId")} clearable - withoutCategoryIds={[id]} + withoutCategoryIds={excludedCategoryIds} /> Position} {...form.getInputProps("position")} required min={0} /> diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/resource/CategoryREST.java b/commafeed-server/src/main/java/com/commafeed/frontend/resource/CategoryREST.java index 668d8ac1..62c67dca 100644 --- a/commafeed-server/src/main/java/com/commafeed/frontend/resource/CategoryREST.java +++ b/commafeed-server/src/main/java/com/commafeed/frontend/resource/CategoryREST.java @@ -435,6 +435,19 @@ public class CategoryREST { && !Strings.CS.equals(req.getParentId(), String.valueOf(req.getId()))) { parent = feedCategoryDAO.findById(user, Long.valueOf(req.getParentId())); } + if (parent != null) { + Long parentId = parent.getId(); + List descendants = + feedCategoryDAO.findAllChildrenCategories(user, category); + boolean parentIsDescendant = + descendants.stream().anyMatch(c -> c.getId().equals(parentId)); + if (parentIsDescendant) { + return Response.status(Status.BAD_REQUEST) + .entity( + "cannot set a category's own descendant as its parent") + .build(); + } + } category.setParent(parent); if (req.getPosition() != null) {