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
// =========================================================================