diff --git a/pom.xml b/pom.xml index 3cd72d1..ef8b554 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ -LOCAL - 1.26.2 + 1.26.3 BentoBoxWorld_AOneBlock bentobox-world diff --git a/src/main/java/world/bentobox/aoneblock/panels/PhasesPanel.java b/src/main/java/world/bentobox/aoneblock/panels/PhasesPanel.java index f4ed70b..ba84b01 100644 --- a/src/main/java/world/bentobox/aoneblock/panels/PhasesPanel.java +++ b/src/main/java/world/bentobox/aoneblock/panels/PhasesPanel.java @@ -539,6 +539,10 @@ private boolean canApplyPhase(OneBlockPhase phase) { return false; } + if (!this.hasSetCountPermission()) + { + return false; + } if (phase.getBlockNumberValue() >= this.oneBlockIsland.getLifetime()) { return false; @@ -546,6 +550,19 @@ private boolean canApplyPhase(OneBlockPhase phase) return !this.phaseRequirementsFail(phase, this.oneBlockIsland); } + /** + * Checks that the player holds the permission of the setcount command that a phase + * click runs, so the "click to change" action is only offered when it can succeed. + */ + private boolean hasSetCountPermission() + { + String label = this.addon.getSettings().getSetCountCommand().split(" ")[0]; + return this.addon.getPlayerCommand() + .flatMap(mainCommand -> mainCommand.getSubCommand(label)) + .map(subCommand -> this.user.hasPermission(subCommand.getPermission())) + .orElse(true); + } + /** * Builds the click handler for a phase button. */ diff --git a/src/test/java/world/bentobox/aoneblock/panels/PhasesPanelTest.java b/src/test/java/world/bentobox/aoneblock/panels/PhasesPanelTest.java index 7722fa7..850b1f5 100644 --- a/src/test/java/world/bentobox/aoneblock/panels/PhasesPanelTest.java +++ b/src/test/java/world/bentobox/aoneblock/panels/PhasesPanelTest.java @@ -42,6 +42,7 @@ import world.bentobox.bank.Bank; import world.bentobox.bank.BankManager; import world.bentobox.bank.data.Money; +import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.panels.TemplatedPanel; import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; import world.bentobox.bentobox.api.user.User; @@ -554,6 +555,84 @@ void testCanApplyPhaseSuccess() throws Exception { assertTrue(result); } + /** + * Test canApplyPhase returns false when the player lacks the setcount command + * permission — the "click to change" action must not be offered. + */ + @Test + void testCanApplyPhaseNoSetCountPermission() throws Exception { + setUpAddonMocks(); + Island testIsland = new Island(); + testIsland.setOwner(uuid); + OneBlockIslands oneBlockIsland = new OneBlockIslands(testIsland.getUniqueId()); + oneBlockIsland.setLifetime(100L); + oneBlockIsland.setLastPhaseChangeTime(System.currentTimeMillis()); + + User user = User.getInstance(mockPlayer); + when(im.getIsland(world, user)).thenReturn(testIsland); + when(blockListener.getIsland(testIsland)).thenReturn(oneBlockIsland); + when(oneBlockManager.getBlockProbs()).thenReturn(createBlockProbs()); + + // The setcount command exists and carries a permission the player does not have + CompositeCommand mainCommand = mock(CompositeCommand.class); + CompositeCommand setCountCommand = mock(CompositeCommand.class); + when(mainCommand.getSubCommand("setcount")).thenReturn(Optional.of(setCountCommand)); + when(setCountCommand.getPermission()).thenReturn("aoneblock.island.setcount"); + when(addon.getPlayerCommand()).thenReturn(Optional.of(mainCommand)); + when(mockPlayer.hasPermission("aoneblock.island.setcount")).thenReturn(false); + + Constructor constructor = PhasesPanel.class.getDeclaredConstructor(AOneBlock.class, World.class, User.class); + constructor.setAccessible(true); + panel = constructor.newInstance(addon, world, user); + + OneBlockPhase phase = createTestPhase("Plains"); + + Method method = PhasesPanel.class.getDeclaredMethod("canApplyPhase", OneBlockPhase.class); + method.setAccessible(true); + + boolean result = (boolean) method.invoke(panel, phase); + + assertFalse(result); + } + + /** + * Test canApplyPhase returns true when the player holds the setcount permission. + */ + @Test + void testCanApplyPhaseWithSetCountPermission() throws Exception { + setUpAddonMocks(); + Island testIsland = new Island(); + testIsland.setOwner(uuid); + OneBlockIslands oneBlockIsland = new OneBlockIslands(testIsland.getUniqueId()); + oneBlockIsland.setLifetime(100L); + oneBlockIsland.setLastPhaseChangeTime(System.currentTimeMillis()); + + User user = User.getInstance(mockPlayer); + when(im.getIsland(world, user)).thenReturn(testIsland); + when(blockListener.getIsland(testIsland)).thenReturn(oneBlockIsland); + when(oneBlockManager.getBlockProbs()).thenReturn(createBlockProbs()); + + CompositeCommand mainCommand = mock(CompositeCommand.class); + CompositeCommand setCountCommand = mock(CompositeCommand.class); + when(mainCommand.getSubCommand("setcount")).thenReturn(Optional.of(setCountCommand)); + when(setCountCommand.getPermission()).thenReturn("aoneblock.island.setcount"); + when(addon.getPlayerCommand()).thenReturn(Optional.of(mainCommand)); + when(mockPlayer.hasPermission("aoneblock.island.setcount")).thenReturn(true); + + Constructor constructor = PhasesPanel.class.getDeclaredConstructor(AOneBlock.class, World.class, User.class); + constructor.setAccessible(true); + panel = constructor.newInstance(addon, world, user); + + OneBlockPhase phase = createTestPhase("Plains"); + + Method method = PhasesPanel.class.getDeclaredMethod("canApplyPhase", OneBlockPhase.class); + method.setAccessible(true); + + boolean result = (boolean) method.invoke(panel, phase); + + assertTrue(result); + } + // ========================================================================= // Test phaseRequirementsFail // =========================================================================