Implement system for shorter editor urls & display diff when changes are applied
This commit is contained in:
parent
26feeb125e
commit
3afeb30795
@ -31,7 +31,6 @@ user-invalid-entry: "&4{0}&c is not a valid username/uuid."
|
||||
group-invalid-entry: "&4{0}&c is not a valid group name."
|
||||
track-invalid-entry: "&4{0}&c is not a valid track name."
|
||||
server-world-invalid-entry: "&cServer/world names can only contain alphanumeric characters and cannot exceed 36 characters in length."
|
||||
use-inherit-command: "&cUse the 'parent add' and 'parent remove' commands instead of specifying the node."
|
||||
verbose-invalid-filter: "&4{0}&c is not a valid verbose filter."
|
||||
verbose-on: "&bVerbose logging &aenabled &bfor checks matching &aANY&b."
|
||||
verbose-on-query: "&bVerbose logging &aenabled &bfor checks matching &a{0}&b."
|
||||
@ -57,7 +56,10 @@ apply-edits-target-group-not-exists: "&cTarget group &4{0}&c does not exist."
|
||||
apply-edits-target-user-not-uuid: "&cTarget user &4{0}&c is not a valid uuid."
|
||||
apply-edits-target-user-unable-to-load: "&cUnable to load target user &4{0}&c."
|
||||
apply-edits-target-unknown: "&cInvalid target. &7({0})"
|
||||
apply-edits-success: "&aSuccessfully applied &b{0}&a nodes to &b{1}&a."
|
||||
apply-edits-success: "&aWeb editor data was applied to &b{0}&a successfully."
|
||||
apply-edits-success-summary: "&7(&a{0} &7{1} and &c{2} &7{3})"
|
||||
apply-edits-diff-added: "&a+ &f{0}"
|
||||
apply-edits-diff-removed: "&c- &f{0}"
|
||||
editor-upload-failure: "&cUnable to upload permission data to the editor."
|
||||
editor-url: "&aEditor URL:"
|
||||
check-result: "&aPermission check result on user &b{0}&a for permission &b{1}&a: &f{2}"
|
||||
@ -234,6 +236,11 @@ group-info-general: >
|
||||
{PREFIX}&f- &3Suffixes: &a{6}\n
|
||||
{PREFIX}&f- &3Meta: &a{7}
|
||||
group-set-weight: "&aSet weight to &b{0}&a for group &b{1}&a."
|
||||
group-set-display-name-doesnt-have: "&b{0}&a doesn't have a display name set."
|
||||
group-set-display-name-already-has: "&b{0}&a already has a display name of &b{1}&a."
|
||||
group-set-display-name-already-in-use: "&aThe display name &b{0}&a is already being used by &b{1}&a."
|
||||
group-set-display-name: "&aSet display name to &b{0}&a for group &b{1}&a."
|
||||
group-set-display-name-removed: "&aRemoved display name for group &b{0}&a."
|
||||
track-info: >
|
||||
{PREFIX}&b&l> &bShowing Track: &f{0}\n
|
||||
{PREFIX}&f- &7Path: &f{1}
|
||||
|
@ -362,6 +362,7 @@ public class CommandManager {
|
||||
case "e":
|
||||
args.remove(2);
|
||||
args.add(2, "editor");
|
||||
break;
|
||||
|
||||
// Provide backwards compatibility
|
||||
case "setprimarygroup":
|
||||
|
@ -25,13 +25,10 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.impl.generic.other;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import me.lucko.luckperms.common.commands.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
@ -40,16 +37,15 @@ import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
|
||||
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.Group;
|
||||
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;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
@ -57,21 +53,9 @@ import net.kyori.text.event.ClickEvent;
|
||||
import net.kyori.text.event.HoverEvent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
private static final String USER_ID_PATTERN = "user/";
|
||||
private static final String GROUP_ID_PATTERN = "group/";
|
||||
private static final String FILE_NAME = "luckperms-data.json";
|
||||
|
||||
public HolderEditor(LocaleManager locale, boolean user) {
|
||||
super(CommandSpec.HOLDER_EDITOR.spec(locale), "editor", user ? CommandPermission.USER_EDITOR : CommandPermission.GROUP_EDITOR, Predicates.alwaysFalse());
|
||||
}
|
||||
@ -85,7 +69,7 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
|
||||
// form the payload data
|
||||
JsonObject payload = new JsonObject();
|
||||
payload.addProperty("who", id(holder));
|
||||
payload.addProperty("who", WebEditorUtils.getHolderIdentifier(holder));
|
||||
payload.addProperty("whoFriendly", holder.getFriendlyName());
|
||||
if (holder instanceof User) {
|
||||
payload.addProperty("whoUuid", ((User) holder).getUuid().toString());
|
||||
@ -96,7 +80,7 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
payload.addProperty("time", System.currentTimeMillis());
|
||||
|
||||
// attach the holders permissions
|
||||
payload.add("nodes", serializePermissions(holder.getEnduringNodes().values().stream().map(NodeModel::fromNode)));
|
||||
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();
|
||||
@ -106,18 +90,14 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
payload.add("knownPermissions", knownPermsArray);
|
||||
|
||||
// upload the payload data to gist
|
||||
String dataUrl = paste(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload));
|
||||
if (dataUrl == null) {
|
||||
String gistId = WebEditorUtils.postToGist(new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(payload));
|
||||
if (gistId == null) {
|
||||
Message.EDITOR_UPLOAD_FAILURE.send(sender);
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
// extract the information we need from the gist url
|
||||
List<String> parts = Splitter.on('/').splitToList(dataUrl);
|
||||
String id = "?" + parts.get(4) + "/" + parts.get(6);
|
||||
|
||||
// form a url for the editor
|
||||
String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + id;
|
||||
String url = plugin.getConfiguration().get(ConfigKeys.WEB_EDITOR_URL_PATTERN) + "?" + gistId;
|
||||
|
||||
Message.EDITOR_URL.send(sender);
|
||||
|
||||
@ -130,89 +110,4 @@ public class HolderEditor<T extends PermissionHolder> extends SubCommand<T> {
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static String id(PermissionHolder holder) {
|
||||
if (holder instanceof User) {
|
||||
User user = ((User) holder);
|
||||
return USER_ID_PATTERN + user.getUuid().toString();
|
||||
} else {
|
||||
Group group = ((Group) holder);
|
||||
return GROUP_ID_PATTERN + group.getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static String paste(String content) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL("https://api.github.com/gists").openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
StringWriter sw = new StringWriter();
|
||||
new JsonWriter(sw).beginObject()
|
||||
.name("description").value("LuckPerms Web Permissions Editor Data")
|
||||
.name("public").value(false)
|
||||
.name("files")
|
||||
.beginObject().name(FILE_NAME)
|
||||
.beginObject().name("content").value(content)
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
|
||||
os.write(sw.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() >= 400) {
|
||||
throw new RuntimeException("Connection returned response code: " + connection.getResponseCode() + " - " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
JsonObject response = new Gson().fromJson(reader, JsonObject.class);
|
||||
return response.get("files").getAsJsonObject().get(FILE_NAME).getAsJsonObject().get("raw_url").getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonArray serializePermissions(Stream<NodeModel> nodes) {
|
||||
JsonArray arr = new JsonArray();
|
||||
nodes.forEach(node -> {
|
||||
JsonObject attributes = new JsonObject();
|
||||
attributes.addProperty("permission", node.getPermission());
|
||||
attributes.addProperty("value", node.getValue());
|
||||
|
||||
if (!node.getServer().equals("global")) {
|
||||
attributes.addProperty("server", node.getServer());
|
||||
}
|
||||
|
||||
if (!node.getWorld().equals("global")) {
|
||||
attributes.addProperty("world", node.getWorld());
|
||||
}
|
||||
|
||||
if (node.getExpiry() != 0L) {
|
||||
attributes.addProperty("expiry", node.getExpiry());
|
||||
}
|
||||
|
||||
if (!node.getContexts().isEmpty()) {
|
||||
attributes.add("context", ContextSetJsonSerializer.serializeContextSet(node.getContexts()));
|
||||
}
|
||||
|
||||
arr.add(attributes);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ public class ParentInfo extends SharedSubCommand {
|
||||
List<Node> page = new ArrayList<>();
|
||||
for (Node node : nodes) {
|
||||
if (!node.isGroupNode()) continue;
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (node.isTemporary()) continue;
|
||||
page.add(node);
|
||||
}
|
||||
@ -105,6 +106,7 @@ public class ParentInfo extends SharedSubCommand {
|
||||
List<Node> page = new ArrayList<>();
|
||||
for (Node node : nodes) {
|
||||
if (!node.isGroupNode()) continue;
|
||||
if (!node.getValuePrimitive()) continue;
|
||||
if (node.isPermanent()) continue;
|
||||
page.add(node);
|
||||
}
|
||||
|
@ -121,14 +121,10 @@ public class PermissionInfo extends SharedSubCommand {
|
||||
context:
|
||||
if (index != -1) {
|
||||
String key = filter.substring(0, index);
|
||||
if (key.equals("")) {
|
||||
break context;
|
||||
}
|
||||
if (key.equals("")) break context;
|
||||
|
||||
String value = filter.substring(index + 1);
|
||||
if (value.equals("")) {
|
||||
break context;
|
||||
}
|
||||
if (value.equals("")) break context;
|
||||
|
||||
contextFilter = Maps.immutableEntry(key, value);
|
||||
}
|
||||
@ -140,7 +136,9 @@ public class PermissionInfo extends SharedSubCommand {
|
||||
|
||||
List<Node> l = new ArrayList<>();
|
||||
for (Node node : nodes) {
|
||||
if (node.isGroupNode() || node.isPrefix() || node.isSuffix() || node.isMeta()) continue;
|
||||
if ((node.isGroupNode() && node.getValuePrimitive()) || node.isPrefix() || node.isSuffix() || node.isMeta()) continue;
|
||||
|
||||
// check against filters
|
||||
if (nodeFilter != null && !node.getPermission().startsWith(nodeFilter)) continue;
|
||||
if (contextFilter != null && !node.getFullContexts().hasIgnoreCase(contextFilter.getKey(), contextFilter.getValue())) continue;
|
||||
if (temp != node.isTemporary()) continue;
|
||||
|
@ -62,7 +62,7 @@ public class PermissionSet extends SharedSubCommand {
|
||||
}
|
||||
|
||||
String node = ArgumentUtils.handleString(0, args);
|
||||
boolean b = ArgumentUtils.handleBoolean(1, args);
|
||||
boolean value = ArgumentUtils.handleBoolean(1, args);
|
||||
MutableContextSet context = ArgumentUtils.handleContext(2, args, plugin);
|
||||
|
||||
if (ArgumentPermissions.checkContext(plugin, sender, permission, context)) {
|
||||
@ -75,13 +75,13 @@ public class PermissionSet extends SharedSubCommand {
|
||||
return CommandResult.NO_PERMISSION;
|
||||
}
|
||||
|
||||
DataMutateResult result = holder.setPermission(NodeFactory.newBuilder(node).setValue(b).withExtraContext(context).build());
|
||||
DataMutateResult result = holder.setPermission(NodeFactory.newBuilder(node).setValue(value).withExtraContext(context).build());
|
||||
|
||||
if (result.asBoolean()) {
|
||||
Message.SETPERMISSION_SUCCESS.send(sender, node, b, holder.getFriendlyName(), CommandUtils.contextSetToString(context));
|
||||
Message.SETPERMISSION_SUCCESS.send(sender, node, value, holder.getFriendlyName(), CommandUtils.contextSetToString(context));
|
||||
|
||||
ExtendedLogEntry.build().actor(sender).acted(holder)
|
||||
.action("permission", "set", node, b, context)
|
||||
.action("permission", "set", node, value, context)
|
||||
.build().submit(plugin, sender);
|
||||
|
||||
save(holder, sender, plugin);
|
||||
|
@ -67,7 +67,7 @@ public class PermissionSetTemp extends SharedSubCommand {
|
||||
}
|
||||
|
||||
String node = ArgumentUtils.handleString(0, args);
|
||||
boolean b = ArgumentUtils.handleBoolean(1, args);
|
||||
boolean value = ArgumentUtils.handleBoolean(1, args);
|
||||
long duration = ArgumentUtils.handleDuration(2, args);
|
||||
MutableContextSet context = ArgumentUtils.handleContext(3, args, plugin);
|
||||
|
||||
@ -82,14 +82,14 @@ public class PermissionSetTemp extends SharedSubCommand {
|
||||
}
|
||||
|
||||
TemporaryModifier modifier = plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR);
|
||||
Map.Entry<DataMutateResult, Node> result = holder.setPermission(NodeFactory.newBuilder(node).setValue(b).withExtraContext(context).setExpiry(duration).build(), modifier);
|
||||
Map.Entry<DataMutateResult, Node> result = holder.setPermission(NodeFactory.newBuilder(node).setValue(value).withExtraContext(context).setExpiry(duration).build(), modifier);
|
||||
|
||||
if (result.getKey().asBoolean()) {
|
||||
duration = result.getValue().getExpiryUnixTime();
|
||||
Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, b, holder.getFriendlyName(), DateUtil.formatDateDiff(duration), CommandUtils.contextSetToString(context));
|
||||
Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, value, holder.getFriendlyName(), DateUtil.formatDateDiff(duration), CommandUtils.contextSetToString(context));
|
||||
|
||||
ExtendedLogEntry.build().actor(sender).acted(holder)
|
||||
.action("permission", "settemp", node, b, duration, context)
|
||||
.action("permission", "settemp", node, value, duration, context)
|
||||
.build().submit(plugin, sender);
|
||||
|
||||
save(holder, sender, plugin);
|
||||
|
@ -52,7 +52,7 @@ public class LogGroupHistory extends SubCommand<Log> {
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Log log, List<String> args, String label) throws CommandException {
|
||||
String group = args.get(0).toLowerCase();
|
||||
int page = -999;
|
||||
int page = Integer.MIN_VALUE;
|
||||
|
||||
if (args.size() == 2) {
|
||||
try {
|
||||
@ -75,7 +75,7 @@ public class LogGroupHistory extends SubCommand<Log> {
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
if (page == -999) {
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class LogSearch extends SubCommand<Log> {
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Log log, List<String> args, String label) throws CommandException {
|
||||
int page = -999;
|
||||
int page = Integer.MIN_VALUE;
|
||||
if (args.size() > 1) {
|
||||
try {
|
||||
page = Integer.parseInt(args.get(args.size() - 1));
|
||||
@ -68,7 +68,7 @@ public class LogSearch extends SubCommand<Log> {
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
if (page == -999) {
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public class LogTrackHistory extends SubCommand<Log> {
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Log log, List<String> args, String label) throws CommandException {
|
||||
String track = args.get(0).toLowerCase();
|
||||
int page = -999;
|
||||
int page = Integer.MIN_VALUE;
|
||||
|
||||
if (args.size() == 2) {
|
||||
try {
|
||||
@ -75,7 +75,7 @@ public class LogTrackHistory extends SubCommand<Log> {
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
if (page == -999) {
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class LogUserHistory extends SubCommand<Log> {
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Log log, List<String> args, String label) throws CommandException {
|
||||
String target = args.get(0);
|
||||
int page = -999;
|
||||
int page = Integer.MIN_VALUE;
|
||||
|
||||
if (args.size() == 2) {
|
||||
try {
|
||||
@ -95,7 +95,7 @@ public class LogUserHistory extends SubCommand<Log> {
|
||||
}
|
||||
}
|
||||
|
||||
if (page == -999) {
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = log.getUserHistoryMaxPages(uuid);
|
||||
}
|
||||
|
||||
|
@ -25,13 +25,10 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.impl.misc;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.common.actionlog.ExtendedLogEntry;
|
||||
import me.lucko.luckperms.common.commands.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
@ -41,25 +38,20 @@ import me.lucko.luckperms.common.commands.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.commands.utils.CommandUtils;
|
||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
|
||||
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.node.NodeModel;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.DateUtil;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.webeditor.WebEditorUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ApplyEditsCommand extends SingleCommand {
|
||||
@ -72,27 +64,13 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
String code = args.get(0);
|
||||
String who = args.size() == 2 ? args.get(1) : null;
|
||||
|
||||
if (!code.contains("/")) {
|
||||
if (code.isEmpty()) {
|
||||
Message.APPLY_EDITS_INVALID_CODE.send(sender, code);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
Iterator<String> codeParts = Splitter.on('/').limit(2).split(code).iterator();
|
||||
String part1 = codeParts.next();
|
||||
String part2 = codeParts.next();
|
||||
|
||||
if (part1.isEmpty() || part2.isEmpty()) {
|
||||
Message.APPLY_EDITS_INVALID_CODE.send(sender, code);
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
String url = "https://gist.githubusercontent.com/anonymous/" + part1 + "/raw/" + part2 + "/luckperms-data.json";
|
||||
JsonObject data;
|
||||
|
||||
try {
|
||||
data = read(url);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
JsonObject data = WebEditorUtils.getDataFromGist(code);
|
||||
if (data == null) {
|
||||
Message.APPLY_EDITS_UNABLE_TO_READ.send(sender, code);
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
@ -106,34 +84,9 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
who = data.get("who").getAsString();
|
||||
}
|
||||
|
||||
PermissionHolder holder;
|
||||
|
||||
if (who.startsWith("group/")) {
|
||||
String group = who.substring("group/".length());
|
||||
holder = plugin.getGroupManager().getIfLoaded(group);
|
||||
|
||||
if (holder == null) {
|
||||
Message.APPLY_EDITS_TARGET_GROUP_NOT_EXISTS.send(sender, group);
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
} else if (who.startsWith("user/")) {
|
||||
String user = who.substring("user/".length());
|
||||
UUID uuid = CommandUtils.parseUuid(user);
|
||||
if (uuid == null) {
|
||||
Message.APPLY_EDITS_TARGET_USER_NOT_UUID.send(sender, user);
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
holder = plugin.getUserManager().getIfLoaded(uuid);
|
||||
if (holder == null) {
|
||||
plugin.getStorage().loadUser(uuid, null).join();
|
||||
}
|
||||
holder = plugin.getUserManager().getIfLoaded(uuid);
|
||||
if (holder == null) {
|
||||
Message.APPLY_EDITS_TARGET_USER_UNABLE_TO_LOAD.send(sender, uuid.toString());
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
} else {
|
||||
Message.APPLY_EDITS_TARGET_UNKNOWN.send(sender, who);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -143,66 +96,54 @@ public class ApplyEditsCommand extends SingleCommand {
|
||||
}
|
||||
|
||||
ExtendedLogEntry.build().actor(sender).acted(holder)
|
||||
.action("applyedits", part1 + "/" + part2)
|
||||
.action("applyedits", code)
|
||||
.build().submit(plugin, sender);
|
||||
|
||||
Set<NodeModel> nodes = deserializePermissions(data.getAsJsonArray("nodes"));
|
||||
holder.setEnduringNodes(nodes.stream().map(NodeModel::toNode).collect(Collectors.toSet()));
|
||||
Message.APPLY_EDITS_SUCCESS.send(sender, nodes.size(), holder.getFriendlyName());
|
||||
Set<NodeModel> rawNodes = WebEditorUtils.deserializePermissions(data.getAsJsonArray("nodes"));
|
||||
|
||||
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");
|
||||
|
||||
Message.APPLY_EDITS_SUCCESS.send(sender, holder.getFriendlyName());
|
||||
Message.APPLY_EDITS_SUCCESS_SUMMARY.send(sender, additions, additionsSummary, deletions, deletionsSummary);
|
||||
for (Node n : diff.getKey()) {
|
||||
Message.APPLY_EDITS_DIFF_ADDED.send(sender, formatNode(n));
|
||||
}
|
||||
for (Node n : diff.getValue()) {
|
||||
Message.APPLY_EDITS_DIFF_REMOVED.send(sender, formatNode(n));
|
||||
}
|
||||
|
||||
SharedSubCommand.save(holder, sender, plugin);
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static String formatNode(Node n) {
|
||||
return n.getPermission() + " &7(" + (n.getValuePrimitive() ? "&a" : "&c") + n.getValuePrimitive() + "&7)" + CommandUtils.getAppendableNodeContextString(n) +
|
||||
(n.isTemporary() ? " &7(" + DateUtil.formatDateDiffShort(n.getExpiryUnixTime()) + ")" : "");
|
||||
}
|
||||
|
||||
private static Map.Entry<Set<Node>, Set<Node>> diff(Set<Node> before, Set<Node> after) {
|
||||
// entries in before but not after are being removed
|
||||
// entries in after but not before are being added
|
||||
|
||||
Set<Node> added = new HashSet<>(after);
|
||||
added.removeAll(before);
|
||||
|
||||
Set<Node> removed = new HashSet<>(before);
|
||||
removed.removeAll(after);
|
||||
|
||||
return Maps.immutableEntry(added, removed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisplay() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JsonObject read(String address) throws IOException {
|
||||
URL url = new URL(address);
|
||||
try (InputStream in = url.openStream(); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
|
||||
return new Gson().fromJson(reader, JsonObject.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<NodeModel> deserializePermissions(JsonArray permissionsSection) {
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
|
||||
for (JsonElement ent : permissionsSection) {
|
||||
if (!ent.isJsonObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject data = ent.getAsJsonObject();
|
||||
|
||||
String permission = data.get("permission").getAsString();
|
||||
boolean value = true;
|
||||
String server = "global";
|
||||
String world = "global";
|
||||
long expiry = 0L;
|
||||
ImmutableContextSet context = ImmutableContextSet.empty();
|
||||
|
||||
if (data.has("value")) {
|
||||
value = data.get("value").getAsBoolean();
|
||||
}
|
||||
if (data.has("server")) {
|
||||
server = data.get("server").getAsString();
|
||||
}
|
||||
if (data.has("world")) {
|
||||
world = data.get("world").getAsString();
|
||||
}
|
||||
if (data.has("expiry")) {
|
||||
expiry = data.get("expiry").getAsLong();
|
||||
}
|
||||
|
||||
if (data.has("context") && data.get("context").isJsonObject()) {
|
||||
JsonObject contexts = data.get("context").getAsJsonObject();
|
||||
context = ContextSetJsonSerializer.deserializeContextSet(contexts).makeImmutable();
|
||||
}
|
||||
|
||||
nodes.add(NodeModel.of(permission, value, server, world, expiry, context));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,10 @@ public enum Message {
|
||||
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),
|
||||
APPLY_EDITS_TARGET_UNKNOWN("&cInvalid target. &7({})", true),
|
||||
APPLY_EDITS_SUCCESS("&aSuccessfully applied &b{}&a nodes to &b{}&a.", true),
|
||||
APPLY_EDITS_SUCCESS("&aWeb editor data was applied to &b{}&a successfully.", true),
|
||||
APPLY_EDITS_SUCCESS_SUMMARY("&7(&a{} &7{} and &c{} &7{})", true),
|
||||
APPLY_EDITS_DIFF_ADDED("&a+ &f{}", false),
|
||||
APPLY_EDITS_DIFF_REMOVED("&c- &f{}", false),
|
||||
|
||||
EDITOR_UPLOAD_FAILURE("&cUnable to upload permission data to the editor.", true),
|
||||
EDITOR_URL("&aEditor URL:", true),
|
||||
|
@ -165,8 +165,13 @@ public class DateUtil {
|
||||
return DateUtil.formatDateDiff(now, then);
|
||||
}
|
||||
|
||||
public static String formatDateDiffShort(long seconds) {
|
||||
long now = unixSecondsNow();
|
||||
return formatTimeShort(seconds - now);
|
||||
}
|
||||
|
||||
public static String formatTimeShort(long seconds) {
|
||||
if (seconds == 0) {
|
||||
if (seconds <= 0) {
|
||||
return "0s";
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
@ -57,8 +58,8 @@ public class PasteUtils {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL(GIST_API).openConnection();
|
||||
connection.setRequestProperty("User-Agent", "luckperms");
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
@ -85,9 +86,11 @@ public class PasteUtils {
|
||||
|
||||
String pasteUrl;
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
JsonObject response = new Gson().fromJson(reader, JsonObject.class);
|
||||
pasteUrl = response.get("html_url").getAsString();
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
|
||||
JsonObject response = new Gson().fromJson(reader, JsonObject.class);
|
||||
pasteUrl = response.get("html_url").getAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +98,9 @@ public class PasteUtils {
|
||||
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL(SHORTEN_API).openConnection();
|
||||
connection.setRequestProperty("User-Agent", "luckperms");
|
||||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setDoOutput(true);
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
os.write(("url=" + pasteUrl).getBytes(StandardCharsets.UTF_8));
|
||||
|
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.webeditor;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.commands.utils.CommandUtils;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
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 java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Utility methods for interacting with the LuckPerms web permission editor.
|
||||
*/
|
||||
@UtilityClass
|
||||
public class WebEditorUtils {
|
||||
|
||||
private static final String FILE_NAME = "luckperms-data.json";
|
||||
private static final String GIST_API_URL = "https://api.github.com/gists";
|
||||
|
||||
private static final String USER_ID_PATTERN = "user/";
|
||||
private static final String GROUP_ID_PATTERN = "group/";
|
||||
|
||||
public static String getHolderIdentifier(PermissionHolder holder) {
|
||||
if (holder instanceof User) {
|
||||
User user = ((User) holder);
|
||||
return USER_ID_PATTERN + user.getUuid().toString();
|
||||
} else {
|
||||
Group group = ((Group) holder);
|
||||
return GROUP_ID_PATTERN + group.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static PermissionHolder getHolderFromIdentifier(LuckPermsPlugin plugin, Sender sender, String who) {
|
||||
if (who.startsWith("group/")) {
|
||||
String group = who.substring("group/".length());
|
||||
Group holder = plugin.getGroupManager().getIfLoaded(group);
|
||||
if (holder == null) {
|
||||
Message.APPLY_EDITS_TARGET_GROUP_NOT_EXISTS.send(sender, group);
|
||||
}
|
||||
return holder;
|
||||
} else if (who.startsWith("user/")) {
|
||||
String user = who.substring("user/".length());
|
||||
UUID uuid = CommandUtils.parseUuid(user);
|
||||
if (uuid == null) {
|
||||
Message.APPLY_EDITS_TARGET_USER_NOT_UUID.send(sender, user);
|
||||
return null;
|
||||
}
|
||||
plugin.getStorage().loadUser(uuid, null).join();
|
||||
User holder = plugin.getUserManager().getIfLoaded(uuid);
|
||||
if (holder == null) {
|
||||
Message.APPLY_EDITS_TARGET_USER_UNABLE_TO_LOAD.send(sender, uuid.toString());
|
||||
}
|
||||
return holder;
|
||||
} else {
|
||||
Message.APPLY_EDITS_TARGET_UNKNOWN.send(sender, who);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String postToGist(String content) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL(GIST_API_URL).openConnection();
|
||||
connection.addRequestProperty("User-Agent", "luckperms");
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
StringWriter sw = new StringWriter();
|
||||
new JsonWriter(sw).beginObject()
|
||||
.name("description").value("LuckPerms Web Permissions Editor Data")
|
||||
.name("public").value(false)
|
||||
.name("files")
|
||||
.beginObject().name(FILE_NAME)
|
||||
.beginObject().name("content").value(content)
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
|
||||
os.write(sw.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() >= 400) {
|
||||
throw new RuntimeException("Connection returned response code: " + connection.getResponseCode() + " - " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
|
||||
JsonObject response = new Gson().fromJson(reader, JsonObject.class);
|
||||
return response.get("id").getAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonObject getDataFromGist(String id) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL(GIST_API_URL + "/" + id).openConnection();
|
||||
connection.addRequestProperty("User-Agent", "luckperms");
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
if (connection.getResponseCode() >= 400) {
|
||||
throw new RuntimeException("Connection returned response code: " + connection.getResponseCode() + " - " + connection.getResponseMessage());
|
||||
}
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
|
||||
JsonObject response = new Gson().fromJson(reader, JsonObject.class);
|
||||
JsonObject files = response.get("files").getAsJsonObject();
|
||||
JsonObject permsFile = files.get(FILE_NAME).getAsJsonObject();
|
||||
|
||||
// uh..
|
||||
if (permsFile.get("truncated").getAsBoolean()) {
|
||||
String rawUrlStr = permsFile.get("raw_url").getAsString();
|
||||
URL rawUrl = new URL(rawUrlStr);
|
||||
try (InputStream rawInputStream = rawUrl.openStream()) {
|
||||
try (InputStreamReader rawInputStreamReader = new InputStreamReader(rawInputStream, StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader rawReader = new BufferedReader(rawInputStreamReader)) {
|
||||
return new Gson().fromJson(rawReader, JsonObject.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String content = permsFile.get("content").getAsString();
|
||||
return new Gson().fromJson(content, JsonObject.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonArray serializePermissions(Stream<NodeModel> nodes) {
|
||||
JsonArray arr = new JsonArray();
|
||||
nodes.forEach(node -> {
|
||||
JsonObject attributes = new JsonObject();
|
||||
attributes.addProperty("permission", node.getPermission());
|
||||
attributes.addProperty("value", node.getValue());
|
||||
|
||||
if (!node.getServer().equals("global")) {
|
||||
attributes.addProperty("server", node.getServer());
|
||||
}
|
||||
|
||||
if (!node.getWorld().equals("global")) {
|
||||
attributes.addProperty("world", node.getWorld());
|
||||
}
|
||||
|
||||
if (node.getExpiry() != 0L) {
|
||||
attributes.addProperty("expiry", node.getExpiry());
|
||||
}
|
||||
|
||||
if (!node.getContexts().isEmpty()) {
|
||||
attributes.add("context", ContextSetJsonSerializer.serializeContextSet(node.getContexts()));
|
||||
}
|
||||
|
||||
arr.add(attributes);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static Set<NodeModel> deserializePermissions(JsonArray permissionsSection) {
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
|
||||
for (JsonElement ent : permissionsSection) {
|
||||
if (!ent.isJsonObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject data = ent.getAsJsonObject();
|
||||
|
||||
String permission = data.get("permission").getAsString();
|
||||
boolean value = true;
|
||||
String server = "global";
|
||||
String world = "global";
|
||||
long expiry = 0L;
|
||||
ImmutableContextSet context = ImmutableContextSet.empty();
|
||||
|
||||
if (data.has("value")) {
|
||||
value = data.get("value").getAsBoolean();
|
||||
}
|
||||
if (data.has("server")) {
|
||||
server = data.get("server").getAsString();
|
||||
}
|
||||
if (data.has("world")) {
|
||||
world = data.get("world").getAsString();
|
||||
}
|
||||
if (data.has("expiry")) {
|
||||
expiry = data.get("expiry").getAsLong();
|
||||
}
|
||||
|
||||
if (data.has("context") && data.get("context").isJsonObject()) {
|
||||
JsonObject contexts = data.get("context").getAsJsonObject();
|
||||
context = ContextSetJsonSerializer.deserializeContextSet(contexts).makeImmutable();
|
||||
}
|
||||
|
||||
nodes.add(NodeModel.of(permission, value, server, world, expiry, context));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user