From 4969ece53bfce919407ef0ccfd05c78acc72a48b Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Mon, 26 Oct 2026 11:01:26 +0100 Subject: [PATCH] Let categories and skins stay off the website catalog catalog: false on a category or a skin set leaves it out of the catalog sync to the website, whose /catalog is public. Hidden content, such as a secret class's skins, can then live in a permission-gated category without being listed for everyone. Co-Authored-By: Claude Opus 5.5 --- .../armourshop/objects/SkinCategory.java | 8 ++++++++ .../tfminecraft/armourshop/objects/SkinSet.java | 7 +++++++ .../pack/catalog/CatalogSyncService.java | 4 ++-- .../loaders/ConfigurationModelsTest.java | 9 ++++++++- .../pack/catalog/CatalogSyncServiceTest.java | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/tfminecraft/armourshop/objects/SkinCategory.java b/src/main/java/net/tfminecraft/armourshop/objects/SkinCategory.java index b985876..d59d6fc 100644 --- a/src/main/java/net/tfminecraft/armourshop/objects/SkinCategory.java +++ b/src/main/java/net/tfminecraft/armourshop/objects/SkinCategory.java @@ -15,6 +15,7 @@ public class SkinCategory { private boolean isItem; private List sets = new ArrayList<>(); private Optional permission = Optional.empty(); + private boolean hiddenFromCatalog; public SkinCategory(String key, ConfigurationSection config) { this.id = key; @@ -24,6 +25,7 @@ public SkinCategory(String key, ConfigurationSection config) { this.permission = Optional.of(config.getString("permission")); } isItem = config.getBoolean("is-item", false); + hiddenFromCatalog = !config.getBoolean("catalog", true); } public String getId() { @@ -49,6 +51,11 @@ public boolean hasPermission() { return permission.isPresent(); } + /** catalog: false keeps the category off the website catalog sync, e.g. a hidden class's skins. */ + public boolean isHiddenFromCatalog() { + return hiddenFromCatalog; + } + public List getSets(){ return sets; } @@ -63,5 +70,6 @@ public SkinCategory(SkinCategory another) { this.item = another.getItem(); this.permission = another.permission; this.isItem = another.isItem(); + this.hiddenFromCatalog = another.hiddenFromCatalog; } } diff --git a/src/main/java/net/tfminecraft/armourshop/objects/SkinSet.java b/src/main/java/net/tfminecraft/armourshop/objects/SkinSet.java index 6c97996..afbccad 100644 --- a/src/main/java/net/tfminecraft/armourshop/objects/SkinSet.java +++ b/src/main/java/net/tfminecraft/armourshop/objects/SkinSet.java @@ -26,6 +26,7 @@ public class SkinSet { private Optional item = Optional.empty(); private boolean addName; private String permission; + private boolean hiddenFromCatalog; public SkinSet(String key, ConfigurationSection config) { this.id = key; @@ -59,6 +60,12 @@ public SkinSet(String key, ConfigurationSection config) { addName = false; } permission = config.getString("permission", "none"); + hiddenFromCatalog = !config.getBoolean("catalog", true); + } + + /** catalog: false keeps the skin off the website catalog sync. */ + public boolean isHiddenFromCatalog() { + return hiddenFromCatalog; } public boolean hasPermission() { diff --git a/src/main/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncService.java b/src/main/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncService.java index 6f1fd0f..d92ea29 100644 --- a/src/main/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncService.java +++ b/src/main/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncService.java @@ -33,7 +33,7 @@ public static String buildPayloadJson() { sb.append("{\"categories\":["); boolean firstCat = true; for (SkinCategory cat : CategoryLoader.get()) { - if (cat == null || cat.getId() == null || cat.getId().isBlank()) { + if (cat == null || cat.getId() == null || cat.getId().isBlank() || cat.isHiddenFromCatalog()) { continue; } if (!firstCat) { @@ -50,7 +50,7 @@ public static String buildPayloadJson() { List sets = cat.getSets(); if (sets != null) { for (SkinSet set : sets) { - if (set == null || set.getId() == null || set.getId().isBlank()) { + if (set == null || set.getId() == null || set.getId().isBlank() || set.isHiddenFromCatalog()) { continue; } if (!firstSet) { diff --git a/src/test/java/net/tfminecraft/armourshop/loaders/ConfigurationModelsTest.java b/src/test/java/net/tfminecraft/armourshop/loaders/ConfigurationModelsTest.java index 4168540..e5f584f 100644 --- a/src/test/java/net/tfminecraft/armourshop/loaders/ConfigurationModelsTest.java +++ b/src/test/java/net/tfminecraft/armourshop/loaders/ConfigurationModelsTest.java @@ -284,7 +284,14 @@ ItemStack crafted(String metal) { @Test void copyingPublicCategoriesPreservesTheirLackOfPermission() { var config = new YamlConfiguration(); config.set("name", "Public"); config.set("item", "v.STONE"); SkinCategory copy = assertDoesNotThrow(() -> new SkinCategory(new SkinCategory("public", config))); - assertFalse(copy.hasPermission()); assertEquals("Public", copy.getName()); + assertFalse(copy.hasPermission()); assertEquals("Public", copy.getName()); assertFalse(copy.isHiddenFromCatalog()); + assertFalse(new SkinSet("public", config).isHiddenFromCatalog()); + } + + @Test void catalogFalseHidesCategoriesAndSkinsFromTheCatalog() { + var config = new YamlConfiguration(); config.set("name", "Hidden"); config.set("catalog", false); + assertTrue(new SkinCategory(new SkinCategory("hidden", config)).isHiddenFromCatalog()); + assertTrue(new SkinSet("hidden", config).isHiddenFromCatalog()); } @Test void pieceSuffixesAreIndependentOfServerLocale() { diff --git a/src/test/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncServiceTest.java b/src/test/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncServiceTest.java index be0ef5e..8ac3212 100644 --- a/src/test/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncServiceTest.java +++ b/src/test/java/net/tfminecraft/armourshop/pack/catalog/CatalogSyncServiceTest.java @@ -49,6 +49,20 @@ private static PermissionGroupDefinition group(String id, String permission, Str } private static JsonObject payload() { return JsonParser.parseString(CatalogSyncService.buildPayloadJson()).getAsJsonObject(); } + @Test void categoriesAndSkinsHiddenFromTheCatalogAreLeftOut() { + var hiddenSkin = skin("secret"); when(hiddenSkin.isHiddenFromCatalog()).thenReturn(true); + var inside = skin("inside"); + var shown = skin("shown"); + var hiddenCategory = category("hidden", "Hidden", false, List.of(inside)); + when(hiddenCategory.isHiddenFromCatalog()).thenReturn(true); + var armor = category("armor", "Armor", false, List.of(shown, hiddenSkin)); + categories.when(CategoryLoader::get).thenReturn(List.of(hiddenCategory, armor)); + var listed = payload().getAsJsonArray("categories"); + assertEquals(1, listed.size()); + assertEquals("armor", listed.get(0).getAsJsonObject().get("id").getAsString()); + assertEquals(JsonParser.parseString("[\"shown\"]"), listed.get(0).getAsJsonObject().get("skin_sets")); + } + @Test void serializesCategoriesScrollsAndEntitlementsWhileSkippingInvalidRows() { var rows = Arrays.asList(null, category(null, "invalid", false, null), category(" ", "invalid", false, null), category("armor", "§aArmor", false, Arrays.asList(null, skin(null), skin(" "), skin("first"), skin("second"))),