Update from Git Manager GUI
This commit is contained in:
@@ -177,7 +177,6 @@ public class DatabaseManager {
|
|||||||
// ─────────────────────────── Tabellen erstellen ────────────────────────
|
// ─────────────────────────── Tabellen erstellen ────────────────────────
|
||||||
|
|
||||||
private void createTables() {
|
private void createTables() {
|
||||||
// close_comment ist jetzt von Anfang an in der CREATE-Anweisung enthalten
|
|
||||||
String sql = """
|
String sql = """
|
||||||
CREATE TABLE IF NOT EXISTS tickets (
|
CREATE TABLE IF NOT EXISTS tickets (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
@@ -198,7 +197,8 @@ public class DatabaseManager {
|
|||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
claimed_at TIMESTAMP NULL,
|
claimed_at TIMESTAMP NULL,
|
||||||
closed_at TIMESTAMP NULL,
|
closed_at TIMESTAMP NULL,
|
||||||
close_comment VARCHAR(500) NULL
|
close_comment VARCHAR(500) NULL,
|
||||||
|
player_deleted BOOLEAN DEFAULT FALSE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
""";
|
""";
|
||||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||||
@@ -210,11 +210,9 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ergänzt fehlende Spalten in bestehenden Datenbanken automatisch.
|
* Ergänzt fehlende Spalten in bestehenden Datenbanken automatisch.
|
||||||
* Wichtig für Server, die das Plugin bereits installiert hatten bevor
|
|
||||||
* close_comment existierte.
|
|
||||||
*/
|
*/
|
||||||
private void ensureColumns() {
|
private void ensureColumns() {
|
||||||
// close_comment hinzufügen, falls nicht vorhanden
|
// close_comment hinzufügen
|
||||||
String checkSql = """
|
String checkSql = """
|
||||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
WHERE TABLE_SCHEMA = DATABASE()
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
@@ -225,20 +223,34 @@ public class DatabaseManager {
|
|||||||
Statement stmt = conn.createStatement()) {
|
Statement stmt = conn.createStatement()) {
|
||||||
ResultSet rs = stmt.executeQuery(checkSql);
|
ResultSet rs = stmt.executeQuery(checkSql);
|
||||||
if (rs.next() && rs.getInt(1) == 0) {
|
if (rs.next() && rs.getInt(1) == 0) {
|
||||||
// Spalte existiert nicht → hinzufügen
|
|
||||||
stmt.execute("ALTER TABLE tickets ADD COLUMN close_comment VARCHAR(500) NULL");
|
stmt.execute("ALTER TABLE tickets ADD COLUMN close_comment VARCHAR(500) NULL");
|
||||||
plugin.getLogger().info("[TicketSystem] Spalte 'close_comment' wurde zur Datenbank hinzugefügt.");
|
plugin.getLogger().info("[TicketSystem] Spalte 'close_comment' wurde zur Datenbank hinzugefügt.");
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Fehler bei ensureColumns(): " + e.getMessage(), e);
|
plugin.getLogger().log(Level.SEVERE, "Fehler bei ensureColumns(): " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// player_deleted Spalte prüfen
|
||||||
|
String checkSqlDel = """
|
||||||
|
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'tickets'
|
||||||
|
AND COLUMN_NAME = 'player_deleted'
|
||||||
|
""";
|
||||||
|
try (Connection conn = getConnection();
|
||||||
|
Statement stmt = conn.createStatement()) {
|
||||||
|
ResultSet rs = stmt.executeQuery(checkSqlDel);
|
||||||
|
if (rs.next() && rs.getInt(1) == 0) {
|
||||||
|
stmt.execute("ALTER TABLE tickets ADD COLUMN player_deleted BOOLEAN DEFAULT FALSE");
|
||||||
|
plugin.getLogger().info("[TicketSystem] Spalte 'player_deleted' wurde hinzugefügt.");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Fehler bei ensureColumns(player_deleted): " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────── CRUD ──────────────────────────────────────
|
// ─────────────────────────── CRUD ──────────────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Speichert ein neues Ticket in der DB und gibt die generierte ID zurück.
|
|
||||||
*/
|
|
||||||
public int createTicket(Ticket ticket) {
|
public int createTicket(Ticket ticket) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = """
|
String sql = """
|
||||||
@@ -282,13 +294,14 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ─── FIX: player_deleted wird beim Claimen zurückgesetzt, damit der Spieler
|
||||||
* Claimt ein Ticket (Status → CLAIMED).
|
// sein Ticket wieder sieht, sobald ein Supporter es annimmt. ───────
|
||||||
*/
|
|
||||||
public boolean claimTicket(int ticketId, UUID claimerUUID, String claimerName) {
|
public boolean claimTicket(int ticketId, UUID claimerUUID, String claimerName) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = """
|
String sql = """
|
||||||
UPDATE tickets SET status = 'CLAIMED', claimer_uuid = ?, claimer_name = ?, claimed_at = NOW()
|
UPDATE tickets
|
||||||
|
SET status = 'CLAIMED', claimer_uuid = ?, claimer_name = ?,
|
||||||
|
claimed_at = NOW(), player_deleted = FALSE
|
||||||
WHERE id = ? AND status = 'OPEN'
|
WHERE id = ? AND status = 'OPEN'
|
||||||
""";
|
""";
|
||||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
@@ -307,6 +320,7 @@ public class DatabaseManager {
|
|||||||
t.setClaimerUUID(claimerUUID);
|
t.setClaimerUUID(claimerUUID);
|
||||||
t.setClaimerName(claimerName);
|
t.setClaimerName(claimerName);
|
||||||
t.setClaimedAt(new Timestamp(System.currentTimeMillis()));
|
t.setClaimedAt(new Timestamp(System.currentTimeMillis()));
|
||||||
|
t.setPlayerDeleted(false); // FIX: Sichtbarkeit für den Spieler wiederherstellen
|
||||||
dataConfig.set("tickets." + ticketId, t);
|
dataConfig.set("tickets." + ticketId, t);
|
||||||
try {
|
try {
|
||||||
dataConfig.save(dataFile);
|
dataConfig.save(dataFile);
|
||||||
@@ -318,9 +332,6 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Schließt ein Ticket (Status → CLOSED).
|
|
||||||
*/
|
|
||||||
public boolean closeTicket(int ticketId, String closeComment) {
|
public boolean closeTicket(int ticketId, String closeComment) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = """
|
String sql = """
|
||||||
@@ -352,9 +363,36 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ─── Soft Delete Methode ────────────────────────────────────────────────
|
||||||
* Löscht ein Ticket anhand der ID.
|
public boolean markAsPlayerDeleted(int id) {
|
||||||
*/
|
if (useMySQL) {
|
||||||
|
String sql = "UPDATE tickets SET player_deleted = TRUE WHERE id = ?";
|
||||||
|
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setInt(1, id);
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Fehler beim Markieren als gelöscht: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (dataConfig.contains("tickets." + id)) {
|
||||||
|
Ticket t = (Ticket) dataConfig.get("tickets." + id);
|
||||||
|
if (t != null) {
|
||||||
|
t.setPlayerDeleted(true);
|
||||||
|
dataConfig.set("tickets." + id, t);
|
||||||
|
try {
|
||||||
|
dataConfig.save(dataFile);
|
||||||
|
backupDataFile();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().severe("Fehler beim Speichern (Soft Delete): " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean deleteTicket(int id) {
|
public boolean deleteTicket(int id) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = "DELETE FROM tickets WHERE id = ?";
|
String sql = "DELETE FROM tickets WHERE id = ?";
|
||||||
@@ -384,13 +422,14 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ─── FIX: player_deleted wird beim Weiterleiten zurückgesetzt, damit der
|
||||||
* Leitet ein Ticket an einen anderen Supporter weiter (Status → FORWARDED).
|
// Spieler sein Ticket wieder sieht, sobald es weitergeleitet wird. ──
|
||||||
*/
|
|
||||||
public boolean forwardTicket(int ticketId, UUID toUUID, String toName) {
|
public boolean forwardTicket(int ticketId, UUID toUUID, String toName) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = """
|
String sql = """
|
||||||
UPDATE tickets SET status = 'FORWARDED', forwarded_to_uuid = ?, forwarded_to_name = ?
|
UPDATE tickets
|
||||||
|
SET status = 'FORWARDED', forwarded_to_uuid = ?, forwarded_to_name = ?,
|
||||||
|
player_deleted = FALSE
|
||||||
WHERE id = ? AND status != 'CLOSED'
|
WHERE id = ? AND status != 'CLOSED'
|
||||||
""";
|
""";
|
||||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
@@ -408,6 +447,7 @@ public class DatabaseManager {
|
|||||||
t.setStatus(TicketStatus.FORWARDED);
|
t.setStatus(TicketStatus.FORWARDED);
|
||||||
t.setForwardedToUUID(toUUID);
|
t.setForwardedToUUID(toUUID);
|
||||||
t.setForwardedToName(toName);
|
t.setForwardedToName(toName);
|
||||||
|
t.setPlayerDeleted(false); // FIX: Sichtbarkeit für den Spieler wiederherstellen
|
||||||
dataConfig.set("tickets." + ticketId, t);
|
dataConfig.set("tickets." + ticketId, t);
|
||||||
try {
|
try {
|
||||||
dataConfig.save(dataFile);
|
dataConfig.save(dataFile);
|
||||||
@@ -421,9 +461,6 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
// ─────────────────────────── Abfragen ──────────────────────────────────
|
// ─────────────────────────── Abfragen ──────────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Gibt alle Tickets mit einem bestimmten Status zurück.
|
|
||||||
*/
|
|
||||||
public List<Ticket> getTicketsByStatus(TicketStatus... statuses) {
|
public List<Ticket> getTicketsByStatus(TicketStatus... statuses) {
|
||||||
List<Ticket> list = new ArrayList<>();
|
List<Ticket> list = new ArrayList<>();
|
||||||
if (statuses.length == 0) return list;
|
if (statuses.length == 0) return list;
|
||||||
@@ -452,9 +489,6 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gibt alle Tickets zurück (alle Status).
|
|
||||||
*/
|
|
||||||
public List<Ticket> getAllTickets() {
|
public List<Ticket> getAllTickets() {
|
||||||
List<Ticket> list = new ArrayList<>();
|
List<Ticket> list = new ArrayList<>();
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
@@ -475,9 +509,6 @@ public class DatabaseManager {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gibt ein einzelnes Ticket anhand der ID zurück.
|
|
||||||
*/
|
|
||||||
public Ticket getTicketById(int id) {
|
public Ticket getTicketById(int id) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = "SELECT * FROM tickets WHERE id = ?";
|
String sql = "SELECT * FROM tickets WHERE id = ?";
|
||||||
@@ -497,9 +528,6 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Anzahl offener Tickets (OPEN) – für Join-Benachrichtigung.
|
|
||||||
*/
|
|
||||||
public int countOpenTickets() {
|
public int countOpenTickets() {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = "SELECT COUNT(*) FROM tickets WHERE status = 'OPEN'";
|
String sql = "SELECT COUNT(*) FROM tickets WHERE status = 'OPEN'";
|
||||||
@@ -522,9 +550,6 @@ public class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Anzahl offener Tickets eines bestimmten Spielers.
|
|
||||||
*/
|
|
||||||
public int countOpenTicketsByPlayer(UUID uuid) {
|
public int countOpenTicketsByPlayer(UUID uuid) {
|
||||||
if (useMySQL) {
|
if (useMySQL) {
|
||||||
String sql = "SELECT COUNT(*) FROM tickets WHERE creator_uuid = ? AND status IN ('OPEN', 'CLAIMED', 'FORWARDED')";
|
String sql = "SELECT COUNT(*) FROM tickets WHERE creator_uuid = ? AND status IN ('OPEN', 'CLAIMED', 'FORWARDED')";
|
||||||
@@ -553,9 +578,6 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
// ─────────────────────────── Archivierung ──────────────────────────────
|
// ─────────────────────────── Archivierung ──────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Archiviert alle geschlossenen Tickets in eine separate Datei.
|
|
||||||
*/
|
|
||||||
public int archiveClosedTickets() {
|
public int archiveClosedTickets() {
|
||||||
List<Ticket> all = getAllTickets();
|
List<Ticket> all = getAllTickets();
|
||||||
List<Ticket> toArchive = new ArrayList<>();
|
List<Ticket> toArchive = new ArrayList<>();
|
||||||
@@ -705,11 +727,6 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
// ─────────────────────────── Mapping ───────────────────────────────────
|
// ─────────────────────────── Mapping ───────────────────────────────────
|
||||||
|
|
||||||
/**
|
|
||||||
* Liest eine Zeile aus dem ResultSet und erstellt ein Ticket-Objekt.
|
|
||||||
* close_comment wird mit try-catch abgesichert, damit ältere Datenbanken
|
|
||||||
* ohne diese Spalte nicht abstürzen.
|
|
||||||
*/
|
|
||||||
private Ticket mapRow(ResultSet rs) throws SQLException {
|
private Ticket mapRow(ResultSet rs) throws SQLException {
|
||||||
Ticket t = new Ticket();
|
Ticket t = new Ticket();
|
||||||
t.setId(rs.getInt("id"));
|
t.setId(rs.getInt("id"));
|
||||||
@@ -727,15 +744,10 @@ public class DatabaseManager {
|
|||||||
t.setClaimedAt(rs.getTimestamp("claimed_at"));
|
t.setClaimedAt(rs.getTimestamp("claimed_at"));
|
||||||
t.setClosedAt(rs.getTimestamp("closed_at"));
|
t.setClosedAt(rs.getTimestamp("closed_at"));
|
||||||
|
|
||||||
// ── BUGFIX: close_comment mit try-catch absichern ──────────────────
|
|
||||||
// Wenn die Spalte in einer alten DB noch nicht existiert, wird der
|
|
||||||
// Fehler ignoriert statt die gesamte Ticket-Liste leer zu lassen.
|
|
||||||
try {
|
try {
|
||||||
String closeComment = rs.getString("close_comment");
|
String closeComment = rs.getString("close_comment");
|
||||||
if (closeComment != null) t.setCloseComment(closeComment);
|
if (closeComment != null) t.setCloseComment(closeComment);
|
||||||
} catch (SQLException ignored) {
|
} catch (SQLException ignored) { }
|
||||||
// Spalte existiert noch nicht – ensureColumns() ergänzt sie beim nächsten Start
|
|
||||||
}
|
|
||||||
|
|
||||||
String claimerUUID = rs.getString("claimer_uuid");
|
String claimerUUID = rs.getString("claimer_uuid");
|
||||||
if (claimerUUID != null) {
|
if (claimerUUID != null) {
|
||||||
@@ -747,6 +759,10 @@ public class DatabaseManager {
|
|||||||
t.setForwardedToUUID(UUID.fromString(fwdUUID));
|
t.setForwardedToUUID(UUID.fromString(fwdUUID));
|
||||||
t.setForwardedToName(rs.getString("forwarded_to_name"));
|
t.setForwardedToName(rs.getString("forwarded_to_name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mapping des Soft Delete Flags
|
||||||
|
t.setPlayerDeleted(rs.getBoolean("player_deleted"));
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,6 +789,7 @@ public class DatabaseManager {
|
|||||||
if (t.getForwardedToUUID() != null) obj.put("forwardedToUUID", t.getForwardedToUUID().toString());
|
if (t.getForwardedToUUID() != null) obj.put("forwardedToUUID", t.getForwardedToUUID().toString());
|
||||||
if (t.getForwardedToName() != null) obj.put("forwardedToName", t.getForwardedToName());
|
if (t.getForwardedToName() != null) obj.put("forwardedToName", t.getForwardedToName());
|
||||||
if (t.getCloseComment() != null) obj.put("closeComment", t.getCloseComment());
|
if (t.getCloseComment() != null) obj.put("closeComment", t.getCloseComment());
|
||||||
|
obj.put("playerDeleted", t.isPlayerDeleted());
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,6 +815,7 @@ public class DatabaseManager {
|
|||||||
if (obj.get("forwardedToUUID") != null) t.setForwardedToUUID(UUID.fromString((String) obj.get("forwardedToUUID")));
|
if (obj.get("forwardedToUUID") != null) t.setForwardedToUUID(UUID.fromString((String) obj.get("forwardedToUUID")));
|
||||||
if (obj.get("forwardedToName") != null) t.setForwardedToName((String) obj.get("forwardedToName"));
|
if (obj.get("forwardedToName") != null) t.setForwardedToName((String) obj.get("forwardedToName"));
|
||||||
if (obj.get("closeComment") != null) t.setCloseComment((String) obj.get("closeComment"));
|
if (obj.get("closeComment") != null) t.setCloseComment((String) obj.get("closeComment"));
|
||||||
|
if (obj.containsKey("playerDeleted")) t.setPlayerDeleted((Boolean) obj.get("playerDeleted"));
|
||||||
return t;
|
return t;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (plugin != null) plugin.getLogger().severe("Fehler beim Parsen eines Tickets: " + e.getMessage());
|
if (plugin != null) plugin.getLogger().severe("Fehler beim Parsen eines Tickets: " + e.getMessage());
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import org.bukkit.inventory.meta.ItemMeta;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class TicketGUI implements Listener {
|
public class TicketGUI implements Listener {
|
||||||
@@ -27,9 +29,13 @@ public class TicketGUI implements Listener {
|
|||||||
// ─────────────────────────── Titel-Konstanten ──────────────────────────
|
// ─────────────────────────── Titel-Konstanten ──────────────────────────
|
||||||
|
|
||||||
private static final String GUI_TITLE = "§8§lTicket-Übersicht"; // Admin/Supporter Übersicht
|
private static final String GUI_TITLE = "§8§lTicket-Übersicht"; // Admin/Supporter Übersicht
|
||||||
|
private static final String CLOSED_GUI_TITLE = "§8§lTicket-Archiv"; // Admin: Geschlossene Tickets
|
||||||
private static final String PLAYER_GUI_TITLE = "§8§lMeine Tickets"; // Spieler: eigene Tickets
|
private static final String PLAYER_GUI_TITLE = "§8§lMeine Tickets"; // Spieler: eigene Tickets
|
||||||
private static final String DETAIL_GUI_TITLE = "§8§lTicket-Details"; // Admin: Detail-Ansicht
|
private static final String DETAIL_GUI_TITLE = "§8§lTicket-Details"; // Admin: Detail-Ansicht
|
||||||
|
|
||||||
|
/** Permission für den Zugriff auf das Archiv */
|
||||||
|
private static final String ARCHIVE_PERMISSION = "ticket.archive";
|
||||||
|
|
||||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||||
|
|
||||||
private final TicketPlugin plugin;
|
private final TicketPlugin plugin;
|
||||||
@@ -37,6 +43,9 @@ public class TicketGUI implements Listener {
|
|||||||
/** Admin-Übersicht: Slot → Ticket */
|
/** Admin-Übersicht: Slot → Ticket */
|
||||||
private final Map<UUID, Map<Integer, Ticket>> playerSlotMap = new HashMap<>();
|
private final Map<UUID, Map<Integer, Ticket>> playerSlotMap = new HashMap<>();
|
||||||
|
|
||||||
|
/** Admin-Archiv: Slot → Ticket */
|
||||||
|
private final Map<UUID, Map<Integer, Ticket>> playerClosedSlotMap = new HashMap<>();
|
||||||
|
|
||||||
/** Spieler-GUI: Slot → Ticket */
|
/** Spieler-GUI: Slot → Ticket */
|
||||||
private final Map<UUID, Map<Integer, Ticket>> playerOwnSlotMap = new HashMap<>();
|
private final Map<UUID, Map<Integer, Ticket>> playerOwnSlotMap = new HashMap<>();
|
||||||
|
|
||||||
@@ -46,40 +55,76 @@ public class TicketGUI implements Listener {
|
|||||||
/** Wartet auf Chat-Eingabe für Close-Kommentar: Player-UUID → Ticket-ID */
|
/** Wartet auf Chat-Eingabe für Close-Kommentar: Player-UUID → Ticket-ID */
|
||||||
private final Map<UUID, Integer> awaitingComment = new HashMap<>();
|
private final Map<UUID, Integer> awaitingComment = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merkt, welche Spieler die Detail-Ansicht aus dem Archiv heraus geöffnet haben,
|
||||||
|
* damit der Zurück-Button wieder ins Archiv führt (statt in die Hauptübersicht).
|
||||||
|
*/
|
||||||
|
private final Set<UUID> viewingFromArchive = new HashSet<>();
|
||||||
|
|
||||||
public TicketGUI(TicketPlugin plugin) {
|
public TicketGUI(TicketPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
// ADMIN / SUPPORTER GUI (Übersicht aller Tickets)
|
// ADMIN / SUPPORTER GUI (Feste 54 Slots mit Archiv-Button)
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
public void openGUI(Player player) {
|
public void openGUI(Player player) {
|
||||||
|
// Lade nur offene/aktive Tickets
|
||||||
List<Ticket> tickets = plugin.getDatabaseManager().getTicketsByStatus(
|
List<Ticket> tickets = plugin.getDatabaseManager().getTicketsByStatus(
|
||||||
TicketStatus.OPEN, TicketStatus.CLAIMED, TicketStatus.FORWARDED);
|
TicketStatus.OPEN, TicketStatus.CLAIMED, TicketStatus.FORWARDED);
|
||||||
|
|
||||||
if (tickets.isEmpty()) {
|
// Admin GUI hat immer 54 Slots (6 Reihen) für feste Buttons
|
||||||
player.sendMessage(plugin.formatMessage("messages.no-open-tickets"));
|
Inventory inv = Bukkit.createInventory(null, 54, GUI_TITLE);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = calcSize(tickets.size());
|
|
||||||
Inventory inv = Bukkit.createInventory(null, size, GUI_TITLE);
|
|
||||||
Map<Integer, Ticket> slotMap = new HashMap<>();
|
Map<Integer, Ticket> slotMap = new HashMap<>();
|
||||||
|
|
||||||
for (int i = 0; i < tickets.size() && i < 54; i++) {
|
// Tickets in die ersten 5 Reihen (0-44) füllen
|
||||||
|
for (int i = 0; i < tickets.size() && i < 45; i++) {
|
||||||
Ticket ticket = tickets.get(i);
|
Ticket ticket = tickets.get(i);
|
||||||
inv.setItem(i, buildAdminListItem(ticket));
|
inv.setItem(i, buildAdminListItem(ticket));
|
||||||
slotMap.put(i, ticket);
|
slotMap.put(i, ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
fillEmpty(inv);
|
// Letzte Reihe (45-53) mit Navigations-Items füllen
|
||||||
|
// Archiv-Button nur anzeigen wenn der Spieler die Archiv-Permission hat
|
||||||
|
fillAdminNavigation(inv, false, player);
|
||||||
|
|
||||||
playerSlotMap.put(player.getUniqueId(), slotMap);
|
playerSlotMap.put(player.getUniqueId(), slotMap);
|
||||||
player.openInventory(inv);
|
player.openInventory(inv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
// SPIELER-GUI (nur eigene Tickets, mit Lösch-Option bei OPEN)
|
// ADMIN ARCHIV GUI (Geschlossene Tickets) – nur mit ticket.archive
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
public void openClosedGUI(Player player) {
|
||||||
|
// ── Permission-Check ──────────────────────────────────────────────
|
||||||
|
if (!player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
player.sendMessage(plugin.color("&cDu hast keine Berechtigung, das Archiv zu öffnen."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lade nur geschlossene Tickets
|
||||||
|
List<Ticket> tickets = plugin.getDatabaseManager().getTicketsByStatus(TicketStatus.CLOSED);
|
||||||
|
|
||||||
|
Inventory inv = Bukkit.createInventory(null, 54, CLOSED_GUI_TITLE);
|
||||||
|
Map<Integer, Ticket> slotMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < tickets.size() && i < 45; i++) {
|
||||||
|
Ticket ticket = tickets.get(i);
|
||||||
|
inv.setItem(i, buildAdminListItem(ticket));
|
||||||
|
slotMap.put(i, ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation (Zurück-Button statt Archiv-Button)
|
||||||
|
fillAdminNavigation(inv, true, player);
|
||||||
|
|
||||||
|
playerClosedSlotMap.put(player.getUniqueId(), slotMap);
|
||||||
|
player.openInventory(inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// SPIELER-GUI (Filtert 'playerDeleted' Tickets)
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
public void openPlayerGUI(Player player) {
|
public void openPlayerGUI(Player player) {
|
||||||
@@ -88,7 +133,10 @@ public class TicketGUI implements Listener {
|
|||||||
|
|
||||||
List<Ticket> tickets = new ArrayList<>();
|
List<Ticket> tickets = new ArrayList<>();
|
||||||
for (Ticket t : all) {
|
for (Ticket t : all) {
|
||||||
if (t.getCreatorUUID().equals(player.getUniqueId())) tickets.add(t);
|
// Verstecke Tickets, die der Spieler als gelöscht markiert hat
|
||||||
|
if (t.getCreatorUUID().equals(player.getUniqueId()) && !t.isPlayerDeleted()) {
|
||||||
|
tickets.add(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tickets.isEmpty()) {
|
if (tickets.isEmpty()) {
|
||||||
@@ -112,27 +160,37 @@ public class TicketGUI implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
// ADMIN DETAIL-GUI (Aktionen für ein einzelnes Ticket)
|
// ADMIN DETAIL-GUI
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
public void openDetailGUI(Player player, Ticket ticket) {
|
public void openDetailGUI(Player player, Ticket ticket) {
|
||||||
Inventory inv = Bukkit.createInventory(null, 27, DETAIL_GUI_TITLE);
|
Inventory inv = Bukkit.createInventory(null, 27, DETAIL_GUI_TITLE);
|
||||||
|
|
||||||
// Slot 4: Ticket-Info (Mitte oben)
|
// Slot 4: Ticket-Info
|
||||||
inv.setItem(4, buildDetailInfoItem(ticket));
|
inv.setItem(4, buildDetailInfoItem(ticket));
|
||||||
|
|
||||||
// Slot 10: Teleportieren (immer verfügbar)
|
// Slot 10: Teleportieren
|
||||||
inv.setItem(10, buildActionItem(
|
inv.setItem(10, buildActionItem(
|
||||||
Material.ENDER_PEARL,
|
Material.ENDER_PEARL,
|
||||||
"§b§lTeleportieren",
|
"§b§lTeleportieren",
|
||||||
List.of("§7Teleportiert dich zur", "§7Position des Tickets.")));
|
List.of("§7Teleportiert dich zur", "§7Position des Tickets.")));
|
||||||
|
|
||||||
// Slot 12: Claimen (nur wenn OPEN), sonst grauer Platzhalter
|
// Slot 12: Claimen (nur wenn OPEN) / Permanent löschen (wenn CLOSED + ticket.archive) / Grau
|
||||||
if (ticket.getStatus() == TicketStatus.OPEN) {
|
if (ticket.getStatus() == TicketStatus.OPEN) {
|
||||||
inv.setItem(12, buildActionItem(
|
inv.setItem(12, buildActionItem(
|
||||||
Material.LIME_WOOL,
|
Material.LIME_WOOL,
|
||||||
"§a§lTicket annehmen",
|
"§a§lTicket annehmen",
|
||||||
List.of("§7Nimmt dieses Ticket an", "§7und markiert es als bearbeitet.")));
|
List.of("§7Nimmt dieses Ticket an", "§7und markiert es als bearbeitet.")));
|
||||||
|
} else if (ticket.getStatus() == TicketStatus.CLOSED && player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
// ── NEU: Löschen-Button nur für Archiv-berechtigte Spieler ──
|
||||||
|
inv.setItem(12, buildActionItem(
|
||||||
|
Material.BARRIER,
|
||||||
|
"§4§lTicket permanent löschen",
|
||||||
|
List.of(
|
||||||
|
"§7Löscht dieses Ticket",
|
||||||
|
"§7unwiderruflich aus der Datenbank.",
|
||||||
|
"§8§m ",
|
||||||
|
"§c§lACHTUNG: §cNicht rückgängig zu machen!")));
|
||||||
} else {
|
} else {
|
||||||
inv.setItem(12, buildActionItem(
|
inv.setItem(12, buildActionItem(
|
||||||
Material.GRAY_WOOL,
|
Material.GRAY_WOOL,
|
||||||
@@ -140,18 +198,12 @@ public class TicketGUI implements Listener {
|
|||||||
List.of("§7Dieses Ticket wurde bereits", "§7angenommen.")));
|
List.of("§7Dieses Ticket wurde bereits", "§7angenommen.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot 14: Schließen — für OPEN, CLAIMED und FORWARDED; grauer Block wenn bereits CLOSED
|
// Slot 14: Schließen
|
||||||
if (ticket.getStatus() != TicketStatus.CLOSED) {
|
if (ticket.getStatus() != TicketStatus.CLOSED) {
|
||||||
inv.setItem(14, buildActionItem(
|
inv.setItem(14, buildActionItem(
|
||||||
Material.RED_WOOL,
|
Material.RED_WOOL,
|
||||||
"§c§lTicket schließen",
|
"§c§lTicket schließen",
|
||||||
List.of(
|
List.of("§7Schließt das Ticket.", "§8§m ", "§eKlick für Kommentar-Eingabe.")));
|
||||||
"§7Schließt das Ticket.",
|
|
||||||
"§8§m ",
|
|
||||||
"§eNach dem Klick kannst du im",
|
|
||||||
"§eChat einen Kommentar eingeben.",
|
|
||||||
"§7Tippe §c- §7für keinen Kommentar.",
|
|
||||||
"§7Tippe §ccancel §7zum Abbrechen.")));
|
|
||||||
} else {
|
} else {
|
||||||
inv.setItem(14, buildActionItem(
|
inv.setItem(14, buildActionItem(
|
||||||
Material.GRAY_WOOL,
|
Material.GRAY_WOOL,
|
||||||
@@ -159,7 +211,7 @@ public class TicketGUI implements Listener {
|
|||||||
List.of("§7Dieses Ticket ist bereits", "§7geschlossen.")));
|
List.of("§7Dieses Ticket ist bereits", "§7geschlossen.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot 16: Zurück zur Übersicht
|
// Slot 16: Zurück
|
||||||
inv.setItem(16, buildActionItem(
|
inv.setItem(16, buildActionItem(
|
||||||
Material.ARROW,
|
Material.ARROW,
|
||||||
"§7§lZurück",
|
"§7§lZurück",
|
||||||
@@ -179,36 +231,58 @@ public class TicketGUI implements Listener {
|
|||||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||||
String title = event.getView().getTitle();
|
String title = event.getView().getTitle();
|
||||||
|
|
||||||
if (!title.equals(GUI_TITLE) && !title.equals(PLAYER_GUI_TITLE) && !title.equals(DETAIL_GUI_TITLE)) return;
|
if (!title.equals(GUI_TITLE) && !title.equals(CLOSED_GUI_TITLE) && !title.equals(PLAYER_GUI_TITLE) && !title.equals(DETAIL_GUI_TITLE)) return;
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
int slot = event.getRawSlot();
|
int slot = event.getRawSlot();
|
||||||
if (slot < 0) return;
|
if (slot < 0) return;
|
||||||
|
|
||||||
// ── Admin-Übersicht ──────────────────────────────────────────────
|
// ── Admin Haupt-Übersicht ──────────────────────────────────────────────
|
||||||
if (title.equals(GUI_TITLE)) {
|
if (title.equals(GUI_TITLE)) {
|
||||||
|
// Klick auf die Truhe (Archiv-Button) in Slot 49
|
||||||
|
if (slot == 49) {
|
||||||
|
// ── Permission-Check beim Klick ──
|
||||||
|
if (!player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
player.sendMessage(plugin.color("&cDu hast keine Berechtigung, das Archiv zu öffnen."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openClosedGUI(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Klick auf ein Ticket
|
||||||
Map<Integer, Ticket> slotMap = playerSlotMap.get(player.getUniqueId());
|
Map<Integer, Ticket> slotMap = playerSlotMap.get(player.getUniqueId());
|
||||||
if (slotMap == null) return;
|
if (slotMap == null) return;
|
||||||
Ticket ticket = slotMap.get(slot);
|
Ticket ticket = slotMap.get(slot);
|
||||||
if (ticket == null) return;
|
if (ticket != null) {
|
||||||
|
viewingFromArchive.remove(player.getUniqueId()); // Kommt aus Hauptübersicht
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
|
openTicketDetailAsync(player, ticket);
|
||||||
// Frische Daten aus DB holen, dann Detail-GUI öffnen
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
|
||||||
Ticket fresh = plugin.getDatabaseManager().getTicketById(ticket.getId());
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
||||||
if (fresh == null) {
|
|
||||||
player.sendMessage(plugin.formatMessage("messages.ticket-not-found"));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
openDetailGUI(player, fresh);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Spieler-GUI ──────────────────────────────────────────────────
|
// ── Admin Archiv (Geschlossene Tickets) ─────────────────────────────────
|
||||||
|
if (title.equals(CLOSED_GUI_TITLE)) {
|
||||||
|
// Klick auf den Zurück-Pfeil (Slot 49)
|
||||||
|
if (slot == 49) {
|
||||||
|
openGUI(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Klick auf ein Ticket
|
||||||
|
Map<Integer, Ticket> slotMap = playerClosedSlotMap.get(player.getUniqueId());
|
||||||
|
if (slotMap == null) return;
|
||||||
|
Ticket ticket = slotMap.get(slot);
|
||||||
|
if (ticket != null) {
|
||||||
|
viewingFromArchive.add(player.getUniqueId()); // Kommt aus Archiv
|
||||||
|
player.closeInventory();
|
||||||
|
openTicketDetailAsync(player, ticket);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Spieler-GUI ──────────────────────────────────────────────────────
|
||||||
if (title.equals(PLAYER_GUI_TITLE)) {
|
if (title.equals(PLAYER_GUI_TITLE)) {
|
||||||
Map<Integer, Ticket> slotMap = playerOwnSlotMap.get(player.getUniqueId());
|
Map<Integer, Ticket> slotMap = playerOwnSlotMap.get(player.getUniqueId());
|
||||||
if (slotMap == null) return;
|
if (slotMap == null) return;
|
||||||
@@ -217,28 +291,26 @@ public class TicketGUI implements Listener {
|
|||||||
|
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
|
|
||||||
if (ticket.getStatus() == TicketStatus.OPEN) {
|
// Nur löschen wenn OFFEN oder GESCHLOSSEN
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
if (ticket.getStatus() == TicketStatus.OPEN || ticket.getStatus() == TicketStatus.CLOSED) {
|
||||||
boolean deleted = plugin.getDatabaseManager().deleteTicket(ticket.getId());
|
boolean success = plugin.getDatabaseManager().markAsPlayerDeleted(ticket.getId());
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
if (deleted) {
|
if (success) {
|
||||||
player.sendMessage(plugin.color(
|
player.sendMessage(plugin.color("&aDein Ticket &e#" + ticket.getId() + " &awurde aus deiner Übersicht entfernt."));
|
||||||
"&aDein Ticket &e#" + ticket.getId() + " &awurde gelöscht."));
|
|
||||||
openPlayerGUI(player);
|
openPlayerGUI(player);
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(plugin.color("&cFehler beim Löschen des Tickets."));
|
player.sendMessage(plugin.color("&cFehler beim Entfernen des Tickets."));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(plugin.color(
|
// Ticket wird bearbeitet (Claimed oder Forwarded) -> Löschen verweigern
|
||||||
"&cDieses Ticket kann nicht mehr gelöscht werden, " +
|
player.sendMessage(plugin.color("&cDu kannst dieses Ticket nicht löschen, da es bereits von einem Supporter bearbeitet wird."));
|
||||||
"da es bereits angenommen oder geschlossen wurde."));
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Admin Detail-GUI ─────────────────────────────────────────────
|
// ── Admin Detail-GUI ─────────────────────────────────────────────────
|
||||||
if (title.equals(DETAIL_GUI_TITLE)) {
|
if (title.equals(DETAIL_GUI_TITLE)) {
|
||||||
Ticket ticket = detailTicketMap.get(player.getUniqueId());
|
Ticket ticket = detailTicketMap.get(player.getUniqueId());
|
||||||
if (ticket == null) return;
|
if (ticket == null) return;
|
||||||
@@ -247,21 +319,46 @@ public class TicketGUI implements Listener {
|
|||||||
|
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case 10 -> handleDetailTeleport(player, ticket);
|
case 10 -> handleDetailTeleport(player, ticket);
|
||||||
case 12 -> handleDetailClaim(player, ticket);
|
case 12 -> {
|
||||||
|
// Wenn CLOSED + archive-Permission → permanent löschen, sonst claimen
|
||||||
|
if (ticket.getStatus() == TicketStatus.CLOSED && player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
handleDetailPermanentDelete(player, ticket);
|
||||||
|
} else {
|
||||||
|
handleDetailClaim(player, ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
case 14 -> handleDetailClose(player, ticket);
|
case 14 -> handleDetailClose(player, ticket);
|
||||||
case 16 -> openGUI(player);
|
case 16 -> {
|
||||||
// Glasscheiben und andere Slots → nichts tun
|
// Zurück zur richtigen GUI je nach Herkunft
|
||||||
|
if (viewingFromArchive.remove(player.getUniqueId())) {
|
||||||
|
openClosedGUI(player);
|
||||||
|
} else {
|
||||||
|
openGUI(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────── Detail-Aktionen ───────────────────────────
|
// ─────────────────────────── Detail-Aktionen & Helpers ──────────────────
|
||||||
|
|
||||||
|
private void openTicketDetailAsync(Player player, Ticket currentTicket) {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
Ticket fresh = plugin.getDatabaseManager().getTicketById(currentTicket.getId());
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
|
if (fresh == null) {
|
||||||
|
player.sendMessage(plugin.formatMessage("messages.ticket-not-found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openDetailGUI(player, fresh);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void handleDetailTeleport(Player player, Ticket ticket) {
|
private void handleDetailTeleport(Player player, Ticket ticket) {
|
||||||
if (ticket.getLocation() != null) {
|
if (ticket.getLocation() != null) {
|
||||||
player.teleport(ticket.getLocation());
|
player.teleport(ticket.getLocation());
|
||||||
player.sendMessage(plugin.color(
|
player.sendMessage(plugin.color("&7Du wurdest zu Ticket &e#" + ticket.getId() + " &7teleportiert."));
|
||||||
"&7Du wurdest zu Ticket &e#" + ticket.getId() + " &7teleportiert."));
|
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(plugin.color("&cDie Welt des Tickets ist nicht geladen!"));
|
player.sendMessage(plugin.color("&cDie Welt des Tickets ist nicht geladen!"));
|
||||||
}
|
}
|
||||||
@@ -272,32 +369,26 @@ public class TicketGUI implements Listener {
|
|||||||
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
boolean success = plugin.getDatabaseManager().claimTicket(
|
boolean success = plugin.getDatabaseManager().claimTicket(ticket.getId(), player.getUniqueId(), player.getName());
|
||||||
ticket.getId(), player.getUniqueId(), player.getName());
|
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
if (success) {
|
if (success) {
|
||||||
player.sendMessage(plugin.formatMessage("messages.ticket-claimed")
|
player.sendMessage(plugin.formatMessage("messages.ticket-claimed")
|
||||||
.replace("{id}", String.valueOf(ticket.getId()))
|
.replace("{id}", String.valueOf(ticket.getId()))
|
||||||
.replace("{player}", ticket.getCreatorName()));
|
.replace("{player}", ticket.getCreatorName()));
|
||||||
|
|
||||||
plugin.getTicketManager().notifyCreatorClaimed(ticket);
|
ticket.setClaimerUUID(player.getUniqueId());
|
||||||
|
ticket.setClaimerName(player.getName());
|
||||||
|
|
||||||
|
plugin.getTicketManager().notifyCreatorClaimed(ticket);
|
||||||
if (ticket.getLocation() != null) player.teleport(ticket.getLocation());
|
if (ticket.getLocation() != null) player.teleport(ticket.getLocation());
|
||||||
|
|
||||||
// ── BUGFIX: Detail-GUI mit frischen DB-Daten neu öffnen ──
|
|
||||||
// Dadurch verschwindet der Claim-Button und der Schließen-Button
|
|
||||||
// ist sofort korrekt sichtbar, ohne dass der Admin die GUI
|
|
||||||
// erst schließen und neu öffnen muss.
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
Ticket fresh = plugin.getDatabaseManager().getTicketById(ticket.getId());
|
Ticket fresh = plugin.getDatabaseManager().getTicketById(ticket.getId());
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
if (fresh != null) openDetailGUI(player, fresh);
|
if (fresh != null) openDetailGUI(player, fresh);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
player.sendMessage(plugin.formatMessage("messages.already-claimed"));
|
||||||
}
|
}
|
||||||
@@ -305,76 +396,125 @@ public class TicketGUI implements Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht ein geschlossenes Ticket permanent aus der Datenbank.
|
||||||
|
* Nur für Spieler mit der Permission ticket.archive.
|
||||||
|
*/
|
||||||
|
private void handleDetailPermanentDelete(Player player, Ticket ticket) {
|
||||||
|
if (!player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
player.sendMessage(plugin.color("&cDu hast keine Berechtigung, Tickets permanent zu löschen."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ticket.getStatus() != TicketStatus.CLOSED) {
|
||||||
|
player.sendMessage(plugin.color("&cNur geschlossene Tickets können permanent gelöscht werden."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
boolean success = plugin.getDatabaseManager().deleteTicket(ticket.getId());
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
|
if (success) {
|
||||||
|
player.sendMessage(plugin.color(
|
||||||
|
"&aTicket &e#" + ticket.getId() + " &awurde permanent aus der Datenbank gelöscht."));
|
||||||
|
viewingFromArchive.remove(player.getUniqueId());
|
||||||
|
openClosedGUI(player);
|
||||||
|
} else {
|
||||||
|
player.sendMessage(plugin.color("&cFehler beim Löschen des Tickets."));
|
||||||
|
openClosedGUI(player);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void handleDetailClose(Player player, Ticket ticket) {
|
private void handleDetailClose(Player player, Ticket ticket) {
|
||||||
if (ticket.getStatus() == TicketStatus.CLOSED) {
|
if (ticket.getStatus() == TicketStatus.CLOSED) {
|
||||||
player.sendMessage(plugin.color("&cDieses Ticket ist bereits geschlossen."));
|
player.sendMessage(plugin.color("&cDieses Ticket ist bereits geschlossen."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitingComment.put(player.getUniqueId(), ticket.getId());
|
awaitingComment.put(player.getUniqueId(), ticket.getId());
|
||||||
player.sendMessage(plugin.color("&8&m "));
|
player.sendMessage(plugin.color("&8&m "));
|
||||||
player.sendMessage(plugin.color("&6Ticket #" + ticket.getId() + " schließen"));
|
player.sendMessage(plugin.color("&6Ticket #" + ticket.getId() + " schließen"));
|
||||||
player.sendMessage(plugin.color("&7Gib einen Kommentar für den Spieler ein."));
|
player.sendMessage(plugin.color("&7Gib einen Kommentar ein (&e- &7für keinen)."));
|
||||||
player.sendMessage(plugin.color("&7Kein Kommentar? Tippe: &e-"));
|
player.sendMessage(plugin.color("&7Abbrechen mit &ccancel"));
|
||||||
player.sendMessage(plugin.color("&7Abbrechen? Tippe: &ccancel"));
|
|
||||||
player.sendMessage(plugin.color("&8&m "));
|
player.sendMessage(plugin.color("&8&m "));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────── Chat-Listener (Kommentar-Eingabe) ─────────
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (!awaitingComment.containsKey(player.getUniqueId())) return;
|
if (!awaitingComment.containsKey(player.getUniqueId())) return;
|
||||||
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
int ticketId = awaitingComment.remove(player.getUniqueId());
|
int ticketId = awaitingComment.remove(player.getUniqueId());
|
||||||
String input = event.getMessage().trim();
|
String input = event.getMessage().trim();
|
||||||
|
|
||||||
if (input.equalsIgnoreCase("cancel")) {
|
if (input.equalsIgnoreCase("cancel")) {
|
||||||
Bukkit.getScheduler().runTask(plugin, () ->
|
Bukkit.getScheduler().runTask(plugin, () -> player.sendMessage(plugin.color("&cAbgebrochen.")));
|
||||||
player.sendMessage(plugin.color("&cSchließen abgebrochen.")));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "-" = bewusst kein Kommentar
|
|
||||||
final String comment = input.equals("-") ? "" : input;
|
final String comment = input.equals("-") ? "" : input;
|
||||||
|
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
boolean success = plugin.getDatabaseManager().closeTicket(ticketId, comment);
|
boolean success = plugin.getDatabaseManager().closeTicket(ticketId, comment);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
Ticket ticket = plugin.getDatabaseManager().getTicketById(ticketId);
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
player.sendMessage(plugin.formatMessage("messages.ticket-closed")
|
player.sendMessage(plugin.formatMessage("messages.ticket-closed").replace("{id}", String.valueOf(ticketId)));
|
||||||
.replace("{id}", String.valueOf(ticketId)));
|
if (!comment.isEmpty()) player.sendMessage(plugin.color("&7Kommentar: &f" + comment));
|
||||||
|
|
||||||
if (!comment.isEmpty()) {
|
|
||||||
player.sendMessage(plugin.color("&7Kommentar gespeichert: &f" + comment));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ticket != null) {
|
if (ticket != null) {
|
||||||
ticket.setCloseComment(comment);
|
ticket.setCloseComment(comment);
|
||||||
plugin.getTicketManager().notifyCreatorClosed(ticket);
|
plugin.getTicketManager().notifyCreatorClosed(ticket, player.getName());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () ->
|
|
||||||
player.sendMessage(plugin.formatMessage("messages.ticket-not-found")));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ─────────────────────────── Item-Builder & Füll-Methoden ─────────────
|
||||||
// ITEM-BUILDER
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
/**
|
||||||
|
* Füllt die Navigationsleiste (letzte Reihe) der Admin-GUIs.
|
||||||
|
* Der Archiv-Button (Truhe) wird nur angezeigt, wenn der Spieler ticket.archive besitzt.
|
||||||
|
*/
|
||||||
|
private void fillAdminNavigation(Inventory inv, boolean isArchiveView, Player player) {
|
||||||
|
ItemStack glass = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
|
||||||
|
ItemMeta meta = glass.getItemMeta();
|
||||||
|
if (meta != null) {
|
||||||
|
meta.setDisplayName(" ");
|
||||||
|
glass.setItemMeta(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letzte Reihe (45-53) füllen
|
||||||
|
for (int i = 45; i < 54; i++) {
|
||||||
|
if (i != 49) inv.setItem(i, glass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArchiveView) {
|
||||||
|
// Im Archiv: Zurück-Pfeil in Slot 49
|
||||||
|
inv.setItem(49, buildActionItem(
|
||||||
|
Material.ARROW,
|
||||||
|
"§7§lZurück zur Übersicht",
|
||||||
|
List.of("§7Zeigt alle offenen Tickets.")));
|
||||||
|
} else {
|
||||||
|
// In der Übersicht: Archiv-Truhe nur mit Permission
|
||||||
|
if (player.hasPermission(ARCHIVE_PERMISSION)) {
|
||||||
|
inv.setItem(49, buildActionItem(
|
||||||
|
Material.CHEST,
|
||||||
|
"§7§lGeschlossene Tickets",
|
||||||
|
List.of("§7Zeigt alle abgeschlossenen", "§7Tickets im Archiv an.")));
|
||||||
|
} else {
|
||||||
|
// Kein Archiv-Zugriff → Slot 49 bleibt Glas (kein Button)
|
||||||
|
inv.setItem(49, glass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ItemStack buildAdminListItem(Ticket ticket) {
|
private ItemStack buildAdminListItem(Ticket ticket) {
|
||||||
Material mat = switch (ticket.getStatus()) {
|
Material mat = switch (ticket.getStatus()) {
|
||||||
case OPEN -> Material.PAPER;
|
case OPEN -> Material.PAPER;
|
||||||
case CLAIMED -> Material.YELLOW_DYE;
|
case CLAIMED -> Material.YELLOW_DYE;
|
||||||
case FORWARDED -> Material.ORANGE_DYE;
|
case FORWARDED -> Material.ORANGE_DYE;
|
||||||
default -> Material.PAPER;
|
case CLOSED -> Material.GRAY_DYE;
|
||||||
};
|
};
|
||||||
|
|
||||||
ItemStack item = new ItemStack(mat);
|
ItemStack item = new ItemStack(mat);
|
||||||
@@ -382,20 +522,19 @@ public class TicketGUI implements Listener {
|
|||||||
if (meta == null) return item;
|
if (meta == null) return item;
|
||||||
|
|
||||||
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
||||||
|
|
||||||
List<String> lore = new ArrayList<>();
|
List<String> lore = new ArrayList<>();
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
lore.add("§7Ersteller: §e" + ticket.getCreatorName());
|
lore.add("§7Ersteller: §e" + ticket.getCreatorName());
|
||||||
lore.add("§7Anliegen: §f" + ticket.getMessage());
|
lore.add("§7Anliegen: §f" + ticket.getMessage());
|
||||||
lore.add("§7Erstellt: §e" + DATE_FORMAT.format(ticket.getCreatedAt()));
|
lore.add("§7Erstellt: §e" + DATE_FORMAT.format(ticket.getCreatedAt()));
|
||||||
lore.add("§7Welt: §e" + ticket.getWorldName());
|
if (ticket.getStatus() == TicketStatus.CLOSED && ticket.getCloseComment() != null && !ticket.getCloseComment().isEmpty()) {
|
||||||
lore.add(String.format("§7Position: §e%.0f, %.0f, %.0f", ticket.getX(), ticket.getY(), ticket.getZ()));
|
lore.add("§7Kommentar: §f" + ticket.getCloseComment());
|
||||||
if (ticket.getClaimerName() != null)
|
}
|
||||||
lore.add("§7Angenommen: §a" + ticket.getClaimerName());
|
if (ticket.isPlayerDeleted()) {
|
||||||
if (ticket.getForwardedToName() != null)
|
lore.add("§cSpieler hat Ticket gelöscht.");
|
||||||
lore.add("§7Weitergeleitet an: §6" + ticket.getForwardedToName());
|
}
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
lore.add("§e§l» KLICKEN für Details & Aktionen");
|
lore.add("§e§l» KLICKEN für Details");
|
||||||
|
|
||||||
meta.setLore(lore);
|
meta.setLore(lore);
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
@@ -415,7 +554,6 @@ public class TicketGUI implements Listener {
|
|||||||
if (meta == null) return item;
|
if (meta == null) return item;
|
||||||
|
|
||||||
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
||||||
|
|
||||||
List<String> lore = new ArrayList<>();
|
List<String> lore = new ArrayList<>();
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
lore.add("§7Ersteller: §e" + ticket.getCreatorName());
|
lore.add("§7Ersteller: §e" + ticket.getCreatorName());
|
||||||
@@ -429,8 +567,6 @@ public class TicketGUI implements Listener {
|
|||||||
if (ticket.getClaimedAt() != null)
|
if (ticket.getClaimedAt() != null)
|
||||||
lore.add("§7Angenommen am: §a" + DATE_FORMAT.format(ticket.getClaimedAt()));
|
lore.add("§7Angenommen am: §a" + DATE_FORMAT.format(ticket.getClaimedAt()));
|
||||||
}
|
}
|
||||||
if (ticket.getForwardedToName() != null)
|
|
||||||
lore.add("§7Weitergeleitet an: §6" + ticket.getForwardedToName());
|
|
||||||
if (ticket.getStatus() == TicketStatus.CLOSED) {
|
if (ticket.getStatus() == TicketStatus.CLOSED) {
|
||||||
if (ticket.getClosedAt() != null)
|
if (ticket.getClosedAt() != null)
|
||||||
lore.add("§7Geschlossen am: §c" + DATE_FORMAT.format(ticket.getClosedAt()));
|
lore.add("§7Geschlossen am: §c" + DATE_FORMAT.format(ticket.getClosedAt()));
|
||||||
@@ -438,7 +574,6 @@ public class TicketGUI implements Listener {
|
|||||||
lore.add("§7Kommentar: §f" + ticket.getCloseComment());
|
lore.add("§7Kommentar: §f" + ticket.getCloseComment());
|
||||||
}
|
}
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
|
|
||||||
meta.setLore(lore);
|
meta.setLore(lore);
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
return item;
|
return item;
|
||||||
@@ -457,17 +592,12 @@ public class TicketGUI implements Listener {
|
|||||||
if (meta == null) return item;
|
if (meta == null) return item;
|
||||||
|
|
||||||
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
meta.setDisplayName("§6§lTicket #" + ticket.getId() + " §r" + ticket.getStatus().getColored());
|
||||||
|
|
||||||
List<String> lore = new ArrayList<>();
|
List<String> lore = new ArrayList<>();
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
lore.add("§7Anliegen: §f" + ticket.getMessage());
|
lore.add("§7Anliegen: §f" + ticket.getMessage());
|
||||||
lore.add("§7Erstellt: §e" + DATE_FORMAT.format(ticket.getCreatedAt()));
|
lore.add("§7Erstellt: §e" + DATE_FORMAT.format(ticket.getCreatedAt()));
|
||||||
lore.add("§7Welt: §e" + ticket.getWorldName());
|
lore.add("§7Welt: §e" + ticket.getWorldName());
|
||||||
lore.add(String.format("§7Position: §e%.0f, %.0f, %.0f", ticket.getX(), ticket.getY(), ticket.getZ()));
|
lore.add(String.format("§7Position: §e%.0f, %.0f, %.0f", ticket.getX(), ticket.getY(), ticket.getZ()));
|
||||||
if (ticket.getStatus() == TicketStatus.CLAIMED && ticket.getClaimerName() != null)
|
|
||||||
lore.add("§7Angenommen von: §a" + ticket.getClaimerName());
|
|
||||||
if (ticket.getStatus() == TicketStatus.FORWARDED && ticket.getForwardedToName() != null)
|
|
||||||
lore.add("§7Bearbeiter: §6" + ticket.getForwardedToName());
|
|
||||||
if (ticket.getStatus() == TicketStatus.CLOSED
|
if (ticket.getStatus() == TicketStatus.CLOSED
|
||||||
&& ticket.getCloseComment() != null && !ticket.getCloseComment().isEmpty()) {
|
&& ticket.getCloseComment() != null && !ticket.getCloseComment().isEmpty()) {
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
@@ -475,14 +605,17 @@ public class TicketGUI implements Listener {
|
|||||||
lore.add("§f" + ticket.getCloseComment());
|
lore.add("§f" + ticket.getCloseComment());
|
||||||
}
|
}
|
||||||
lore.add("§8§m ");
|
lore.add("§8§m ");
|
||||||
switch (ticket.getStatus()) {
|
|
||||||
case OPEN -> { lore.add("§c§l» KLICKEN zum Löschen");
|
|
||||||
lore.add("§7Nur möglich solange noch nicht angenommen."); }
|
|
||||||
case CLOSED -> lore.add("§8» Dieses Ticket ist abgeschlossen.");
|
|
||||||
default -> { lore.add("§e» Ticket wird bearbeitet...");
|
|
||||||
lore.add("§7Kann nicht mehr gelöscht werden."); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch (ticket.getStatus()) {
|
||||||
|
case OPEN, CLOSED -> {
|
||||||
|
lore.add("§c§l» KLICKEN zum Löschen");
|
||||||
|
lore.add("§7Entferne dieses Ticket aus deiner Übersicht.");
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
lore.add("§e» Ticket wird bearbeitet...");
|
||||||
|
lore.add("§7Kann nicht mehr gelöscht werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
meta.setLore(lore);
|
meta.setLore(lore);
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
return item;
|
return item;
|
||||||
@@ -498,8 +631,6 @@ public class TicketGUI implements Listener {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
|
|
||||||
|
|
||||||
private int calcSize(int ticketCount) {
|
private int calcSize(int ticketCount) {
|
||||||
int size = (int) Math.ceil(ticketCount / 9.0) * 9;
|
int size = (int) Math.ceil(ticketCount / 9.0) * 9;
|
||||||
return Math.max(9, Math.min(54, size));
|
return Math.max(9, Math.min(54, size));
|
||||||
|
|||||||
@@ -51,9 +51,13 @@ public class TicketManager {
|
|||||||
* und sendet optional eine Discord-Webhook-Nachricht.
|
* und sendet optional eine Discord-Webhook-Nachricht.
|
||||||
*/
|
*/
|
||||||
public void notifyTeam(Ticket ticket) {
|
public void notifyTeam(Ticket ticket) {
|
||||||
|
// Sicherheitschecks für null-Werte
|
||||||
|
String creatorName = ticket.getCreatorName() != null ? ticket.getCreatorName() : "Unbekannt";
|
||||||
|
String message = ticket.getMessage() != null ? ticket.getMessage() : "";
|
||||||
|
|
||||||
String msg = plugin.formatMessage("messages.new-ticket-notify")
|
String msg = plugin.formatMessage("messages.new-ticket-notify")
|
||||||
.replace("{player}", ticket.getCreatorName())
|
.replace("{player}", creatorName)
|
||||||
.replace("{message}", ticket.getMessage())
|
.replace("{message}", message)
|
||||||
.replace("{id}", String.valueOf(ticket.getId()));
|
.replace("{id}", String.valueOf(ticket.getId()));
|
||||||
|
|
||||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||||
@@ -63,19 +67,32 @@ public class TicketManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discord-Webhook (asynchron, kein Einfluss auf Server-Performance)
|
// Discord-Webhook (asynchron)
|
||||||
plugin.getDiscordWebhook().sendNewTicket(ticket);
|
plugin.getDiscordWebhook().sendNewTicket(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Benachrichtigt den Ersteller, wenn sein Ticket angenommen wurde.
|
* Benachrichtigt den Ersteller, wenn sein Ticket angenommen wurde.
|
||||||
|
* --- FIX PROBLEMK 1: NIE "UNBEKANNT" ---
|
||||||
*/
|
*/
|
||||||
public void notifyCreatorClaimed(Ticket ticket) {
|
public void notifyCreatorClaimed(Ticket ticket) {
|
||||||
Player creator = Bukkit.getPlayer(ticket.getCreatorUUID());
|
Player creator = Bukkit.getPlayer(ticket.getCreatorUUID());
|
||||||
if (creator != null && creator.isOnline()) {
|
if (creator != null && creator.isOnline()) {
|
||||||
|
|
||||||
|
// 1. Versuch: Name aus dem Ticket-Objekt
|
||||||
|
String claimerName = ticket.getClaimerName();
|
||||||
|
|
||||||
|
// 2. Versuch: Wenn Name fehlt, aber UUID vorhanden -> Namen über Bukkit holen
|
||||||
|
if (claimerName == null && ticket.getClaimerUUID() != null) {
|
||||||
|
claimerName = Bukkit.getOfflinePlayer(ticket.getClaimerUUID()).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fallback: Falls immer noch kein Name da ist, nimm "Support" (nie "Unbekannt")
|
||||||
|
if (claimerName == null) claimerName = "Support";
|
||||||
|
|
||||||
String msg = plugin.formatMessage("messages.ticket-claimed-notify")
|
String msg = plugin.formatMessage("messages.ticket-claimed-notify")
|
||||||
.replace("{id}", String.valueOf(ticket.getId()))
|
.replace("{id}", String.valueOf(ticket.getId()))
|
||||||
.replace("{claimer}", ticket.getClaimerName());
|
.replace("{claimer}", claimerName);
|
||||||
creator.sendMessage(msg);
|
creator.sendMessage(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,8 +118,10 @@ public class TicketManager {
|
|||||||
public void notifyForwardedTo(Ticket ticket, String fromName) {
|
public void notifyForwardedTo(Ticket ticket, String fromName) {
|
||||||
Player target = Bukkit.getPlayer(ticket.getForwardedToUUID());
|
Player target = Bukkit.getPlayer(ticket.getForwardedToUUID());
|
||||||
if (target != null && target.isOnline()) {
|
if (target != null && target.isOnline()) {
|
||||||
|
String creatorName = ticket.getCreatorName() != null ? ticket.getCreatorName() : "Unbekannt";
|
||||||
|
|
||||||
String msg = plugin.formatMessage("messages.ticket-forwarded-notify")
|
String msg = plugin.formatMessage("messages.ticket-forwarded-notify")
|
||||||
.replace("{player}", ticket.getCreatorName())
|
.replace("{player}", creatorName)
|
||||||
.replace("{id}", String.valueOf(ticket.getId()));
|
.replace("{id}", String.valueOf(ticket.getId()));
|
||||||
target.sendMessage(msg);
|
target.sendMessage(msg);
|
||||||
}
|
}
|
||||||
@@ -121,7 +140,6 @@ public class TicketManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Benachrichtigt den Ersteller, wenn sein Ticket geschlossen wurde.
|
* Benachrichtigt den Ersteller, wenn sein Ticket geschlossen wurde.
|
||||||
* @param closerName Name des Admins/Supporters der es geschlossen hat (für Discord, kann null sein)
|
|
||||||
*/
|
*/
|
||||||
public void notifyCreatorClosed(Ticket ticket, String closerName) {
|
public void notifyCreatorClosed(Ticket ticket, String closerName) {
|
||||||
notifiedClosedTickets.add(ticket.getId());
|
notifiedClosedTickets.add(ticket.getId());
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ public class Ticket implements ConfigurationSerializable {
|
|||||||
private Timestamp closedAt;
|
private Timestamp closedAt;
|
||||||
private String closeComment;
|
private String closeComment;
|
||||||
|
|
||||||
|
// ─── NEU: Soft Delete Flag ───
|
||||||
|
private boolean playerDeleted = false;
|
||||||
|
|
||||||
|
|
||||||
public Ticket() {}
|
public Ticket() {}
|
||||||
|
|
||||||
@@ -101,6 +104,11 @@ public class Ticket implements ConfigurationSerializable {
|
|||||||
this.forwardedToUUID = fwdObj instanceof UUID ? (UUID) fwdObj : UUID.fromString((String) fwdObj);
|
this.forwardedToUUID = fwdObj instanceof UUID ? (UUID) fwdObj : UUID.fromString((String) fwdObj);
|
||||||
this.forwardedToName = (String) map.get("forwardedToName");
|
this.forwardedToName = (String) map.get("forwardedToName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── NEU: Laden des Soft Delete Flags ───
|
||||||
|
if (map.containsKey("playerDeleted")) {
|
||||||
|
this.playerDeleted = (boolean) map.get("playerDeleted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NEU: Methode zum Speichern in die YAML (Serialisierung) ---
|
// --- NEU: Methode zum Speichern in die YAML (Serialisierung) ---
|
||||||
@@ -140,6 +148,9 @@ public class Ticket implements ConfigurationSerializable {
|
|||||||
map.put("forwardedToName", forwardedToName);
|
map.put("forwardedToName", forwardedToName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── NEU: Speichern des Soft Delete Flags ───
|
||||||
|
map.put("playerDeleted", playerDeleted);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,4 +224,8 @@ public class Ticket implements ConfigurationSerializable {
|
|||||||
|
|
||||||
public String getCloseComment() { return closeComment; }
|
public String getCloseComment() { return closeComment; }
|
||||||
public void setCloseComment(String closeComment) { this.closeComment = closeComment; }
|
public void setCloseComment(String closeComment) { this.closeComment = closeComment; }
|
||||||
|
|
||||||
|
// ─── NEU: Getter/Setter für Soft Delete ───
|
||||||
|
public boolean isPlayerDeleted() { return playerDeleted; }
|
||||||
|
public void setPlayerDeleted(boolean playerDeleted) { this.playerDeleted = playerDeleted; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: TicketSystem
|
name: TicketSystem
|
||||||
version: 1.0.2
|
version: 1.0.3
|
||||||
main: de.ticketsystem.TicketPlugin
|
main: de.ticketsystem.TicketPlugin
|
||||||
api-version: 1.20
|
api-version: 1.20
|
||||||
author: M_Viper
|
author: M_Viper
|
||||||
@@ -20,6 +20,12 @@ permissions:
|
|||||||
description: Supporter kann Tickets einsehen und claimen
|
description: Supporter kann Tickets einsehen und claimen
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
|
ticket.archive:
|
||||||
|
description: Zugriff auf das Ticket-Archiv (öffnen, einsehen, permanent löschen)
|
||||||
|
default: false
|
||||||
|
|
||||||
ticket.admin:
|
ticket.admin:
|
||||||
description: Admin hat vollen Zugriff inkl. Weiterleitung und Reload
|
description: Admin hat vollen Zugriff inkl. Weiterleitung und Reload
|
||||||
default: op
|
default: op
|
||||||
|
children:
|
||||||
|
ticket.support: true
|
||||||
Reference in New Issue
Block a user