Add /lp search command

This commit is contained in:
Luck 2017-01-13 20:11:25 +00:00
parent 139010e75b
commit d06fda6d9d
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
8 changed files with 242 additions and 3 deletions

View File

@ -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.ImportCommand;
import me.lucko.luckperms.common.commands.misc.InfoCommand; import me.lucko.luckperms.common.commands.misc.InfoCommand;
import me.lucko.luckperms.common.commands.misc.NetworkSyncCommand; 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.SyncCommand;
import me.lucko.luckperms.common.commands.misc.VerboseCommand; import me.lucko.luckperms.common.commands.misc.VerboseCommand;
import me.lucko.luckperms.common.commands.sender.Sender; import me.lucko.luckperms.common.commands.sender.Sender;
@ -84,9 +85,10 @@ public class CommandManager {
.addAll(plugin.getExtraCommands()) .addAll(plugin.getExtraCommands())
.add(new LogMainCommand()) .add(new LogMainCommand())
.add(new SyncCommand()) .add(new SyncCommand())
.add(new NetworkSyncCommand())
.add(new InfoCommand()) .add(new InfoCommand())
.add(new VerboseCommand()) .add(new VerboseCommand())
.add(new SearchCommand())
.add(new NetworkSyncCommand())
.add(new ImportCommand()) .add(new ImportCommand())
.add(new ExportCommand()) .add(new ExportCommand())
.add(new MigrationMainCommand()) .add(new MigrationMainCommand())

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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>", 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<String> 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<HeldPermission<UUID>> matchedUsers = plugin.getStorage().getUsersWithPermission(query).join();
List<HeldPermission<String>> matchedGroups = plugin.getStorage().getGroupsWithPermission(query).join();
int users = matchedUsers.size();
int groups = matchedGroups.size();
Message.SEARCH_RESULT.send(sender, users + groups, users, groups);
Map<UUID, String> uuidLookups = new HashMap<>();
Function<UUID, String> lookupFunc = uuid -> uuidLookups.computeIfAbsent(uuid, u -> {
String s = plugin.getStorage().getName(u).join();
if (s == null) {
s = "null";
}
return s;
});
Map.Entry<FancyMessage, String> msgUsers = Util.searchUserResultToMessage(matchedUsers, lookupFunc, label, page);
Map.Entry<FancyMessage, String> 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;
}
}

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.common.constants.Message;
import me.lucko.luckperms.common.constants.Patterns; import me.lucko.luckperms.common.constants.Patterns;
import me.lucko.luckperms.common.core.model.PermissionHolder; import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User; import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.storage.holder.HeldPermission;
import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.DateUtil;
import io.github.mkremins.fanciful.ChatColor; import io.github.mkremins.fanciful.ChatColor;
@ -48,6 +49,8 @@ import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@UtilityClass @UtilityClass
public class Util { public class Util {
@ -150,6 +153,16 @@ public class Util {
return message; 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) { public static String contextToString(String key, String value) {
return "&8(&7" + key + "=&f" + value + "&8)"; return "&8(&7" + key + "=&f" + value + "&8)";
} }
@ -199,6 +212,29 @@ public class Util {
return message; 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<FancyMessage, String> permNodesToMessage(SortedSet<LocalizedNode> nodes, PermissionHolder holder, String label, int pageNumber) { public static Map.Entry<FancyMessage, String> permNodesToMessage(SortedSet<LocalizedNode> nodes, PermissionHolder holder, String label, int pageNumber) {
List<Node> l = new ArrayList<>(); List<Node> l = new ArrayList<>();
for (Node node : nodes) { for (Node node : nodes) {
@ -227,14 +263,83 @@ public class Util {
for (Node node : page) { for (Node node : page) {
message = makeFancy(holder, label, node, message.then("> ").color(ChatColor.getByChar('3'))); 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, 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"); message = message.then("\n");
} }
return Maps.immutableEntry(message, title); return Maps.immutableEntry(message, title);
} }
private static <T> List<List<T>> divideList(List<T> source, int size) { public static Map.Entry<FancyMessage, String> searchUserResultToMessage(List<HeldPermission<UUID>> results, Function<UUID, String> uuidLookup, String label, int pageNumber) {
if (results.isEmpty()) {
return Maps.immutableEntry(new FancyMessage("None").color(ChatColor.getByChar('3')), null);
}
List<HeldPermission<UUID>> sorted = new ArrayList<>(results);
sorted.sort(Comparator.comparing(HeldPermission::getHolder));
int index = pageNumber - 1;
List<List<HeldPermission<UUID>>> pages = divideList(sorted, 15);
if ((index < 0 || index >= pages.size())) {
pageNumber = 1;
index = 0;
}
List<HeldPermission<UUID>> page = pages.get(index);
List<Map.Entry<String, HeldPermission<UUID>>> 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<String, HeldPermission<UUID>> 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<FancyMessage, String> searchGroupResultToMessage(List<HeldPermission<String>> results, String label, int pageNumber) {
if (results.isEmpty()) {
return Maps.immutableEntry(new FancyMessage("None").color(ChatColor.getByChar('3')), null);
}
List<HeldPermission<String>> sorted = new ArrayList<>(results);
sorted.sort(Comparator.comparing(HeldPermission::getHolder));
int index = pageNumber - 1;
List<List<HeldPermission<String>>> pages = divideList(sorted, 15);
if ((index < 0 || index >= pages.size())) {
pageNumber = 1;
index = 0;
}
List<HeldPermission<String>> 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<String> 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 <T> List<List<T>> divideList(List<T> source, int size) {
List<List<T>> lists = new ArrayList<>(); List<List<T>> lists = new ArrayList<>();
Iterator<T> it = source.iterator(); Iterator<T> it = source.iterator();
while (it.hasNext()) { while (it.hasNext()) {

View File

@ -92,6 +92,13 @@ public enum Message {
VERBOSE_RECORDING_UPLOAD_START("&bVerbose recording was disabled. Uploading results...", true), VERBOSE_RECORDING_UPLOAD_START("&bVerbose recording was disabled. Uploading results...", true),
VERBOSE_RECORDING_URL("&aVerbose results URL: {0}", 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), CREATE_SUCCESS("&b{0}&a was successfully created.", true),
DELETE_SUCCESS("&b{0}&a was successfully deleted.", true), DELETE_SUCCESS("&b{0}&a was successfully deleted.", true),
RENAME_SUCCESS("&b{0}&a was successfully renamed to &b{1}&a.", true), RENAME_SUCCESS("&b{0}&a was successfully renamed to &b{1}&a.", true),

View File

@ -37,6 +37,7 @@ public enum Permission {
SYNC(list("sync"), Type.NONE), SYNC(list("sync"), Type.NONE),
INFO(list("info"), Type.NONE), INFO(list("info"), Type.NONE),
SEARCH(list("search"), Type.NONE),
VERBOSE(list("verbose"), Type.NONE), VERBOSE(list("verbose"), Type.NONE),
IMPORT(list("import"), Type.NONE), IMPORT(list("import"), Type.NONE),

View File

@ -24,6 +24,8 @@ package me.lucko.luckperms.common.storage.holder;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.Node;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
@ -36,5 +38,6 @@ public interface HeldPermission<T> {
Optional<String> getWorld(); Optional<String> getWorld();
OptionalLong getExpiry(); OptionalLong getExpiry();
Multimap<String, String> getContext(); Multimap<String, String> getContext();
Node asNode();
} }

View File

@ -74,4 +74,9 @@ public class NodeHeldPermission<T> implements HeldPermission<T> {
public Multimap<String, String> getContext() { public Multimap<String, String> getContext() {
return node.getContexts().toMultimap(); return node.getContexts().toMultimap();
} }
@Override
public Node asNode() {
return node;
}
} }

View File

@ -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-upload-start: "&bVerbose recording was disabled. Uploading results..."
verbose-recording-url: "&aVerbose results URL: {0}" 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." create-success: "&b{0}&a was successfully created."
delete-success: "&b{0}&a was successfully deleted." delete-success: "&b{0}&a was successfully deleted."
rename-success: "&b{0}&a was successfully renamed to &b{1}&a." rename-success: "&b{0}&a was successfully renamed to &b{1}&a."