package fr.xephi.authme.command.help; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import fr.xephi.authme.command.CommandArgumentDescription; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandPermissions; import fr.xephi.authme.command.CommandUtils; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.util.CollectionUtils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import javax.inject.Inject; import java.util.ArrayList; import java.util.List; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; /** * Help syntax generator for AuthMe commands. */ public class HelpProvider { // --- Bit flags --- /** Set to not show the command. */ public static final int HIDE_COMMAND = 0x001; /** Set to show the detailed description of a command. */ public static final int SHOW_LONG_DESCRIPTION = 0x002; /** Set to include the arguments the command takes. */ public static final int SHOW_ARGUMENTS = 0x004; /** Set to show the permissions required to execute the command. */ public static final int SHOW_PERMISSIONS = 0x008; /** Set to show alternative labels for the command. */ public static final int SHOW_ALTERNATIVES = 0x010; /** Set to show the child commands of the command. */ public static final int SHOW_CHILDREN = 0x020; /** Shortcut for setting all options apart from {@link HelpProvider#HIDE_COMMAND}. */ public static final int ALL_OPTIONS = ~HIDE_COMMAND; private final PermissionsManager permissionsManager; private final String helpHeader; @Inject public HelpProvider(PermissionsManager permissionsManager, NewSetting settings) { this.permissionsManager = permissionsManager; this.helpHeader = settings.getProperty(PluginSettings.HELP_HEADER); } public List printHelp(CommandSender sender, FoundCommandResult result, int options) { if (result.getCommandDescription() == null) { return singletonList(ChatColor.DARK_RED + "Failed to retrieve any help information!"); } List lines = new ArrayList<>(); lines.add(ChatColor.GOLD + "==========[ " + helpHeader + " HELP ]=========="); CommandDescription command = result.getCommandDescription(); List labels = ImmutableList.copyOf(result.getLabels()); List correctLabels = ImmutableList.copyOf(filterCorrectLabels(command, labels)); if (!hasFlag(HIDE_COMMAND, options)) { lines.add(ChatColor.GOLD + "Command: " + CommandSyntaxHelper.getSyntax(command, correctLabels)); } if (hasFlag(SHOW_LONG_DESCRIPTION, options)) { printDetailedDescription(command, lines); } if (hasFlag(SHOW_ARGUMENTS, options)) { printArguments(command, lines); } if (hasFlag(SHOW_PERMISSIONS, options) && sender != null) { printPermissions(command, sender, permissionsManager, lines); } if (hasFlag(SHOW_ALTERNATIVES, options)) { printAlternatives(command, correctLabels, lines); } if (hasFlag(SHOW_CHILDREN, options)) { printChildren(command, labels, lines); } return lines; } private static void printDetailedDescription(CommandDescription command, List lines) { lines.add(ChatColor.GOLD + "Short description: " + ChatColor.WHITE + command.getDescription()); lines.add(ChatColor.GOLD + "Detailed description:"); lines.add(ChatColor.WHITE + " " + command.getDetailedDescription()); } private static void printArguments(CommandDescription command, List lines) { if (command.getArguments().isEmpty()) { return; } lines.add(ChatColor.GOLD + "Arguments:"); StringBuilder argString = new StringBuilder(); for (CommandArgumentDescription argument : command.getArguments()) { argString.setLength(0); argString.append(" ").append(ChatColor.YELLOW).append(ChatColor.ITALIC).append(argument.getName()) .append(": ").append(ChatColor.WHITE).append(argument.getDescription()); if (argument.isOptional()) { argString.append(ChatColor.GRAY).append(ChatColor.ITALIC).append(" (Optional)"); } lines.add(argString.toString()); } } private static void printAlternatives(CommandDescription command, List correctLabels, List lines) { // TODO ljacqu 20151219: Need to show alternatives for base labels too? E.g. /r for /register if (command.getLabels().size() <= 1 || correctLabels.size() <= 1) { return; } lines.add(ChatColor.GOLD + "Alternatives:"); // Get the label used final String parentLabel = correctLabels.get(0); final String childLabel = correctLabels.get(1); // Create a list of alternatives for (String entry : command.getLabels()) { if (!entry.equalsIgnoreCase(childLabel)) { lines.add(" " + CommandSyntaxHelper.getSyntax(command, asList(parentLabel, entry))); } } } private static void printPermissions(CommandDescription command, CommandSender sender, PermissionsManager permissionsManager, List lines) { CommandPermissions permissions = command.getCommandPermissions(); // TODO ljacqu 20151224: Isn't it possible to have a default permission but no permission nodes? if (permissions == null || CollectionUtils.isEmpty(permissions.getPermissionNodes())) { return; } lines.add(ChatColor.GOLD + "Permissions:"); for (PermissionNode node : permissions.getPermissionNodes()) { boolean hasPermission = permissionsManager.hasPermission(sender, node); final String nodePermsString = "" + ChatColor.GRAY + ChatColor.ITALIC + (hasPermission ? " (You have permission)" : " (No permission)"); lines.add(" " + ChatColor.YELLOW + ChatColor.ITALIC + node.getNode() + nodePermsString); } // Addendum to the line to specify whether the sender has permission or not when default is OP_ONLY final DefaultPermission defaultPermission = permissions.getDefaultPermission(); String addendum = ""; if (DefaultPermission.OP_ONLY.equals(defaultPermission)) { addendum = PermissionsManager.evaluateDefaultPermission(defaultPermission, sender) ? " (You have permission)" : " (No permission)"; } lines.add(ChatColor.GOLD + "Default: " + ChatColor.GRAY + ChatColor.ITALIC + defaultPermission.getTitle() + addendum); // Evaluate if the sender has permission to the command if (permissionsManager.hasPermission(sender, command)) { lines.add(ChatColor.GOLD + " Result: " + ChatColor.GREEN + ChatColor.ITALIC + "You have permission"); } else { lines.add(ChatColor.GOLD + " Result: " + ChatColor.DARK_RED + ChatColor.ITALIC + "No permission"); } } private static void printChildren(CommandDescription command, List parentLabels, List lines) { if (command.getChildren().isEmpty()) { return; } lines.add(ChatColor.GOLD + "Commands:"); String parentCommandPath = CommandUtils.labelsToString(parentLabels); for (CommandDescription child : command.getChildren()) { lines.add(" /" + parentCommandPath + " " + child.getLabels().get(0) + ChatColor.GRAY + ChatColor.ITALIC + ": " + child.getDescription()); } } private static boolean hasFlag(int flag, int options) { return (flag & options) != 0; } @VisibleForTesting protected static List filterCorrectLabels(CommandDescription command, List labels) { List commands = CommandUtils.constructParentList(command); List correctLabels = new ArrayList<>(); boolean foundIncorrectLabel = false; for (int i = 0; i < commands.size(); ++i) { if (!foundIncorrectLabel && i < labels.size() && commands.get(i).hasLabel(labels.get(i))) { correctLabels.add(labels.get(i)); } else { foundIncorrectLabel = true; correctLabels.add(commands.get(i).getLabels().get(0)); } } return correctLabels; } }