Add ability to edit multiple users/groups in the same editor session

This commit is contained in:
Luck 2018-02-12 22:04:39 +00:00
parent 02ac4bc48b
commit 620b0c898c
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
8 changed files with 225 additions and 45 deletions

View File

@ -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))

View File

@ -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),

View File

@ -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));

View File

@ -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) {

View File

@ -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;
}
}
}

View File

@ -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(

View File

@ -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),

View File

@ -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();