Upload folder via GUI - src
This commit is contained in:
@@ -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,6 +264,49 @@ public class TicketPlugin extends JavaPlugin {
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────── 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 ────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<Ticket> all = getAllTickets();
|
||||
List<Ticket> toArchive = new ArrayList<>();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;");
|
||||
|
||||
@@ -344,6 +344,16 @@ public class TicketsHandler extends BaseHandler implements HttpHandler {
|
||||
sb.append("</div></div>");
|
||||
}
|
||||
|
||||
// ── Ins Archiv verschieben (nur bei geschlossenen Tickets, nur Admin) ──
|
||||
if (t.getStatus() == TicketStatus.CLOSED && !fromArchive && session.isAdmin()) {
|
||||
sb.append("<div class='card'><div class='card-title'>")
|
||||
.append(escHtml(wl(plugin, "detail-section-actions")))
|
||||
.append("</div><div style='display:flex;gap:.75rem'>");
|
||||
sb.append("<button class='btn btn-warning' onclick='archiveTicket(").append(t.getId()).append(")'>")
|
||||
.append("🗃 ").append(escHtml(wl(plugin, "detail-btn-archive"))).append("</button>");
|
||||
sb.append("</div></div>");
|
||||
}
|
||||
|
||||
// ── Archiv-Aktionen (nur für archivierte Tickets) ──
|
||||
if (fromArchive && session.isAdmin()) {
|
||||
sb.append("<div class='card'><div class='card-title'>")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user