Upload folder via GUI - src
This commit is contained in:
@@ -115,11 +115,18 @@ public class TicketPlugin extends JavaPlugin {
|
||||
// Manager, GUI, FAQ & Discord-Webhook initialisieren
|
||||
categoryManager = new CategoryManager(this);
|
||||
ticketManager = new TicketManager(this);
|
||||
faqManager = new FaqManager(this);
|
||||
ticketGUI = new TicketGUI(this);
|
||||
faqGUI = new FaqGUI(this);
|
||||
discordWebhook = new DiscordWebhook(this);
|
||||
|
||||
// ── FAQ-System (nur initialisieren wenn aktiviert) ─────────────────
|
||||
if (isFaqEnabled()) {
|
||||
faqManager = new FaqManager(this);
|
||||
faqGUI = new FaqGUI(this);
|
||||
getLogger().info("[FAQ] FAQ-System aktiviert.");
|
||||
} else {
|
||||
getLogger().info("[FAQ] FAQ-System deaktiviert (faq-enabled: false in config.yml).");
|
||||
}
|
||||
|
||||
if (getConfig().getBoolean("discord.enabled", false)) {
|
||||
String url = getConfig().getString("discord.webhook-url", "");
|
||||
if (url.isEmpty()) {
|
||||
@@ -134,7 +141,9 @@ public class TicketPlugin extends JavaPlugin {
|
||||
|
||||
getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this);
|
||||
getServer().getPluginManager().registerEvents(ticketGUI, this);
|
||||
getServer().getPluginManager().registerEvents(faqGUI, this);
|
||||
if (faqGUI != null) {
|
||||
getServer().getPluginManager().registerEvents(faqGUI, this);
|
||||
}
|
||||
|
||||
// Automatische Archivierung
|
||||
int archiveIntervalH = getConfig().getInt("auto-archive-interval-hours", 24);
|
||||
@@ -166,6 +175,9 @@ public class TicketPlugin extends JavaPlugin {
|
||||
if (webServer != null) webServer.stop();
|
||||
if (ticketCache != null) ticketCache.clear();
|
||||
if (databaseManager != null) databaseManager.disconnect();
|
||||
// Adventure BukkitAudiences sauber schließen
|
||||
if (languageManager != null && languageManager.getAudiences() != null)
|
||||
languageManager.getAudiences().close();
|
||||
getLogger().info("TicketSystem wurde deaktiviert.");
|
||||
}
|
||||
|
||||
@@ -322,6 +334,14 @@ public class TicketPlugin extends JavaPlugin {
|
||||
TicketPriority.reloadLocalizedNames(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt zurück ob das FAQ-System aktiviert ist.
|
||||
* Konfigurierbar in config.yml → faq-enabled (Standard: true)
|
||||
*/
|
||||
public boolean isFaqEnabled() {
|
||||
return getConfig().getBoolean("faq-enabled", true);
|
||||
}
|
||||
|
||||
public static TicketPlugin getInstance() { return instance; }
|
||||
public LanguageManager getLanguageManager() { return languageManager; }
|
||||
public DatabaseManager getDatabaseManager() { return databaseManager; }
|
||||
|
||||
@@ -9,17 +9,22 @@ import de.ticketsystem.model.TicketComment;
|
||||
import de.ticketsystem.model.TicketPriority;
|
||||
import de.ticketsystem.manager.CategoryManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
@@ -31,15 +36,8 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
// ── Subkommando-Auflösung ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Normalisiert ein Subkommando auf den internen englischen Schlüssel.
|
||||
* Deutsche UND englische Varianten werden immer akzeptiert – unabhängig
|
||||
* von der language-Einstellung. So kann ein Admin auf einem EN-Server
|
||||
* trotzdem den deutschen Begriff tippen ohne einen Fehler zu bekommen.
|
||||
*/
|
||||
private String normalize(String input) {
|
||||
return switch (input.toLowerCase()) {
|
||||
// Deutsch → intern
|
||||
case "erstellen" -> "create";
|
||||
case "liste" -> "list";
|
||||
case "übernehmen", "uebernehmen",
|
||||
@@ -59,7 +57,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
case "priorität", "prioritaet" -> "setpriority";
|
||||
case "hilfe" -> "help";
|
||||
case "kategorie" -> "category";
|
||||
// Englisch + alles andere → unverändert
|
||||
default -> input.toLowerCase();
|
||||
};
|
||||
}
|
||||
@@ -68,17 +65,36 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.lang().get("general.console-only"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
plugin.getTicketManager().sendHelpMessage(player);
|
||||
if (sender instanceof Player player) {
|
||||
plugin.getTicketManager().sendHelpMessage(player);
|
||||
} else {
|
||||
sendConsoleHelp(sender);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (normalize(args[0])) {
|
||||
String sub = normalize(args[0]);
|
||||
|
||||
// ── Konsolen-only: create mit Spielername ────────────────────────
|
||||
if (sender instanceof ConsoleCommandSender) {
|
||||
switch (sub) {
|
||||
case "create" -> { handleCreateConsole(sender, args); return true; }
|
||||
case "reload" -> { handleReloadConsole(sender); return true; }
|
||||
case "archive" -> { handleArchiveConsole(sender); return true; }
|
||||
case "stats" -> { handleStatsConsole(sender); return true; }
|
||||
default -> {
|
||||
sender.sendMessage("[TicketSystem] Konsolen-Befehle: create <Spieler> [Kategorie] <Text> | reload | archive | stats");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Spieler-Befehle ──────────────────────────────────────────────
|
||||
Player player = (Player) sender;
|
||||
|
||||
switch (sub) {
|
||||
case "create" -> handleCreate(player, args);
|
||||
case "list" -> handleList(player);
|
||||
case "claim" -> handleClaim(player, args);
|
||||
@@ -103,6 +119,179 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── /ticket create (Konsole) ──────────────────────────────────────────
|
||||
//
|
||||
// Syntax: /ticket create <Spielername> [Kategorie] [Priorität] <Beschreibung>
|
||||
//
|
||||
// Das Ticket wird im Namen des angegebenen Spielers erstellt.
|
||||
// Als Erstell-Position wird die aktuelle Spieler-Location verwendet
|
||||
// (oder eine Default-Location wenn der Spieler offline ist).
|
||||
|
||||
private void handleCreateConsole(CommandSender console, String[] args) {
|
||||
// args[0] = "create", args[1] = Spielername, args[2..] = [Kategorie] [Priorität] Text
|
||||
if (args.length < 3) {
|
||||
console.sendMessage("[TicketSystem] Verwendung: /ticket create <Spielername> [Kategorie] [Priorität] <Beschreibung>");
|
||||
console.sendMessage("[TicketSystem] Beispiel: /ticket create Notch bug high Spieler fällt durch den Boden");
|
||||
return;
|
||||
}
|
||||
|
||||
String targetName = args[1];
|
||||
|
||||
// Spieler suchen (online bevorzugt, sonst OfflinePlayer)
|
||||
@SuppressWarnings("deprecation")
|
||||
OfflinePlayer offlineTarget = Bukkit.getOfflinePlayer(targetName);
|
||||
UUID targetUUID = offlineTarget.getUniqueId();
|
||||
String resolvedName = offlineTarget.getName() != null ? offlineTarget.getName() : targetName;
|
||||
|
||||
CategoryManager cm = plugin.getCategoryManager();
|
||||
ConfigCategory category = cm.getDefault();
|
||||
TicketPriority priority = TicketPriority.NORMAL;
|
||||
int msgStart = 2; // Index ab dem die Beschreibung beginnt (relativ zu args[2..])
|
||||
|
||||
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
|
||||
// args[2] = optional Kategorie, args[3] = optional Priorität, Rest = Text
|
||||
if (args.length >= 4 && categoriesOn) {
|
||||
ConfigCategory parsedCat = cm.resolve(args[2]);
|
||||
if (parsedCat != null) {
|
||||
category = parsedCat;
|
||||
msgStart = 3;
|
||||
if (prioritiesOn && args.length >= 5) {
|
||||
TicketPriority parsedPrio = parsePriority(args[3]);
|
||||
if (parsedPrio != null) { priority = parsedPrio; msgStart = 4; }
|
||||
}
|
||||
} else if (prioritiesOn) {
|
||||
TicketPriority parsedPrio = parsePriority(args[2]);
|
||||
if (parsedPrio != null) { priority = parsedPrio; msgStart = 3; }
|
||||
}
|
||||
} else if (args.length >= 4 && prioritiesOn) {
|
||||
TicketPriority parsedPrio = parsePriority(args[2]);
|
||||
if (parsedPrio != null) { priority = parsedPrio; msgStart = 3; }
|
||||
}
|
||||
|
||||
// Beschreibung zusammensetzen (args[msgStart+1] weil args[1]=Spielername)
|
||||
int absoluteStart = msgStart + 1; // +1 für den Spielernamen in args[1]
|
||||
if (absoluteStart >= args.length) {
|
||||
console.sendMessage("[TicketSystem] Fehler: Keine Beschreibung angegeben.");
|
||||
console.sendMessage("[TicketSystem] Verwendung: /ticket create <Spielername> [Kategorie] [Priorität] <Beschreibung>");
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, absoluteStart, args.length));
|
||||
int maxLen = plugin.getConfig().getInt("max-description-length", 100);
|
||||
|
||||
if (message.isEmpty()) {
|
||||
console.sendMessage("[TicketSystem] Fehler: Beschreibung darf nicht leer sein.");
|
||||
return;
|
||||
}
|
||||
if (message.length() > maxLen) {
|
||||
console.sendMessage("[TicketSystem] Fehler: Beschreibung zu lang (max. " + maxLen + " Zeichen).");
|
||||
return;
|
||||
}
|
||||
|
||||
// Location: online-Spieler → aktuelle Pos; offline → Spawn der Default-Welt
|
||||
Location location;
|
||||
Player onlineTarget = Bukkit.getPlayer(targetUUID);
|
||||
if (onlineTarget != null) {
|
||||
location = onlineTarget.getLocation();
|
||||
} else {
|
||||
World defaultWorld = Bukkit.getWorlds().isEmpty() ? null : Bukkit.getWorlds().get(0);
|
||||
location = defaultWorld != null
|
||||
? defaultWorld.getSpawnLocation()
|
||||
: new Location(null, 0, 64, 0);
|
||||
}
|
||||
|
||||
final ConfigCategory fCat = category;
|
||||
final TicketPriority fPrio = priority;
|
||||
final UUID fUUID = targetUUID;
|
||||
final String fName = resolvedName;
|
||||
final Location fLocation = location;
|
||||
final String fMessage = message;
|
||||
|
||||
Ticket ticket = new Ticket(fUUID, fName, fMessage, fLocation);
|
||||
ticket.setCategoryKey(fCat.getKey());
|
||||
ticket.setPriority(fPrio);
|
||||
ticket.setServerName(plugin.getServerName());
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int id = plugin.getDatabaseManager().createTicket(ticket);
|
||||
if (id == -1) {
|
||||
console.sendMessage("[TicketSystem] Fehler: Ticket konnte nicht in der Datenbank gespeichert werden.");
|
||||
return;
|
||||
}
|
||||
ticket.setId(id);
|
||||
plugin.getTicketCache().put(ticket);
|
||||
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
boolean catOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
String catInfo = catOn ? " [" + fCat.getName() + "]" : "";
|
||||
String prioInfo = prioOn ? " [" + fPrio.getDisplayName() + "]" : "";
|
||||
|
||||
console.sendMessage("[TicketSystem] Ticket #" + id + " für Spieler '" + fName
|
||||
+ "' erstellt." + catInfo + prioInfo);
|
||||
console.sendMessage("[TicketSystem] Nachricht: " + fMessage);
|
||||
|
||||
// Team benachrichtigen (gleich wie bei normalem /ticket create)
|
||||
plugin.getTicketManager().notifyTeam(ticket);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── /ticket reload (Konsole) ──────────────────────────────────────────
|
||||
|
||||
private void handleReloadConsole(CommandSender console) {
|
||||
plugin.reloadConfig();
|
||||
plugin.lang().reload();
|
||||
plugin.reloadSettings();
|
||||
plugin.getTicketGUI().reloadConfig();
|
||||
plugin.getTicketGUI().reloadTitles();
|
||||
plugin.getFaqGUI().reloadConfig();
|
||||
plugin.getCategoryManager().reload();
|
||||
plugin.getFaqManager().reload();
|
||||
plugin.getTicketCache().clear();
|
||||
console.sendMessage("[TicketSystem] Konfiguration erfolgreich neu geladen.");
|
||||
}
|
||||
|
||||
// ── /ticket archive (Konsole) ─────────────────────────────────────────
|
||||
|
||||
private void handleArchiveConsole(CommandSender console) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
int count = plugin.getDatabaseManager().archiveClosedTickets();
|
||||
Bukkit.getScheduler().runTask(plugin, () ->
|
||||
console.sendMessage("[TicketSystem] " + count + " Ticket(s) archiviert."));
|
||||
});
|
||||
}
|
||||
|
||||
// ── /ticket stats (Konsole) ───────────────────────────────────────────
|
||||
|
||||
private void handleStatsConsole(CommandSender console) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
var stats = plugin.getDatabaseManager().getTicketStats();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
console.sendMessage("[TicketSystem] === Statistiken ===");
|
||||
console.sendMessage("[TicketSystem] Gesamt: " + stats.total);
|
||||
console.sendMessage("[TicketSystem] Offen: " + stats.open);
|
||||
console.sendMessage("[TicketSystem] Geschlossen: " + stats.closed);
|
||||
console.sendMessage("[TicketSystem] Weitergeleitet: " + stats.forwarded);
|
||||
if (plugin.getConfig().getBoolean("rating-enabled", true)) {
|
||||
console.sendMessage("[TicketSystem] Bewertungen: 👍 " + stats.thumbsUp + " 👎 " + stats.thumbsDown);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Konsolen-Hilfe ────────────────────────────────────────────────────
|
||||
|
||||
private void sendConsoleHelp(CommandSender console) {
|
||||
console.sendMessage("[TicketSystem] Verfügbare Konsolen-Befehle:");
|
||||
console.sendMessage("[TicketSystem] /ticket create <Spieler> [Kategorie] [Priorität] <Text>");
|
||||
console.sendMessage("[TicketSystem] /ticket reload");
|
||||
console.sendMessage("[TicketSystem] /ticket archive");
|
||||
console.sendMessage("[TicketSystem] /ticket stats");
|
||||
}
|
||||
|
||||
// ── /ticket create ────────────────────────────────────────────────────
|
||||
|
||||
private void handleCreate(Player player, String[] args) {
|
||||
@@ -136,8 +325,8 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
TicketPriority priority = TicketPriority.NORMAL;
|
||||
int msgStart = 1;
|
||||
|
||||
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
boolean allowPlayersPrio = plugin.getConfig().getBoolean("allow-players-to-set-priority", false);
|
||||
boolean isTeam = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
|
||||
|
||||
@@ -163,8 +352,8 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, msgStart, args.length));
|
||||
int maxLen = plugin.getConfig().getInt("max-description-length", 100);
|
||||
if (message.isEmpty()) { plugin.lang().send(player, "create.no-description"); return; }
|
||||
if (message.length() > maxLen) { plugin.lang().send(player, "create.too-long", "{max}", String.valueOf(maxLen)); return; }
|
||||
if (message.isEmpty()) { plugin.lang().send(player, "create.no-description"); return; }
|
||||
if (message.length() > maxLen) { plugin.lang().send(player, "create.too-long", "{max}", String.valueOf(maxLen)); return; }
|
||||
|
||||
final ConfigCategory fCat = category;
|
||||
final TicketPriority fPrio = priority;
|
||||
@@ -535,17 +724,12 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
plugin.lang().send(player, "general.no-permission"); return;
|
||||
}
|
||||
// Reihenfolge zwingend:
|
||||
// 1. Config neu laden (liest language neu ein)
|
||||
// 2. lang().reload() (liest language aus der frischen Config, baut cmdNames neu)
|
||||
// 3. GUI reloadConfig() (liest Rows/Slots aus der frischen Config)
|
||||
// 4. weitere Manager (nutzen ggf. frische lang-Texte)
|
||||
plugin.reloadConfig();
|
||||
plugin.lang().reload();
|
||||
plugin.reloadSettings(); // serverName & debug-Flag aktualisieren
|
||||
plugin.getTicketGUI().reloadConfig(); // Slots, Rows & Materialien neu laden
|
||||
plugin.getTicketGUI().reloadTitles(); // Inventar-Titel aktualisieren
|
||||
plugin.getFaqGUI().reloadConfig(); // FAQ-Slots, Rows & Materialien neu laden
|
||||
plugin.reloadSettings();
|
||||
plugin.getTicketGUI().reloadConfig();
|
||||
plugin.getTicketGUI().reloadTitles();
|
||||
plugin.getFaqGUI().reloadConfig();
|
||||
plugin.getCategoryManager().reload();
|
||||
plugin.getFaqManager().reload();
|
||||
plugin.getTicketCache().clear();
|
||||
@@ -565,8 +749,7 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
java.io.File backup = plugin.getDatabaseManager().createBackup();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
if (backup != null) {
|
||||
player.sendMessage(plugin.lang().format("backup.success",
|
||||
"{file}", backup.getName()));
|
||||
player.sendMessage(plugin.lang().format("backup.success", "{file}", backup.getName()));
|
||||
} else {
|
||||
player.sendMessage(plugin.lang().get("backup.fail"));
|
||||
}
|
||||
@@ -761,6 +944,12 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
// ── /ticket faq ───────────────────────────────────────────────────────
|
||||
|
||||
private void handleFaq(Player player, String[] args) {
|
||||
// FAQ-System global deaktiviert?
|
||||
if (!plugin.getConfig().getBoolean("faq-enabled", true)) {
|
||||
plugin.lang().send(player, "faq.disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
plugin.getFaqGUI().openFaqGUI(player);
|
||||
return;
|
||||
@@ -777,7 +966,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
player.sendMessage(plugin.lang().get("faq.usage-add-example")); return;
|
||||
}
|
||||
|
||||
// Prüfen ob args[2] ein bekannter FAQ-Kategorie-Schlüssel ist
|
||||
String faqCatKey = null;
|
||||
int textStart = 2;
|
||||
if (plugin.getFaqManager().hasCategoriesEnabled()) {
|
||||
@@ -861,11 +1049,9 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
plugin.lang().send(player, "general.no-permission"); return;
|
||||
}
|
||||
// Richtung bestimmen: tofile oder tomysql (Standard)
|
||||
String direction = args.length >= 3 ? args[2].toLowerCase() : "tomysql";
|
||||
|
||||
if (direction.equals("tofile") || direction.equals("zudatei")) {
|
||||
// MySQL → faqs.yml
|
||||
if (!plugin.getFaqManager().isUsingMySQL()) {
|
||||
player.sendMessage(plugin.lang().get("faq.migrate-no-mysql")); return;
|
||||
}
|
||||
@@ -883,7 +1069,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// faqs.yml → MySQL
|
||||
if (!plugin.getFaqManager().isUsingMySQL()) {
|
||||
player.sendMessage(plugin.lang().get("faq.migrate-no-mysql")); return;
|
||||
}
|
||||
@@ -931,19 +1116,16 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
|
||||
// ── /ticket kategorie (category) ─────────────────────────────────────
|
||||
//
|
||||
// /ticket kategorie add <Name> [Farbe] [Beschreibung]
|
||||
// Farbe optional, z.B. &b (Standard: &7)
|
||||
// Beschreibung optional, alles nach der Farbe
|
||||
//
|
||||
// /ticket kategorie delete <Schlüssel>
|
||||
// /ticket kategorie list
|
||||
// ── /ticket kategorie ─────────────────────────────────────────────────
|
||||
|
||||
private void handleFaqCategory(Player player, String[] args) {
|
||||
if (!player.hasPermission("ticket.admin")) {
|
||||
plugin.lang().send(player, "general.no-permission"); return;
|
||||
}
|
||||
// FAQ-System deaktiviert?
|
||||
if (!plugin.getConfig().getBoolean("faq-enabled", true)) {
|
||||
plugin.lang().send(player, "faq.disabled"); return;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(plugin.lang().get("faqcat.usage")); return;
|
||||
}
|
||||
@@ -951,7 +1133,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
switch (args[1].toLowerCase()) {
|
||||
|
||||
case "add", "hinzufügen", "hinzufuegen" -> {
|
||||
// /ticket kategorie add <Name> [&Farbe] [Beschreibung...]
|
||||
if (args.length < 3) {
|
||||
player.sendMessage(plugin.lang().get("faqcat.usage-add")); return;
|
||||
}
|
||||
@@ -1069,9 +1250,25 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
// Konsolen-Tab-Completion
|
||||
if (sender instanceof ConsoleCommandSender) {
|
||||
if (args.length == 1) {
|
||||
for (String s : List.of("create", "reload", "archive", "stats"))
|
||||
if (s.startsWith(args[0].toLowerCase())) completions.add(s);
|
||||
} else if (args.length == 2 && normalize(args[0]).equals("create")) {
|
||||
// Spielernamen vorschlagen
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
if (p.getName().toLowerCase().startsWith(args[1].toLowerCase())) completions.add(p.getName());
|
||||
} else if (args.length == 3 && normalize(args[0]).equals("create")) {
|
||||
// Kategorien vorschlagen
|
||||
completions.addAll(plugin.getCategoryManager().getDisplayNamesForTabComplete(args[2]));
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) return completions;
|
||||
|
||||
// Nur die in der Config eingestellte Sprache verwenden
|
||||
final boolean useDe = plugin.lang().acceptsGerman();
|
||||
final boolean useEn = plugin.lang().acceptsEnglish();
|
||||
|
||||
@@ -1091,11 +1288,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
if (useDe) subs.add("priorität");
|
||||
}
|
||||
|
||||
if (player.hasPermission("ticket.admin")) {
|
||||
if (useEn) subs.addAll(List.of("faq"));
|
||||
if (useDe) subs.addAll(List.of("faq"));
|
||||
}
|
||||
|
||||
if (player.hasPermission("ticket.admin")) {
|
||||
subs.add("kategorie");
|
||||
if (useEn) subs.add("category");
|
||||
@@ -1123,7 +1315,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
&& (args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("hinzufügen")
|
||||
|| args[1].equalsIgnoreCase("hinzufuegen"))
|
||||
&& player.hasPermission("ticket.admin")) {
|
||||
// /ticket faq add <TAB> → FAQ-Kategorie-Schlüssel vorschlagen
|
||||
completions.addAll(getFaqCategoryKeysForTab(args[2]));
|
||||
|
||||
} else if (args.length == 3 && normalize(args[0]).equals("faq")
|
||||
@@ -1136,7 +1327,6 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
} else if (args.length == 2 && (normalize(args[0]).equals("category")
|
||||
|| args[0].equalsIgnoreCase("kategorie"))
|
||||
&& player.hasPermission("ticket.admin")) {
|
||||
// /ticket kategorie <TAB>
|
||||
if (useEn) completions.addAll(List.of("add", "delete", "list"));
|
||||
if (useDe) completions.addAll(List.of("hinzufügen", "löschen", "liste"));
|
||||
completions.removeIf(s -> !s.startsWith(args[1].toLowerCase()));
|
||||
@@ -1146,21 +1336,17 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
&& (args[1].equalsIgnoreCase("delete") || args[1].equalsIgnoreCase("remove")
|
||||
|| args[1].equalsIgnoreCase("löschen") || args[1].equalsIgnoreCase("loeschen"))
|
||||
&& player.hasPermission("ticket.admin")) {
|
||||
// /ticket kategorie delete <TAB> → vorhandene Schlüssel
|
||||
completions.addAll(getFaqCategoryKeysForTab(args[2]));
|
||||
|
||||
} else if (args.length == 2 && normalize(args[0]).equals("create")) {
|
||||
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
|
||||
// Wenn Kategorien aktiviert: zeige nur Kategorien-Anzeigenamen bei args[1]
|
||||
|
||||
if (categoriesOn) {
|
||||
completions.addAll(plugin.getCategoryManager().getDisplayNamesForTabComplete(args[1]));
|
||||
} else if (prioritiesOn) {
|
||||
// Wenn nur Prioritäten aktiviert (keine Kategorien): zeige Prioritäten bei args[1]
|
||||
boolean allowPlayersPrio = plugin.getConfig().getBoolean("allow-players-to-set-priority", false);
|
||||
boolean isTeam = sender instanceof Player p &&
|
||||
(p.hasPermission("ticket.support") || p.hasPermission("ticket.admin"));
|
||||
boolean isTeam = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
|
||||
if (allowPlayersPrio || isTeam) {
|
||||
completions.addAll(getPriorityInputsForTab(args[1]));
|
||||
}
|
||||
@@ -1169,12 +1355,10 @@ public class TicketCommand implements CommandExecutor, TabCompleter {
|
||||
} else if (args.length == 3 && normalize(args[0]).equals("create")) {
|
||||
boolean categoriesOn = plugin.getConfig().getBoolean("categories-enabled", true);
|
||||
boolean prioritiesOn = plugin.getConfig().getBoolean("priorities-enabled", true);
|
||||
|
||||
// Wenn beide aktiviert: args[2] = Priorität
|
||||
|
||||
if (categoriesOn && prioritiesOn) {
|
||||
boolean allowPlayersPrio = plugin.getConfig().getBoolean("allow-players-to-set-priority", false);
|
||||
boolean isTeam = sender instanceof Player p &&
|
||||
(p.hasPermission("ticket.support") || p.hasPermission("ticket.admin"));
|
||||
boolean isTeam = player.hasPermission("ticket.support") || player.hasPermission("ticket.admin");
|
||||
if (allowPlayersPrio || isTeam) {
|
||||
completions.addAll(getPriorityInputsForTab(args[2]));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package de.ticketsystem.manager;
|
||||
|
||||
import de.ticketsystem.TicketPlugin;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -20,8 +26,13 @@ import java.util.regex.Pattern;
|
||||
* Lädt alle Plugin-Texte aus der aktiven Sprachdatei (lang_de.yml / lang_en.yml)
|
||||
* und ersetzt {cmd_X}-Platzhalter durch die passenden Befehlsnamen.
|
||||
*
|
||||
* Unterstützt Hex-Farbcodes (z.B. &#FF0055 oder <#FF0055>).
|
||||
* Funktioniert ab Spigot 1.16+.
|
||||
* Unterstützt:
|
||||
* - &-Farbcodes und Hex-Farbcodes (&#RRGGBB oder <#RRGGBB>)
|
||||
* - MiniMessage-Tags wie <click:run_command:...>, <hover:show_text:...>, <bold> etc.
|
||||
* → Texte mit MiniMessage-Tags werden als Adventure-Component gesendet,
|
||||
* damit click/hover-Events tatsächlich funktionieren.
|
||||
*
|
||||
* Funktioniert ab Paper/Spigot 1.16+ (Adventure-API vorausgesetzt, ab 1.18 eingebaut).
|
||||
*
|
||||
* ┌─────────────────────────────────────────────────────────┐
|
||||
* │ Einziger Konfigurations-Schlüssel: language │
|
||||
@@ -29,9 +40,6 @@ import java.util.regex.Pattern;
|
||||
* │ language: de → deutsche Texte + deutsche Befehle │
|
||||
* │ language: en → englische Texte + englische Befehle │
|
||||
* │ language: both → deutsche Texte + beide Befehlsnamen │
|
||||
* │ │
|
||||
* │ "command-language" existiert nicht mehr und wird │
|
||||
* │ vollständig ignoriert. │
|
||||
* └─────────────────────────────────────────────────────────┘
|
||||
*
|
||||
* Verfügbare {cmd_X}-Platzhalter in lang.yml:
|
||||
@@ -53,6 +61,17 @@ public class LanguageManager {
|
||||
"blacklist.list-", "gui.", "join.pending-header", "update."
|
||||
};
|
||||
|
||||
/**
|
||||
* Erkennt MiniMessage-Tags, die interaktive Inhalte enthalten:
|
||||
* click, hover, insertion. Nur wenn solche Tags vorhanden sind,
|
||||
* wird die Nachricht als Component (statt als Legacy-String) gesendet.
|
||||
*/
|
||||
private static final Pattern MINIMESSAGE_INTERACTIVE =
|
||||
Pattern.compile("<(?:click|hover|insertion)[^>]*>", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/** MiniMessage-Instanz (singleton, thread-safe). */
|
||||
private static final MiniMessage MINI = MiniMessage.miniMessage();
|
||||
|
||||
// ── Befehlsnamen-Tabellen (statisch, ändern sich nie) ───────────────────
|
||||
|
||||
private static final LinkedHashMap<String, String> DE = new LinkedHashMap<>();
|
||||
@@ -104,7 +123,6 @@ public class LanguageManager {
|
||||
|
||||
/**
|
||||
* Aktiver Sprachmodus – wird bei jedem load() DIREKT aus der Config gelesen.
|
||||
* Kein Cache, kein Zwischenwert. Immer frisch nach reloadConfig().
|
||||
*/
|
||||
private String activeLang;
|
||||
|
||||
@@ -120,23 +138,26 @@ public class LanguageManager {
|
||||
*/
|
||||
private Map<String, String> cmdNames = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* BukkitAudiences – Brücke zwischen Adventure-Components und Spigot.
|
||||
* Muss beim Plugin-onDisable via audiences.close() geschlossen werden.
|
||||
* Siehe TicketPlugin.onDisable().
|
||||
*/
|
||||
private final BukkitAudiences audiences;
|
||||
|
||||
// ── Konstruktor ──────────────────────────────────────────────────────────
|
||||
|
||||
public LanguageManager(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.plugin = plugin;
|
||||
this.audiences = BukkitAudiences.create(plugin);
|
||||
load();
|
||||
}
|
||||
|
||||
// ── Laden ────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Lädt (oder relädt) die Sprachdatei und baut alle Befehlsnamen neu.
|
||||
* Muss nach plugin.reloadConfig() aufgerufen werden, damit die frische
|
||||
* language-Einstellung übernommen wird.
|
||||
*/
|
||||
public void load() {
|
||||
|
||||
// 1. language aus der (bereits neu geladenen) Config lesen
|
||||
// 1. language aus der Config lesen
|
||||
String raw = plugin.getConfig().getString("language", FALLBACK)
|
||||
.toLowerCase().trim();
|
||||
|
||||
@@ -204,20 +225,13 @@ public class LanguageManager {
|
||||
|
||||
// ── Befehlsnamen ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Baut {cmd_X} → Anzeigename anhand von activeLang.
|
||||
*
|
||||
* de → /ticket erstellen
|
||||
* en → /ticket create
|
||||
* both → /ticket create §8(§7erstellen§8)
|
||||
*/
|
||||
private Map<String, String> buildCmdNames() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (String key : EN.keySet()) {
|
||||
String display = switch (activeLang) {
|
||||
case "en" -> "/ticket " + EN.get(key);
|
||||
case "both" -> "/ticket " + EN.get(key) + " §8(§7" + DE.get(key) + "§8)";
|
||||
default -> "/ticket " + DE.get(key); // "de" + alle unbekannten
|
||||
default -> "/ticket " + DE.get(key);
|
||||
};
|
||||
map.put("{cmd_" + key + "}", display);
|
||||
}
|
||||
@@ -232,12 +246,9 @@ public class LanguageManager {
|
||||
};
|
||||
}
|
||||
|
||||
// ── Befehlssprache-Abfragen (für TicketCommand) ──────────────────────────
|
||||
// ── Befehlssprache-Abfragen ──────────────────────────────────────────────
|
||||
|
||||
/** true wenn deutsche Subkommandos akzeptiert werden sollen (Tab-Complete & Eingabe). */
|
||||
public boolean acceptsGerman() { return "de".equals(activeLang) || "both".equals(activeLang); }
|
||||
|
||||
/** true wenn englische Subkommandos akzeptiert werden sollen (Tab-Complete & Eingabe). */
|
||||
public boolean acceptsEnglish() { return "en".equals(activeLang) || "both".equals(activeLang); }
|
||||
|
||||
// ── Interne Platzhalter-Ersetzung ───────────────────────────────────────
|
||||
@@ -281,11 +292,48 @@ public class LanguageManager {
|
||||
return prefix + format(key, replacements);
|
||||
}
|
||||
|
||||
/** Sendet eine Nachricht (mit Prefix wenn nötig) an einen CommandSender. */
|
||||
/**
|
||||
* Sendet eine Nachricht an einen CommandSender.
|
||||
*
|
||||
* Enthält der Rohtext MiniMessage-Tags mit interaktiven Elementen
|
||||
* (click, hover, insertion), wird die Nachricht als Adventure-Component
|
||||
* gesendet – damit run_command, suggest_command und hover-Texte
|
||||
* tatsächlich funktionieren.
|
||||
*
|
||||
* Enthält er keine solchen Tags, wird der klassische Legacy-Pfad
|
||||
* (§-Codes) verwendet – volle Abwärtskompatibilität.
|
||||
*/
|
||||
public void send(CommandSender sender, String key, String... replacements) {
|
||||
sender.sendMessage(needsPrefix(key)
|
||||
? prefix + format(key, replacements)
|
||||
: format(key, replacements));
|
||||
String raw = applyCmdNames(getRaw(key));
|
||||
|
||||
// {placeholder}-Ersetzungen anwenden (vor MiniMessage-Parsing!)
|
||||
if (replacements.length % 2 != 0)
|
||||
plugin.getLogger().warning("[LanguageManager] send() benötigt eine gerade Anzahl an Argumenten für: " + key);
|
||||
for (int i = 0; i + 1 < replacements.length; i += 2)
|
||||
raw = raw.replace(replacements[i], replacements[i + 1]);
|
||||
|
||||
if (hasMiniMessageInteractiveTags(raw)) {
|
||||
// MiniMessage-Pfad: interaktive Komponenten (click/hover)
|
||||
// Legacy-&-Farbcodes in MiniMessage-Äquivalente umwandeln damit
|
||||
// beide Syntax-Varianten in derselben Nachricht funktionieren.
|
||||
String mm = legacyToMiniMessage(raw);
|
||||
Component component = MINI.deserialize(mm);
|
||||
|
||||
// Prefix als Component voranstellen wenn nötig
|
||||
if (needsPrefix(key)) {
|
||||
Component prefixComp = MINI.deserialize(legacyToMiniMessage(
|
||||
color("&8[&6Ticket&8] &r")));
|
||||
component = prefixComp.append(component);
|
||||
}
|
||||
|
||||
// BukkitAudiences sendet korrekt auf Spigot UND Paper
|
||||
Audience audience = audiences.sender(sender);
|
||||
audience.sendMessage(component);
|
||||
} else {
|
||||
// Legacy-Pfad: §-Codes, kein Adventure nötig
|
||||
String text = color(raw);
|
||||
sender.sendMessage(needsPrefix(key) ? prefix + text : text);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sendet die Trennlinie. */
|
||||
@@ -304,11 +352,12 @@ public class LanguageManager {
|
||||
|
||||
/**
|
||||
* Übersetzt &-Farbcodes und Hex-Farbcodes (&#RRGGBB oder <#RRGGBB>) in §-Codes.
|
||||
* Wird für alle Texte verwendet die NICHT als Component gesendet werden.
|
||||
*/
|
||||
public String color(String text) {
|
||||
if (text == null || text.isEmpty()) return "";
|
||||
|
||||
// Regex für Hex Codes: &#RRGGBB oder <#RRGGBB>
|
||||
// Hex-Codes: &#RRGGBB oder <#RRGGBB>
|
||||
Pattern hexPattern = Pattern.compile("&#([A-Fa-f0-9]{6})|<#([A-Fa-f0-9]{6})>");
|
||||
Matcher matcher = hexPattern.matcher(text);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
@@ -316,29 +365,126 @@ public class LanguageManager {
|
||||
while (matcher.find()) {
|
||||
String group = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
|
||||
try {
|
||||
// Fix: Explizite Nutzung von net.md_5.bungee.api.ChatColor für Hex-Support (Spigot 1.16+)
|
||||
// Dies verhindert den "Symbol nicht gefunden"-Fehler beim Kompilieren mit der reinen Bukkit-API.
|
||||
net.md_5.bungee.api.ChatColor hexColor = net.md_5.bungee.api.ChatColor.of("#" + group);
|
||||
matcher.appendReplacement(buffer, hexColor.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Falls der Farbcode ungültig ist, Tag entfernen
|
||||
matcher.appendReplacement(buffer, "");
|
||||
}
|
||||
}
|
||||
|
||||
String parsed = matcher.appendTail(buffer).toString();
|
||||
|
||||
// Übersetzung der klassischen &-Farbcodes (Bukkit Standard)
|
||||
return org.bukkit.ChatColor.translateAlternateColorCodes('&', parsed);
|
||||
}
|
||||
|
||||
public String getPrefix() { return prefix; }
|
||||
public String getActiveLang() { return activeLang; }
|
||||
public String getFileLang() { return fileLang; }
|
||||
public String getPrefix() { return prefix; }
|
||||
public String getActiveLang() { return activeLang; }
|
||||
public String getFileLang() { return fileLang; }
|
||||
public BukkitAudiences getAudiences() { return audiences; }
|
||||
|
||||
/** Relädt die Sprachdatei. Muss NACH plugin.reloadConfig() aufgerufen werden. */
|
||||
public void reload() { load(); }
|
||||
|
||||
// ── MiniMessage-Hilfsmethoden ────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Prüft ob ein Text MiniMessage-Tags mit interaktiven Inhalten enthält
|
||||
* (click, hover, insertion). Nur dann lohnt sich der Component-Pfad.
|
||||
*/
|
||||
private boolean hasMiniMessageInteractiveTags(String text) {
|
||||
if (text == null) return false;
|
||||
return MINIMESSAGE_INTERACTIVE.matcher(text).find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert &-Farbcodes und &#RRGGBB/<#RRGGBB>-Hex-Codes in MiniMessage-Syntax,
|
||||
* damit ein Text der beide Formate mischt korrekt von MiniMessage geparst werden kann.
|
||||
*
|
||||
* Beispiele:
|
||||
* &6Hallo → <gold>Hallo
|
||||
* &#FF5500Text → <#FF5500>Text
|
||||
* &lFett → <bold>Fett
|
||||
* &rReset → <reset>Reset
|
||||
*/
|
||||
private String legacyToMiniMessage(String text) {
|
||||
if (text == null) return "";
|
||||
|
||||
// 1. &#RRGGBB → <#RRGGBB>
|
||||
text = text.replaceAll("&#([A-Fa-f0-9]{6})", "<#$1>");
|
||||
|
||||
// 2. §-Codes (bereits konvertierte Hex-Codes §x§R§G§B... überspringen wir hier,
|
||||
// da sie nicht in MiniMessage passen – nur &-Shorthand-Codes mappen)
|
||||
// Wir ersetzen &X-Codes durch ihre MiniMessage-Äquivalente.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char[] chars = text.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if ((chars[i] == '&' || chars[i] == '§') && i + 1 < chars.length) {
|
||||
char code = Character.toLowerCase(chars[i + 1]);
|
||||
String mm = switch (code) {
|
||||
case '0' -> "<black>";
|
||||
case '1' -> "<dark_blue>";
|
||||
case '2' -> "<dark_green>";
|
||||
case '3' -> "<dark_aqua>";
|
||||
case '4' -> "<dark_red>";
|
||||
case '5' -> "<dark_purple>";
|
||||
case '6' -> "<gold>";
|
||||
case '7' -> "<gray>";
|
||||
case '8' -> "<dark_gray>";
|
||||
case '9' -> "<blue>";
|
||||
case 'a' -> "<green>";
|
||||
case 'b' -> "<aqua>";
|
||||
case 'c' -> "<red>";
|
||||
case 'd' -> "<light_purple>";
|
||||
case 'e' -> "<yellow>";
|
||||
case 'f' -> "<white>";
|
||||
case 'l' -> "<bold>";
|
||||
case 'm' -> "<strikethrough>";
|
||||
case 'n' -> "<underlined>";
|
||||
case 'o' -> "<italic>";
|
||||
case 'r' -> "<reset>";
|
||||
default -> null;
|
||||
};
|
||||
if (mm != null) {
|
||||
sb.append(mm);
|
||||
i++; // Code-Zeichen überspringen
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sb.append(chars[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sendet eine Adventure-Component an einen CommandSender.
|
||||
*
|
||||
* Der Cast auf net.kyori.adventure.audience.Audience umgeht das compile-time
|
||||
* Problem, dass Spigots CommandSender-Interface sendMessage(Component) nicht
|
||||
* direkt exponiert – zur Laufzeit implementiert jeder CommandSender auf
|
||||
* Paper/Spigot 1.18+ das Audience-Interface.
|
||||
*/
|
||||
private void sendComponent(CommandSender sender, Component component) {
|
||||
if (sender instanceof net.kyori.adventure.audience.Audience audience) {
|
||||
try {
|
||||
audience.sendMessage(component);
|
||||
} catch (NoSuchMethodError | AbstractMethodError e) {
|
||||
// Fallback für reines Spigot ohne Adventure-Patch:
|
||||
// Component in Legacy-String serialisieren (click/hover gehen verloren,
|
||||
// aber der Text bleibt lesbar)
|
||||
String legacy = LegacyComponentSerializer.legacySection().serialize(component);
|
||||
sender.sendMessage(legacy);
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("[LanguageManager] Adventure-API nicht verfügbar – "
|
||||
+ "interaktive Tags werden als Plain-Text gesendet. "
|
||||
+ "Wechsel auf Paper für volle MiniMessage-Unterstützung.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Kein Audience (z. B. alte Konsole) → Legacy-Fallback
|
||||
String legacy = LegacyComponentSerializer.legacySection().serialize(component);
|
||||
sender.sendMessage(legacy);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Intern ───────────────────────────────────────────────────────────────
|
||||
|
||||
private boolean needsPrefix(String key) {
|
||||
|
||||
@@ -209,6 +209,9 @@ discord:
|
||||
# ============================================================
|
||||
# GUI KONFIGURATION (Layouts, Slots, Items)
|
||||
# ============================================================
|
||||
# FAQ-System global deaktivieren (Standard: true)
|
||||
faq-enabled: true
|
||||
|
||||
# Hier kannst du das Aussehen und die Anordnung der GUIs anpassen.
|
||||
# WICHTIG: gui-settings muss ganz links stehen (keine Raute davor!).
|
||||
gui-settings:
|
||||
|
||||
@@ -576,7 +576,6 @@ web:
|
||||
login-blocked: "Zu viele Fehlversuche. Bitte warte {seconds} Sekunden."
|
||||
archive-btn-restore: "Wiederherstellen"
|
||||
archive-btn-delete: "Permanent löschen"
|
||||
detail-btn-archive: "Ins Archiv verschieben"
|
||||
login-label-user: "Benutzername"
|
||||
login-label-pass: "Passwort"
|
||||
login-btn: "Anmelden"
|
||||
@@ -615,13 +614,11 @@ web:
|
||||
tickets-col-prio: "Priorität"
|
||||
tickets-col-status: "Status"
|
||||
tickets-col-created: "Erstellt"
|
||||
nav-archive: "Archiv"
|
||||
filter-all-status: "Alle Status"
|
||||
filter-open: "Offen"
|
||||
filter-claimed: "Angenommen"
|
||||
filter-forwarded: "Weitergeleitet"
|
||||
filter-closed: "Geschlossen"
|
||||
filter-archived: "Archiviert"
|
||||
filter-all-cat: "Alle Kategorien"
|
||||
filter-all-prio: "Alle Prioritäten"
|
||||
filter-low: "Niedrig"
|
||||
|
||||
@@ -576,7 +576,6 @@ web:
|
||||
login-error: "Username or password incorrect."
|
||||
archive-btn-restore: "Restore"
|
||||
archive-btn-delete: "Delete permanently"
|
||||
detail-btn-archive: "Move to archive"
|
||||
login-label-user: "Username"
|
||||
login-label-pass: "Password"
|
||||
login-btn: "Sign In"
|
||||
@@ -615,13 +614,11 @@ web:
|
||||
tickets-col-prio: "Priority"
|
||||
tickets-col-status: "Status"
|
||||
tickets-col-created: "Created"
|
||||
nav-archive: "Archive"
|
||||
filter-all-status: "All Statuses"
|
||||
filter-open: "Open"
|
||||
filter-claimed: "Claimed"
|
||||
filter-forwarded: "Forwarded"
|
||||
filter-closed: "Closed"
|
||||
filter-archived: "Archived"
|
||||
filter-all-cat: "All Categories"
|
||||
filter-all-prio: "All Priorities"
|
||||
filter-low: "Low"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: TicketSystem
|
||||
version: 1.1.4
|
||||
version: 1.1.5
|
||||
main: de.ticketsystem.TicketPlugin
|
||||
api-version: 1.20
|
||||
author: M_Viper
|
||||
|
||||
Reference in New Issue
Block a user