mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-09-09 20:38:07 +02:00
Add ability to edit multiple users/groups in the same editor session
This commit is contained in:
parent
02ac4bc48b
commit
620b0c898c
@ -38,6 +38,7 @@ import me.lucko.luckperms.common.commands.impl.misc.ApplyEditsCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.BulkUpdateCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.CheckCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.DebugCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.EditorCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.ExportCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.ImportCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.misc.InfoCommand;
|
||||
@ -109,6 +110,7 @@ public class CommandManager {
|
||||
.add(new LogMainCommand(locale))
|
||||
.add(new SyncCommand(locale))
|
||||
.add(new InfoCommand(locale))
|
||||
.add(new EditorCommand(locale))
|
||||
.add(new DebugCommand(locale))
|
||||
.add(new VerboseCommand(locale))
|
||||
.add(new TreeCommand(locale))
|
||||
|
@ -41,6 +41,7 @@ public enum CommandPermission {
|
||||
|
||||
SYNC("sync", NONE),
|
||||
INFO("info", NONE),
|
||||
EDITOR("editor", NONE),
|
||||
DEBUG("debug", NONE),
|
||||
VERBOSE("verbose", NONE),
|
||||
TREE("tree", NONE),
|
||||
|
@ -26,9 +26,7 @@
|
||||
package me.lucko.luckperms.common.commands.impl.generic.other;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import me.lucko.luckperms.common.commands.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
@ -40,8 +38,6 @@ import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeModel;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.webeditor.WebEditorUtils;
|
||||
@ -52,6 +48,7 @@ import net.kyori.text.event.ClickEvent;
|
||||
import net.kyori.text.event.HoverEvent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
@ -69,26 +66,7 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
Message.EDITOR_START.send(sender);
|
||||
|
||||
// form the payload data
|
||||
JsonObject payload = new JsonObject();
|
||||
payload.addProperty("who", WebEditorUtils.getHolderIdentifier(holder));
|
||||
payload.addProperty("whoFriendly", holder.getFriendlyName());
|
||||
if (holder.getType().isUser()) {
|
||||
payload.addProperty("whoUuid", ((User) holder).getUuid().toString());
|
||||
}
|
||||
payload.addProperty("cmdAlias", label);
|
||||
payload.addProperty("uploadedBy", sender.getNameWithLocation());
|
||||
payload.addProperty("uploadedByUuid", sender.getUuid().toString());
|
||||
payload.addProperty("time", System.currentTimeMillis());
|
||||
|
||||
// attach the holders permissions
|
||||
payload.add("nodes", WebEditorUtils.serializePermissions(holder.getEnduringNodes().values().stream().map(NodeModel::fromNode)));
|
||||
|
||||
// attach an array of all permissions known to the server, to use for tab completion in the editor
|
||||
JsonArray knownPermsArray = new JsonArray();
|
||||
for (String perm : plugin.getPermissionVault().rootAsList()) {
|
||||
knownPermsArray.add(new JsonPrimitive(perm));
|
||||
}
|
||||
payload.add("knownPermissions", knownPermsArray);
|
||||
JsonObject payload = WebEditorUtils.formPayload(Collections.singletonList(holder), sender, label, plugin);
|
||||
|
||||
// upload the payload data to gist
|
||||
String gistId = WebEditorUtils.postToGist(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload));
|
||||
|
@ -26,6 +26,8 @@
|
||||
package me.lucko.luckperms.common.commands.impl.misc;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import me.lucko.luckperms.api.Node;
|
||||
@ -61,7 +63,6 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
|
||||
String code = args.get(0);
|
||||
String who = args.size() == 2 ? args.get(1) : null;
|
||||
|
||||
if (code.isEmpty()) {
|
||||
Message.APPLY_EDITS_INVALID_CODE.send(sender, code);
|
||||
@ -74,39 +75,55 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
if (who == null) {
|
||||
if (!data.has("who") || data.get("who").getAsString().isEmpty()) {
|
||||
Message.APPLY_EDITS_NO_TARGET.send(sender);
|
||||
return CommandResult.STATE_ERROR;
|
||||
if (data.has("tabs") && data.get("tabs").isJsonArray()) {
|
||||
JsonArray rows = data.get("tabs").getAsJsonArray();
|
||||
for (JsonElement row : rows) {
|
||||
read(row.getAsJsonObject(), code, sender, plugin);
|
||||
}
|
||||
|
||||
who = data.get("who").getAsString();
|
||||
} else {
|
||||
read(data, code, sender, plugin);
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private boolean read(JsonObject data, String code, Sender sender, LuckPermsPlugin plugin) {
|
||||
if (!data.has("who") || data.get("who").getAsString().isEmpty()) {
|
||||
Message.APPLY_EDITS_NO_TARGET.send(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
String who = data.get("who").getAsString();
|
||||
PermissionHolder holder = WebEditorUtils.getHolderFromIdentifier(plugin, sender, who);
|
||||
if (holder == null) {
|
||||
// the #getHolderFromIdentifier method will send the error message onto the sender
|
||||
return CommandResult.STATE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ArgumentPermissions.checkModifyPerms(plugin, sender, getPermission().get(), holder)) {
|
||||
Message.COMMAND_NO_PERMISSION.send(sender);
|
||||
return CommandResult.NO_PERMISSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<NodeModel> nodes = WebEditorUtils.deserializePermissions(data.getAsJsonArray("nodes"));
|
||||
|
||||
Set<Node> before = new HashSet<>(holder.getEnduringNodes().values());
|
||||
Set<Node> after = nodes.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
|
||||
Map.Entry<Set<Node>, Set<Node>> diff = diff(before, after);
|
||||
int additions = diff.getKey().size();
|
||||
int deletions = diff.getValue().size();
|
||||
|
||||
if (additions == 0 && deletions == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtendedLogEntry.build().actor(sender).acted(holder)
|
||||
.action("applyedits", code)
|
||||
.build().submit(plugin, sender);
|
||||
|
||||
Set<NodeModel> rawNodes = WebEditorUtils.deserializePermissions(data.getAsJsonArray("nodes"));
|
||||
holder.setEnduringNodes(after);
|
||||
|
||||
Set<Node> before = new HashSet<>(holder.getEnduringNodes().values());
|
||||
Set<Node> nodes = rawNodes.stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
holder.setEnduringNodes(nodes);
|
||||
|
||||
Map.Entry<Set<Node>, Set<Node>> diff = diff(before, nodes);
|
||||
int additions = diff.getKey().size();
|
||||
int deletions = diff.getValue().size();
|
||||
String additionsSummary = "addition" + (additions == 1 ? "" : "s");
|
||||
String deletionsSummary = "deletion" + (deletions == 1 ? "" : "s");
|
||||
|
||||
@ -118,9 +135,8 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
for (Node n : diff.getValue()) {
|
||||
Message.APPLY_EDITS_DIFF_REMOVED.send(sender, formatNode(n));
|
||||
}
|
||||
|
||||
SharedSubCommand.save(holder, sender, plugin);
|
||||
return CommandResult.SUCCESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String formatNode(Node n) {
|
||||
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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.impl.misc;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import me.lucko.luckperms.common.commands.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.commands.utils.ArgumentUtils;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.webeditor.WebEditorUtils;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.event.ClickEvent;
|
||||
import net.kyori.text.event.HoverEvent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EditorCommand extends SingleCommand {
|
||||
public EditorCommand(LocaleManager locale) {
|
||||
super(CommandSpec.EDITOR.spec(locale), "Editor", CommandPermission.EDITOR, Predicates.notInRange(0, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
|
||||
Type type = Type.ALL;
|
||||
|
||||
// parse type
|
||||
String typeString = ArgumentUtils.handleStringOrElse(0, args, null);
|
||||
if (typeString != null) {
|
||||
try {
|
||||
type = Type.valueOf(typeString.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
// collect holders
|
||||
List<PermissionHolder> holders = new ArrayList<>();
|
||||
if (type.includingGroups) {
|
||||
holders.addAll(plugin.getGroupManager().getAll().values());
|
||||
}
|
||||
if (type.includingUsers) {
|
||||
holders.addAll(plugin.getUserManager().getAll().values());
|
||||
}
|
||||
|
||||
// remove holders which the sender doesn't have perms to view
|
||||
holders.removeIf(holder -> ArgumentPermissions.checkViewPerms(plugin, sender, getPermission().get(), holder));
|
||||
|
||||
// they don't have perms to view any of them
|
||||
if (holders.isEmpty()) {
|
||||
Message.COMMAND_NO_PERMISSION.send(sender);
|
||||
return CommandResult.NO_PERMISSION;
|
||||
}
|
||||
|
||||
Message.EDITOR_START.send(sender);
|
||||
|
||||
// form the payload data
|
||||
JsonObject payload = WebEditorUtils.formPayload(holders, sender, label, plugin);
|
||||
|
||||
// upload the payload data to gist
|
||||
String gistId = WebEditorUtils.postToGist(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload));
|
||||
if (gistId == null) {
|
||||
Message.EDITOR_UPLOAD_FAILURE.send(sender);
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
// form a url for the editor
|
||||
String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + gistId;
|
||||
|
||||
Message.EDITOR_URL.send(sender);
|
||||
|
||||
Component message = TextComponent.builder(url).color(TextColor.AQUA)
|
||||
.clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url))
|
||||
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to open the editor.").color(TextColor.GRAY)))
|
||||
.build();
|
||||
|
||||
sender.sendMessage(message);
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private enum Type {
|
||||
ALL(true, true),
|
||||
USERS(true, false),
|
||||
GROUPS(false, true);
|
||||
|
||||
private final boolean includingUsers;
|
||||
private final boolean includingGroups;
|
||||
|
||||
Type(boolean includingUsers, boolean includingGroups) {
|
||||
this.includingUsers = includingUsers;
|
||||
this.includingGroups = includingGroups;
|
||||
}
|
||||
}
|
||||
}
|
@ -49,6 +49,11 @@ public enum CommandSpec {
|
||||
|
||||
SYNC("Sync changes with the storage", "/%s sync"),
|
||||
INFO("Print general plugin info", "/%s info"),
|
||||
EDITOR("Creates a new editor session", "/%s editor [type]",
|
||||
Arg.list(
|
||||
Arg.create("type", false, "the types to load into the editor. ('all', 'users' or 'groups')")
|
||||
)
|
||||
),
|
||||
DEBUG("Produce debugging output", "/%s debug"),
|
||||
VERBOSE("Manage verbose permission checking", "/%s verbose <true|false> [filter]",
|
||||
Arg.list(
|
||||
|
@ -124,7 +124,7 @@ public enum Message {
|
||||
|
||||
APPLY_EDITS_INVALID_CODE("&cInvalid code. &7({})", true),
|
||||
APPLY_EDITS_UNABLE_TO_READ("&cUnable to read data using the given code. &7({})", true),
|
||||
APPLY_EDITS_NO_TARGET("&cUnable to parse the target of the edit. Please supply it as an extra argument.", true),
|
||||
APPLY_EDITS_NO_TARGET("&cUnable to parse the target of the edit.", true),
|
||||
APPLY_EDITS_TARGET_GROUP_NOT_EXISTS("&cTarget group &4{}&c does not exist.", true),
|
||||
APPLY_EDITS_TARGET_USER_NOT_UUID("&cTarget user &4{}&c is not a valid uuid.", true),
|
||||
APPLY_EDITS_TARGET_USER_UNABLE_TO_LOAD("&cUnable to load target user &4{}&c.", true),
|
||||
|
@ -25,10 +25,12 @@
|
||||
|
||||
package me.lucko.luckperms.common.webeditor;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
@ -53,6 +55,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
@ -69,7 +72,51 @@ public final class WebEditorUtils {
|
||||
private static final String USER_ID_PATTERN = "user/";
|
||||
private static final String GROUP_ID_PATTERN = "group/";
|
||||
|
||||
public static String getHolderIdentifier(PermissionHolder holder) {
|
||||
private static void writeData(PermissionHolder holder, JsonObject payload) {
|
||||
payload.addProperty("who", getHolderIdentifier(holder));
|
||||
payload.addProperty("whoFriendly", holder.getFriendlyName());
|
||||
if (holder.getType().isUser()) {
|
||||
payload.addProperty("whoUuid", ((User) holder).getUuid().toString());
|
||||
}
|
||||
|
||||
// attach the holders permissions
|
||||
payload.add("nodes", WebEditorUtils.serializePermissions(holder.getEnduringNodes().values().stream().map(NodeModel::fromNode)));
|
||||
}
|
||||
|
||||
public static JsonObject formPayload(List<PermissionHolder> holders, Sender sender, String cmdLabel, LuckPermsPlugin plugin) {
|
||||
Preconditions.checkArgument(!holders.isEmpty(), "holders is empty");
|
||||
|
||||
// form the payload data
|
||||
JsonObject payload = new JsonObject();
|
||||
|
||||
payload.addProperty("cmdAlias", cmdLabel);
|
||||
payload.addProperty("uploadedBy", sender.getNameWithLocation());
|
||||
payload.addProperty("uploadedByUuid", sender.getUuid().toString());
|
||||
payload.addProperty("time", System.currentTimeMillis());
|
||||
|
||||
if (holders.size() == 1) {
|
||||
writeData(holders.get(0), payload);
|
||||
} else {
|
||||
JsonArray tabs = new JsonArray();
|
||||
for (PermissionHolder holder : holders) {
|
||||
JsonObject o = new JsonObject();
|
||||
writeData(holder, o);
|
||||
tabs.add(o);
|
||||
}
|
||||
payload.add("tabs", tabs);
|
||||
}
|
||||
|
||||
// attach an array of all permissions known to the server, to use for tab completion in the editor
|
||||
JsonArray knownPermsArray = new JsonArray();
|
||||
for (String perm : plugin.getPermissionVault().rootAsList()) {
|
||||
knownPermsArray.add(new JsonPrimitive(perm));
|
||||
}
|
||||
payload.add("knownPermissions", knownPermsArray);
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static String getHolderIdentifier(PermissionHolder holder) {
|
||||
if (holder.getType().isUser()) {
|
||||
User user = ((User) holder);
|
||||
return USER_ID_PATTERN + user.getUuid().toString();
|
||||
|
Loading…
Reference in New Issue
Block a user