From d06fda6d9d2bc0faaccace8230748b768a96d467 Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 13 Jan 2017 20:11:25 +0000 Subject: [PATCH] Add /lp search command --- .../common/commands/CommandManager.java | 4 +- .../common/commands/misc/SearchCommand.java | 109 ++++++++++++++++++ .../luckperms/common/commands/utils/Util.java | 109 +++++++++++++++++- .../luckperms/common/constants/Message.java | 7 ++ .../common/constants/Permission.java | 1 + .../common/storage/holder/HeldPermission.java | 3 + .../storage/holder/NodeHeldPermission.java | 5 + default-lang.yml | 7 ++ 8 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java b/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java index f7715505..86b30063 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/CommandManager.java @@ -37,6 +37,7 @@ import me.lucko.luckperms.common.commands.misc.ExportCommand; import me.lucko.luckperms.common.commands.misc.ImportCommand; import me.lucko.luckperms.common.commands.misc.InfoCommand; import me.lucko.luckperms.common.commands.misc.NetworkSyncCommand; +import me.lucko.luckperms.common.commands.misc.SearchCommand; import me.lucko.luckperms.common.commands.misc.SyncCommand; import me.lucko.luckperms.common.commands.misc.VerboseCommand; import me.lucko.luckperms.common.commands.sender.Sender; @@ -84,9 +85,10 @@ public class CommandManager { .addAll(plugin.getExtraCommands()) .add(new LogMainCommand()) .add(new SyncCommand()) - .add(new NetworkSyncCommand()) .add(new InfoCommand()) .add(new VerboseCommand()) + .add(new SearchCommand()) + .add(new NetworkSyncCommand()) .add(new ImportCommand()) .add(new ExportCommand()) .add(new MigrationMainCommand()) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java new file mode 100644 index 00000000..669f70e7 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.commands.misc; + +import me.lucko.luckperms.common.LuckPermsPlugin; +import me.lucko.luckperms.common.commands.Arg; +import me.lucko.luckperms.common.commands.CommandException; +import me.lucko.luckperms.common.commands.CommandResult; +import me.lucko.luckperms.common.commands.SingleCommand; +import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.commands.utils.Util; +import me.lucko.luckperms.common.constants.Message; +import me.lucko.luckperms.common.constants.Permission; +import me.lucko.luckperms.common.storage.holder.HeldPermission; +import me.lucko.luckperms.common.utils.Predicates; + +import io.github.mkremins.fanciful.FancyMessage; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; + +public class SearchCommand extends SingleCommand { + public SearchCommand() { + super("Search", "Search for users/groups with a specific permission", + "/%s search ", Permission.SEARCH, Predicates.notInRange(1, 2), + Arg.list( + Arg.create("permission", true, "the permission to search for"), + Arg.create("page", false, "the page to view") + ) + ); + } + + @Override + public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List args, String label) throws CommandException { + String query = args.get(0); + + int page = 1; + if (args.size() > 0) { + try { + page = Integer.parseInt(args.get(0)); + } catch (NumberFormatException e) { + // ignored + } + } + + Message.SEARCH_SEARCHING.send(sender, query); + + List> matchedUsers = plugin.getStorage().getUsersWithPermission(query).join(); + List> matchedGroups = plugin.getStorage().getGroupsWithPermission(query).join(); + + int users = matchedUsers.size(); + int groups = matchedGroups.size(); + + Message.SEARCH_RESULT.send(sender, users + groups, users, groups); + + Map uuidLookups = new HashMap<>(); + Function lookupFunc = uuid -> uuidLookups.computeIfAbsent(uuid, u -> { + String s = plugin.getStorage().getName(u).join(); + if (s == null) { + s = "null"; + } + return s; + }); + + Map.Entry msgUsers = Util.searchUserResultToMessage(matchedUsers, lookupFunc, label, page); + Map.Entry msgGroups = Util.searchGroupResultToMessage(matchedGroups, label, page); + + if (msgUsers.getValue() != null) { + Message.SEARCH_SHOWING_USERS_WITH_PAGE.send(sender, msgUsers.getValue()); + sender.sendMessage(msgUsers.getKey()); + } else { + Message.SEARCH_SHOWING_USERS.send(sender); + sender.sendMessage(msgUsers.getKey()); + } + + if (msgGroups.getValue() != null) { + Message.SEARCH_SHOWING_GROUPS_WITH_PAGE.send(sender, msgGroups.getValue()); + sender.sendMessage(msgGroups.getKey()); + } else { + Message.SEARCH_SHOWING_GROUPS.send(sender); + sender.sendMessage(msgGroups.getKey()); + } + + return CommandResult.SUCCESS; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/utils/Util.java b/common/src/main/java/me/lucko/luckperms/common/commands/utils/Util.java index d615add9..8da43b0a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/utils/Util.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/utils/Util.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.common.constants.Message; import me.lucko.luckperms.common.constants.Patterns; import me.lucko.luckperms.common.core.model.PermissionHolder; import me.lucko.luckperms.common.core.model.User; +import me.lucko.luckperms.common.storage.holder.HeldPermission; import me.lucko.luckperms.common.utils.DateUtil; import io.github.mkremins.fanciful.ChatColor; @@ -48,6 +49,8 @@ import java.util.ListIterator; import java.util.Map; import java.util.SortedSet; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; @UtilityClass public class Util { @@ -150,6 +153,16 @@ public class Util { return message; } + public static FancyMessage appendNodeExpiry(Node node, FancyMessage message) { + if (node.isTemporary()) { + message = message.then(" (").color(ChatColor.getByChar('8')); + message = message.then("expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime())).color(ChatColor.getByChar('7')); + message = message.then(")").color(ChatColor.getByChar('8')); + } + + return message; + } + public static String contextToString(String key, String value) { return "&8(&7" + key + "=&f" + value + "&8)"; } @@ -199,6 +212,29 @@ public class Util { return message; } + private static FancyMessage makeFancy(String holderName, boolean group, String label, HeldPermission perm, FancyMessage message) { + Node node = perm.asNode(); + + message = message.formattedTooltip( + new FancyMessage("> ") + .color(ChatColor.getByChar('3')) + .then(node.getPermission()) + .color(node.getValue() ? ChatColor.getByChar('a') : ChatColor.getByChar('c')), + new FancyMessage(" "), + new FancyMessage("Click to remove this node from " + holderName).color(ChatColor.getByChar('7')) + ); + + String command = ExportCommand.nodeToString(node, group ? holderName : holderName, group) + .replace("/luckperms", "/" + label) + .replace("set", "unset") + .replace("add", "remove") + .replace(" true", "") + .replace(" false", ""); + + message = message.suggest(command); + return message; + } + public static Map.Entry permNodesToMessage(SortedSet nodes, PermissionHolder holder, String label, int pageNumber) { List l = new ArrayList<>(); for (Node node : nodes) { @@ -227,14 +263,83 @@ public class Util { for (Node node : page) { message = makeFancy(holder, label, node, message.then("> ").color(ChatColor.getByChar('3'))); message = makeFancy(holder, label, node, message.then(Util.color(node.getPermission())).color(node.getValue() ? ChatColor.getByChar('a') : ChatColor.getByChar('c'))); - message = makeFancy(holder, label, node, appendNodeContextDescription(node, message)); + message = appendNodeContextDescription(node, message); message = message.then("\n"); } return Maps.immutableEntry(message, title); } - private static List> divideList(List source, int size) { + public static Map.Entry searchUserResultToMessage(List> results, Function uuidLookup, String label, int pageNumber) { + if (results.isEmpty()) { + return Maps.immutableEntry(new FancyMessage("None").color(ChatColor.getByChar('3')), null); + } + + List> sorted = new ArrayList<>(results); + sorted.sort(Comparator.comparing(HeldPermission::getHolder)); + + int index = pageNumber - 1; + List>> pages = divideList(sorted, 15); + + if ((index < 0 || index >= pages.size())) { + pageNumber = 1; + index = 0; + } + + List> page = pages.get(index); + List>> uuidMappedPage = page.stream().map(hp -> Maps.immutableEntry(uuidLookup.apply(hp.getHolder()), hp)).collect(Collectors.toList()); + + FancyMessage message = new FancyMessage(""); + String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; + + for (Map.Entry> ent : uuidMappedPage) { + message = makeFancy(ent.getKey(), false, label, ent.getValue(), message.then("> ").color(ChatColor.getByChar('3'))); + message = makeFancy(ent.getKey(), false, label, ent.getValue(), message.then(ent.getKey()).color(ChatColor.getByChar('b'))); + message = makeFancy(ent.getKey(), false, label, ent.getValue(), message.then(" - ").color(ChatColor.getByChar('7'))); + message = makeFancy(ent.getKey(), false, label, ent.getValue(), message.then("" + ent.getValue().getValue()).color(ent.getValue().getValue() ? ChatColor.getByChar('a') : ChatColor.getByChar('c'))); + message = appendNodeExpiry(ent.getValue().asNode(), message); + message = appendNodeContextDescription(ent.getValue().asNode(), message); + message = message.then("\n"); + } + + return Maps.immutableEntry(message, title); + } + + public static Map.Entry searchGroupResultToMessage(List> results, String label, int pageNumber) { + if (results.isEmpty()) { + return Maps.immutableEntry(new FancyMessage("None").color(ChatColor.getByChar('3')), null); + } + + List> sorted = new ArrayList<>(results); + sorted.sort(Comparator.comparing(HeldPermission::getHolder)); + + int index = pageNumber - 1; + List>> pages = divideList(sorted, 15); + + if ((index < 0 || index >= pages.size())) { + pageNumber = 1; + index = 0; + } + + List> page = pages.get(index); + + FancyMessage message = new FancyMessage(""); + String title = "&7(page &f" + pageNumber + "&7 of &f" + pages.size() + "&7 - &f" + sorted.size() + "&7 entries)"; + + for (HeldPermission ent : page) { + message = makeFancy(ent.getHolder(), true, label, ent, message.then("> ").color(ChatColor.getByChar('3'))); + message = makeFancy(ent.getHolder(), true, label, ent, message.then(ent.getHolder()).color(ChatColor.getByChar('b'))); + message = makeFancy(ent.getHolder(), true, label, ent, message.then(" - ").color(ChatColor.getByChar('7'))); + message = makeFancy(ent.getHolder(), true, label, ent, message.then("" + ent.getValue()).color(ent.getValue() ? ChatColor.getByChar('a') : ChatColor.getByChar('c'))); + message = appendNodeExpiry(ent.asNode(), message); + message = appendNodeContextDescription(ent.asNode(), message); + message = message.then("\n"); + } + + return Maps.immutableEntry(message, title); + } + + public static List> divideList(List source, int size) { List> lists = new ArrayList<>(); Iterator it = source.iterator(); while (it.hasNext()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/constants/Message.java b/common/src/main/java/me/lucko/luckperms/common/constants/Message.java index def0a64b..7f6b4ecd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/constants/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/constants/Message.java @@ -92,6 +92,13 @@ public enum Message { VERBOSE_RECORDING_UPLOAD_START("&bVerbose recording was disabled. Uploading results...", true), VERBOSE_RECORDING_URL("&aVerbose results URL: {0}", true), + SEARCH_SEARCHING("&aSearching for users and groups with &b{0}&a...", true), + SEARCH_RESULT("&aFound &b{0}&a entries from &b{1}&a users and &b{2}&a groups.", true), + SEARCH_SHOWING_USERS("&bShowing user entries:", true), + SEARCH_SHOWING_GROUPS("&bShowing group entries:", true), + SEARCH_SHOWING_USERS_WITH_PAGE("&bShowing user entries: {0}", true), + SEARCH_SHOWING_GROUPS_WITH_PAGE("&bShowing group entries: {0}", true), + CREATE_SUCCESS("&b{0}&a was successfully created.", true), DELETE_SUCCESS("&b{0}&a was successfully deleted.", true), RENAME_SUCCESS("&b{0}&a was successfully renamed to &b{1}&a.", true), diff --git a/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java b/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java index 0f138212..bff93d00 100644 --- a/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java +++ b/common/src/main/java/me/lucko/luckperms/common/constants/Permission.java @@ -37,6 +37,7 @@ public enum Permission { SYNC(list("sync"), Type.NONE), INFO(list("info"), Type.NONE), + SEARCH(list("search"), Type.NONE), VERBOSE(list("verbose"), Type.NONE), IMPORT(list("import"), Type.NONE), diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/holder/HeldPermission.java b/common/src/main/java/me/lucko/luckperms/common/storage/holder/HeldPermission.java index 3f2a3899..afc0108c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/holder/HeldPermission.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/holder/HeldPermission.java @@ -24,6 +24,8 @@ package me.lucko.luckperms.common.storage.holder; import com.google.common.collect.Multimap; +import me.lucko.luckperms.api.Node; + import java.util.Optional; import java.util.OptionalLong; @@ -36,5 +38,6 @@ public interface HeldPermission { Optional getWorld(); OptionalLong getExpiry(); Multimap getContext(); + Node asNode(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/holder/NodeHeldPermission.java b/common/src/main/java/me/lucko/luckperms/common/storage/holder/NodeHeldPermission.java index 46798335..08c85161 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/holder/NodeHeldPermission.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/holder/NodeHeldPermission.java @@ -74,4 +74,9 @@ public class NodeHeldPermission implements HeldPermission { public Multimap getContext() { return node.getContexts().toMultimap(); } + + @Override + public Node asNode() { + return node; + } } diff --git a/default-lang.yml b/default-lang.yml index 376565b3..3a20f73f 100644 --- a/default-lang.yml +++ b/default-lang.yml @@ -54,6 +54,13 @@ verbose-recording-on-query: "&bVerbose recording set to &aTRUE &bfor permissions verbose-recording-upload-start: "&bVerbose recording was disabled. Uploading results..." verbose-recording-url: "&aVerbose results URL: {0}" +search-searching: "&aSearching for users and groups with &b{0}&a..." +search-result: "&aFound &b{0}&a entries from &b{1}&a users and &b{2}&a groups." +search-showing-users: "&bShowing user entries:" +search-showing-groups: "&bShowing group entries:" +search-showing-users-with-page: "&bShowing user entries: {0}" +search-showing-groups-with-page: "&bShowing group entries: {0}" + create-success: "&b{0}&a was successfully created." delete-success: "&b{0}&a was successfully deleted." rename-success: "&b{0}&a was successfully renamed to &b{1}&a."