From 3dfbd33656e279d80cde0b8d35e8c01c83cad38a Mon Sep 17 00:00:00 2001 From: Git Manager GUI Date: Thu, 16 Apr 2026 19:56:44 +0200 Subject: [PATCH] Upload folder via GUI - src --- .../java/de/ticketsystem/TicketPlugin.java | 54 ++++++++++++++++--- .../database/DatabaseManager.java | 52 ++++++++++++++++++ .../ticketsystem/web/handlers/ApiHandler.java | 17 +++++- .../web/handlers/StaticHandler.java | 21 ++++++++ .../web/handlers/TicketsHandler.java | 10 ++++ src/main/resources/lang_de.yml | 3 ++ src/main/resources/lang_en.yml | 3 ++ src/main/resources/plugin.yml | 2 +- 8 files changed, 152 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/ticketsystem/TicketPlugin.java b/src/main/java/de/ticketsystem/TicketPlugin.java index b617c7d..baf81db 100644 --- a/src/main/java/de/ticketsystem/TicketPlugin.java +++ b/src/main/java/de/ticketsystem/TicketPlugin.java @@ -93,13 +93,8 @@ public class TicketPlugin extends JavaPlugin { } }); - // Versionsprüfung der config.yml - String configVersion = getConfig().getString("version", ""); - String expectedVersion = "2.5"; - if (!expectedVersion.equals(configVersion)) { - getLogger().warning("[WARNUNG] config.yml-Version (" + configVersion - + ") stimmt nicht mit der erwarteten Version (" + expectedVersion + ") überein!"); - } + // Versionsprüfung & automatische Migration der config.yml + migrateConfig(); debug = getConfig().getBoolean("debug", false); @@ -269,7 +264,50 @@ public class TicketPlugin extends JavaPlugin { }; } - // ─────────────────────────── Getter ──────────────────────────────────── + // ─────────────────────────── Config-Migration ───────────────────────── + + /** + * Ergänzt fehlende Schlüssel in der config.yml automatisch aus der + * Standardkonfiguration (config.yml im JAR) und aktualisiert die Version. + * Bestehende Werte des Servers werden dabei NICHT überschrieben. + */ + private void migrateConfig() { + final String EXPECTED = "2.5"; + String current = getConfig().getString("version", ""); + + if (EXPECTED.equals(current)) return; // nichts zu tun + + getLogger().info("[Config] Alte Version erkannt (" + current + "). Starte automatische Migration auf " + EXPECTED + " ..."); + + // Alle Werte aus der Jar-Default-Config als Fallback setzen + // (addDefault überschreibt KEINE bereits gesetzten Werte) + org.bukkit.configuration.file.YamlConfiguration defaults = + org.bukkit.configuration.file.YamlConfiguration.loadConfiguration( + new java.io.InputStreamReader( + java.util.Objects.requireNonNull(getResource("config.yml")), + java.nio.charset.StandardCharsets.UTF_8)); + + int added = 0; + for (String key : defaults.getKeys(true)) { + if (!getConfig().contains(key)) { + getConfig().set(key, defaults.get(key)); + getLogger().info("[Config] Neuer Schlüssel ergänzt: " + key); + added++; + } + } + + // Version hochsetzen + getConfig().set("version", EXPECTED); + saveConfig(); + + if (added > 0) { + getLogger().info("[Config] Migration abgeschlossen: " + added + " neue Schlüssel ergänzt, Version → " + EXPECTED); + } else { + getLogger().info("[Config] Migration abgeschlossen: Keine neuen Schlüssel. Version → " + EXPECTED); + } + } + + // ─────────────────────────── Getter ──────────────────────────────────── /** * Aktualisiert serverName und debug-Flag aus der (bereits neu geladenen) Config. diff --git a/src/main/java/de/ticketsystem/database/DatabaseManager.java b/src/main/java/de/ticketsystem/database/DatabaseManager.java index 3dd1014..f692f51 100644 --- a/src/main/java/de/ticketsystem/database/DatabaseManager.java +++ b/src/main/java/de/ticketsystem/database/DatabaseManager.java @@ -1330,6 +1330,58 @@ public class DatabaseManager { // ─────────────────────────── Archivierung ────────────────────────────── + /** + * Verschiebt ein einzelnes geschlossenes Ticket ins Archiv. + * Das Ticket wird aus der aktiven Datenbank entfernt und in archive.json / archive.yml gespeichert. + * + * @param ticketId ID des zu archivierenden Tickets + * @return true wenn erfolgreich archiviert + */ + @SuppressWarnings("unchecked") + public boolean archiveTicket(int ticketId) { + Ticket t = getTicketById(ticketId); + if (t == null) return false; + + // Ins Archiv schreiben + File archiveFile = new File(plugin.getDataFolder(), archiveFileName); + JSONArray arr = new JSONArray(); + if (archiveFile.exists()) { + try (FileReader fr = new FileReader(archiveFile)) { + Object parsed = new JSONParser().parse(fr); + if (parsed instanceof JSONArray oldArr) arr.addAll(oldArr); + } catch (Exception ignored) {} + } + arr.add(ticketToJson(t)); + try (FileWriter fw = new FileWriter(archiveFile)) { + fw.write(arr.toJSONString()); + } catch (Exception e) { + sendError("Fehler beim Archivieren von Ticket #" + ticketId + ": " + e.getMessage()); + return false; + } + + // Aus aktiver DB entfernen + if (useMySQL) { + try (Connection conn = getConnection(); + PreparedStatement ps = conn.prepareStatement("DELETE FROM tickets WHERE id = ?")) { + ps.setInt(1, ticketId); + ps.executeUpdate(); + } catch (Exception e) { + sendError("Fehler beim Entfernen von Ticket #" + ticketId + " aus DB: " + e.getMessage()); + return false; + } + } else { + dataConfig.set("tickets." + ticketId, null); + try { dataConfig.save(dataFile); } catch (Exception e) { + sendError("Fehler beim Speichern nach Archivierung: " + e.getMessage()); + return false; + } + } + + if (plugin != null) + plugin.getLogger().info("[Archiv] Ticket #" + ticketId + " manuell archiviert."); + return true; + } + public int archiveClosedTickets() { List all = getAllTickets(); List toArchive = new ArrayList<>(); diff --git a/src/main/java/de/ticketsystem/web/handlers/ApiHandler.java b/src/main/java/de/ticketsystem/web/handlers/ApiHandler.java index 3e062d9..acf1e7c 100644 --- a/src/main/java/de/ticketsystem/web/handlers/ApiHandler.java +++ b/src/main/java/de/ticketsystem/web/handlers/ApiHandler.java @@ -180,7 +180,7 @@ public class ApiHandler extends BaseHandler implements HttpHandler { // Ersteller benachrichtigen (online oder Pending) String notifyMsg = plugin.lang().format("comment.notify-online", "{id}", String.valueOf(ticketId), - "{player}", authorDisplay, + "{author}", authorDisplay, "{message}", msg); Bukkit.getScheduler().runTask(plugin, () -> { org.bukkit.entity.Player creator = Bukkit.getPlayer(ticket.getCreatorUUID()); @@ -246,6 +246,21 @@ public class ApiHandler extends BaseHandler implements HttpHandler { sendJson(ex, 200, json.toString()); } + case "archive" -> { + if (!method.equals("POST")) { sendJson(ex, 405, err("Method not allowed")); return; } + if (!session.isAdmin()) { sendJson(ex, 403, err("Kein Zugriff")); return; } + if (ticket.getStatus() != TicketStatus.CLOSED) { + sendJson(ex, 400, err("Nur geschlossene Tickets können archiviert werden")); return; + } + boolean ok = db.archiveTicket(ticketId); + if (ok) { + plugin.getTicketCache().invalidate(ticketId); + sendJson(ex, 200, "{\"ok\":true}"); + } else { + sendJson(ex, 500, err("Archivieren fehlgeschlagen")); + } + } + default -> sendJson(ex, 404, err("Unbekannte Aktion: " + action)); } } diff --git a/src/main/java/de/ticketsystem/web/handlers/StaticHandler.java b/src/main/java/de/ticketsystem/web/handlers/StaticHandler.java index 4075e35..87791d0 100644 --- a/src/main/java/de/ticketsystem/web/handlers/StaticHandler.java +++ b/src/main/java/de/ticketsystem/web/handlers/StaticHandler.java @@ -377,6 +377,27 @@ public class StaticHandler implements HttpHandler { s.append("if(r.ok){location.reload();}else{toast('Fehler: '+r.error,'error');}"); s.append("}"); + // Ticket: archive (geschlossenes Ticket ins Archiv verschieben) + s.append("async function archiveTicket(id){"); + s.append("if(!confirm('Ticket #'+id+' ins Archiv verschieben?'))return;"); + s.append("var r=await api('/ticket/'+id+'/archive','POST');"); + s.append("if(r.ok){location.href='/tickets';}else{toast('Fehler: '+r.error,'error');}"); + s.append("}"); + + // Ticket: restore (aus Archiv wiederherstellen) + s.append("async function restoreTicket(id){"); + s.append("if(!confirm('Ticket #'+id+' wiederherstellen?'))return;"); + s.append("var r=await api('/archive/'+id+'/restore','POST');"); + s.append("if(r.ok){location.href='/tickets';}else{toast('Fehler: '+r.error,'error');}"); + s.append("}"); + + // Ticket: delete from archive (permanent) + s.append("async function deleteArchivedTicket(id){"); + s.append("if(!confirm('Ticket #'+id+' wirklich PERMANENT loeschen? Diese Aktion kann nicht rueckgaengig gemacht werden!'))return;"); + s.append("var r=await api('/archive/'+id+'/delete','POST');"); + s.append("if(r.ok){location.href='/tickets?status=ARCHIVED';}else{toast('Fehler: '+r.error,'error');}"); + s.append("}"); + // FAQ: delete s.append("async function deleteFaq(id){"); s.append("if(!confirm('FAQ #'+id+' wirklich loeschen?'))return;"); diff --git a/src/main/java/de/ticketsystem/web/handlers/TicketsHandler.java b/src/main/java/de/ticketsystem/web/handlers/TicketsHandler.java index 7668000..d93a399 100644 --- a/src/main/java/de/ticketsystem/web/handlers/TicketsHandler.java +++ b/src/main/java/de/ticketsystem/web/handlers/TicketsHandler.java @@ -344,6 +344,16 @@ public class TicketsHandler extends BaseHandler implements HttpHandler { sb.append(""); } + // ── Ins Archiv verschieben (nur bei geschlossenen Tickets, nur Admin) ── + if (t.getStatus() == TicketStatus.CLOSED && !fromArchive && session.isAdmin()) { + sb.append("
") + .append(escHtml(wl(plugin, "detail-section-actions"))) + .append("
"); + sb.append(""); + sb.append("
"); + } + // ── Archiv-Aktionen (nur für archivierte Tickets) ── if (fromArchive && session.isAdmin()) { sb.append("
") diff --git a/src/main/resources/lang_de.yml b/src/main/resources/lang_de.yml index 296aa2d..c585083 100644 --- a/src/main/resources/lang_de.yml +++ b/src/main/resources/lang_de.yml @@ -576,6 +576,7 @@ 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" @@ -614,11 +615,13 @@ 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" diff --git a/src/main/resources/lang_en.yml b/src/main/resources/lang_en.yml index b171cd9..e433caf 100644 --- a/src/main/resources/lang_en.yml +++ b/src/main/resources/lang_en.yml @@ -576,6 +576,7 @@ 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" @@ -614,11 +615,13 @@ 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" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0ebf272..fc7189c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: TicketSystem -version: 1.1.2 +version: 1.1.4 main: de.ticketsystem.TicketPlugin api-version: 1.20 author: M_Viper