Upload folder via GUI - src

This commit is contained in:
Git Manager GUI
2026-04-15 19:11:02 +02:00
parent bafddee288
commit d66871234c
20 changed files with 3854 additions and 451 deletions

View File

@@ -1,6 +1,7 @@
package de.ticketsystem.manager;
import de.ticketsystem.TicketPlugin;
import de.ticketsystem.model.FaqCategory;
import de.ticketsystem.model.FaqEntry;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -10,28 +11,40 @@ import java.io.IOException;
import java.util.*;
/**
* Manages FAQ entries stored in faqs.yml.
* Manages FAQ entries and FAQ categories stored in faqs.yml.
*
* Admins can add, edit and delete FAQs in-game.
* All changes are saved immediately to faqs.yml.
* faqs.yml wird beim ersten Start automatisch mit Beispiel-Kategorien und -FAQs generiert.
* Admins können Kategorien und FAQs direkt in-game verwalten (GUI + Befehle).
*
* faqs.yml layout:
* faqs.yml Layout:
*
* categories:
* tickets:
* name: "Tickets"
* color: "&b"
* description: "Fragen zum Ticket-System"
*
* faqs:
* 1:
* question: "Wie erstelle ich ein Ticket?"
* answer: "Nutze /ticket create [Kategorie] [Beschreibung]."
* 2:
* question: "..."
* answer: "..."
* answer: "Nutze /ticket create ..."
* category: "tickets"
*
* Material und Textur der Kategorie-Items werden NICHT hier gespeichert
* sie werden zentral in config.yml unter gui-settings.faq.category-head-item gesteuert.
*/
public class FaqManager {
private final TicketPlugin plugin;
private final File faqFile;
private YamlConfiguration faqConfig;
private final List<FaqEntry> entries = new ArrayList<>();
private int nextId = 1;
public static final String UNCATEGORIZED_KEY = "__none__";
private final TicketPlugin plugin;
private final File faqFile;
private YamlConfiguration faqConfig;
private final List<FaqEntry> entries = new ArrayList<>();
private final LinkedHashMap<String, FaqCategory> categories = new LinkedHashMap<>();
private int nextId = 1;
public FaqManager(TicketPlugin plugin) {
this.plugin = plugin;
@@ -43,6 +56,7 @@ public class FaqManager {
private void load() {
entries.clear();
categories.clear();
nextId = 1;
if (!faqFile.exists()) {
@@ -59,16 +73,37 @@ public class FaqManager {
}
faqConfig = YamlConfiguration.loadConfiguration(faqFile);
ConfigurationSection section = faqConfig.getConfigurationSection("faqs");
if (section != null) {
for (String key : section.getKeys(false)) {
// ── 1. Kategorien laden ───────────────────────────────────────────
ConfigurationSection catSection = faqConfig.getConfigurationSection("categories");
if (catSection != null && !catSection.getKeys(false).isEmpty()) {
for (String key : catSection.getKeys(false)) {
ConfigurationSection cat = catSection.getConfigurationSection(key);
if (cat == null) continue;
String name = cat.getString("name", capitalize(key));
String color = cat.getString("color", "&7");
String desc = cat.getString("description", "");
categories.put(key.toLowerCase(), new FaqCategory(key, name, color, desc));
}
if (plugin.isDebug()) {
plugin.getLogger().info("[FaqManager] " + categories.size()
+ " FAQ-Kategorie(n) geladen: " + String.join(", ", categories.keySet()));
}
}
// ── 2. FAQ-Einträge laden ─────────────────────────────────────────
ConfigurationSection faqSection = faqConfig.getConfigurationSection("faqs");
if (faqSection != null) {
for (String key : faqSection.getKeys(false)) {
try {
int id = Integer.parseInt(key);
String question = faqConfig.getString("faqs." + key + ".question", "");
String answer = faqConfig.getString("faqs." + key + ".answer", "");
String category = faqConfig.getString("faqs." + key + ".category", null);
if (!question.isBlank() && !answer.isBlank()) {
entries.add(new FaqEntry(id, question, answer));
FaqEntry entry = new FaqEntry(id, question, answer);
entry.setCategoryKey(normalizeCategoryKey(category));
entries.add(entry);
if (id >= nextId) nextId = id + 1;
}
} catch (NumberFormatException ignored) {}
@@ -82,31 +117,56 @@ public class FaqManager {
}
}
/** Writes the example FAQs into a freshly created faqs.yml. */
private void loadDefaults() {
faqConfig.options().header(
"FAQ-System faqs.yml\n" +
"Wird automatisch generiert. Kategorien sind optional.\n" +
"Material/Textur der Kategorie-Items: config.yml → gui-settings.faq.category-head-item"
);
writeCategory("general", "Allgemein", "&e", "Allgemeine Fragen zum Server");
writeCategory("rules", "Regeln", "&c", "Fragen zu den Server-Regeln");
writeCategory("gameplay", "Gameplay", "&a", "Fragen zum Spielgeschehen");
writeCategory("tickets", "Tickets", "&b", "Fragen zum Ticket-System");
categories.put("general", new FaqCategory("general", "Allgemein", "&e", "Allgemeine Fragen zum Server"));
categories.put("rules", new FaqCategory("rules", "Regeln", "&c", "Fragen zu den Server-Regeln"));
categories.put("gameplay", new FaqCategory("gameplay", "Gameplay", "&a", "Fragen zum Spielgeschehen"));
categories.put("tickets", new FaqCategory("tickets", "Tickets", "&b", "Fragen zum Ticket-System"));
writeEntry(1, "Wie erstelle ich ein Ticket?",
"Nutze den Befehl /ticket create [Kategorie] [Prio][Beschreibung] um ein neues Ticket zu erstellen.");
"Nutze den Befehl /ticket create [Kategorie] [Prio] <Beschreibung>.", "tickets");
writeEntry(2, "Wie lange dauert die Bearbeitung?",
"Unser Support-Team bearbeitet Tickets so schnell wie möglich. Bitte habe etwas Geduld.");
"Unser Support-Team bearbeitet Tickets so schnell wie möglich.", "tickets");
writeEntry(3, "Kann ich mein Ticket löschen?",
"Ja! Öffne /ticket list und klicke auf dein Ticket, um es aus der Übersicht zu entfernen.");
"Ja! Öffne /ticket list und klicke auf dein Ticket.", "tickets");
writeEntry(4, "Wie kann ich meinen Support bewerten?",
"Nach dem Schließen eines Tickets kannst du mit /ticket rate <ID> good/bad eine Bewertung abgeben.");
"Mit /ticket rate <ID> good/bad nach dem Schließen.", "tickets");
nextId = 5;
// Sync entries list with what we just wrote Text muss identisch sein!
entries.add(new FaqEntry(1, "Wie erstelle ich ein Ticket?",
"Nutze den Befehl /ticket create [Kategorie] [Prio][Beschreibung] um ein neues Ticket zu erstellen."));
entries.add(new FaqEntry(2, "Wie lange dauert die Bearbeitung?",
"Unser Support-Team bearbeitet Tickets so schnell wie möglich. Bitte habe etwas Geduld."));
entries.add(new FaqEntry(3, "Kann ich mein Ticket löschen?",
"Ja! Öffne /ticket list und klicke auf dein Ticket, um es aus der Übersicht zu entfernen."));
entries.add(new FaqEntry(4, "Wie kann ich meinen Support bewerten?",
"Nach dem Schließen eines Tickets kannst du mit /ticket rate <ID> good/bad eine Bewertung abgeben."));
for (int i = 1; i <= 4; i++) {
String q = faqConfig.getString("faqs." + i + ".question", "");
String a = faqConfig.getString("faqs." + i + ".answer", "");
FaqEntry e = new FaqEntry(i, q, a);
e.setCategoryKey("tickets");
entries.add(e);
}
}
private void writeEntry(int id, String question, String answer) {
// ── YAML-Hilfsmethoden ─────────────────────────────────────────────────
private void writeCategory(String key, String name, String color, String description) {
faqConfig.set("categories." + key + ".name", name);
faqConfig.set("categories." + key + ".color", color);
faqConfig.set("categories." + key + ".description", description);
}
private void writeEntry(int id, String question, String answer, String categoryKey) {
faqConfig.set("faqs." + id + ".question", question);
faqConfig.set("faqs." + id + ".answer", answer);
if (categoryKey != null && !categoryKey.equals(UNCATEGORIZED_KEY)) {
faqConfig.set("faqs." + id + ".category", categoryKey);
}
}
private void save() {
@@ -117,54 +177,130 @@ public class FaqManager {
}
}
// ─────────────────────────── Public API ────────────────────────────────
// ─────────────────────────── Public API Kategorien ───────────────────
/** Returns an unmodifiable view of all FAQ entries in ID order. */
public List<FaqEntry> getAll() {
return Collections.unmodifiableList(entries);
public boolean hasCategoriesEnabled() { return !categories.isEmpty(); }
public List<FaqCategory> getAllCategories() {
return Collections.unmodifiableList(new ArrayList<>(categories.values()));
}
/** Looks up an entry by its numeric ID. Returns null if not found. */
public FaqEntry getById(int id) {
return entries.stream().filter(e -> e.getId() == id).findFirst().orElse(null);
public FaqCategory getCategoryByKey(String key) {
if (key == null) return null;
return categories.get(key.toLowerCase());
}
/**
* Adds a new FAQ entry and saves immediately.
* Fügt eine neue Kategorie hinzu und speichert sofort.
*
* @param question The question text.
* @param answer The answer text.
* @return The newly created {@link FaqEntry}.
* @return null wenn der Schlüssel bereits existiert, sonst die neue FaqCategory.
*/
public FaqEntry add(String question, String answer) {
int id = nextId++;
FaqEntry entry = new FaqEntry(id, question, answer);
entries.add(entry);
writeEntry(id, question, answer);
public FaqCategory addCategory(String key, String name, String color, String description) {
String lowerKey = key.toLowerCase().replaceAll("\\s+", "_");
if (categories.containsKey(lowerKey)) return null;
FaqCategory cat = new FaqCategory(lowerKey, name, color, description);
categories.put(lowerKey, cat);
writeCategory(lowerKey, name, color, description);
save();
return entry;
return cat;
}
/**
* Edits an existing FAQ entry and saves immediately.
* Bearbeitet eine bestehende Kategorie und speichert sofort.
*
* @return true if the entry was found and updated, false otherwise.
* @return true wenn gefunden und aktualisiert.
*/
public boolean edit(int id, String question, String answer) {
FaqEntry entry = getById(id);
if (entry == null) return false;
entry.setQuestion(question);
entry.setAnswer(answer);
writeEntry(id, question, answer);
public boolean editCategory(String key, String name, String color, String description) {
String lowerKey = key.toLowerCase();
if (!categories.containsKey(lowerKey)) return false;
FaqCategory updated = new FaqCategory(lowerKey, name, color, description);
categories.put(lowerKey, updated);
writeCategory(lowerKey, name, color, description);
save();
return true;
}
/**
* Deletes a FAQ entry and saves immediately.
* Löscht eine Kategorie. FAQs dieser Kategorie werden auf UNCATEGORIZED_KEY gesetzt.
*
* @return true if the entry was found and deleted, false otherwise.
* @return true wenn gefunden und gelöscht.
*/
public boolean deleteCategory(String key) {
String lowerKey = key.toLowerCase();
if (!categories.containsKey(lowerKey)) return false;
categories.remove(lowerKey);
faqConfig.set("categories." + lowerKey, null);
// FAQs dieser Kategorie auf "keine Kategorie" setzen
for (FaqEntry entry : entries) {
if (lowerKey.equals(entry.getCategoryKey())) {
entry.setCategoryKey(UNCATEGORIZED_KEY);
faqConfig.set("faqs." + entry.getId() + ".category", null);
}
}
save();
return true;
}
public List<FaqEntry> getByCategory(String categoryKey) {
String normalizedKey = normalizeCategoryKey(categoryKey);
List<FaqEntry> result = new ArrayList<>();
for (FaqEntry e : entries) {
if (normalizedKey.equals(e.getCategoryKey())) result.add(e);
}
return Collections.unmodifiableList(result);
}
public int countByCategory(String categoryKey) {
return getByCategory(categoryKey).size();
}
// ─────────────────────────── Public API Einträge ─────────────────────
public List<FaqEntry> getAll() { return Collections.unmodifiableList(entries); }
public FaqEntry getById(int id) {
return entries.stream().filter(e -> e.getId() == id).findFirst().orElse(null);
}
public FaqEntry add(String question, String answer, String categoryKey) {
int id = nextId++;
String normalizedKey = normalizeCategoryKey(categoryKey);
FaqEntry entry = new FaqEntry(id, question, answer);
entry.setCategoryKey(normalizedKey);
entries.add(entry);
writeEntry(id, question, answer, normalizedKey);
save();
return entry;
}
public FaqEntry add(String question, String answer) {
return add(question, answer, null);
}
public boolean edit(int id, String question, String answer) {
FaqEntry entry = getById(id);
if (entry == null) return false;
entry.setQuestion(question);
entry.setAnswer(answer);
writeEntry(id, question, answer, entry.getCategoryKey());
save();
return true;
}
public boolean setCategory(int id, String categoryKey) {
FaqEntry entry = getById(id);
if (entry == null) return false;
String normalizedKey = normalizeCategoryKey(categoryKey);
entry.setCategoryKey(normalizedKey);
if (normalizedKey.equals(UNCATEGORIZED_KEY)) {
faqConfig.set("faqs." + id + ".category", null);
} else {
faqConfig.set("faqs." + id + ".category", normalizedKey);
}
save();
return true;
}
public boolean delete(int id) {
FaqEntry entry = getById(id);
if (entry == null) return false;
@@ -174,8 +310,20 @@ public class FaqManager {
return true;
}
/** Reloads FAQs from faqs.yml without restarting the server. */
public void reload() {
load();
public void reload() { load(); }
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
private String normalizeCategoryKey(String key) {
if (key == null || key.isBlank()) return UNCATEGORIZED_KEY;
String lower = key.toLowerCase();
if (lower.equals(UNCATEGORIZED_KEY)) return UNCATEGORIZED_KEY;
if (!categories.isEmpty() && !categories.containsKey(lower)) return UNCATEGORIZED_KEY;
return lower;
}
private static String capitalize(String s) {
if (s == null || s.isEmpty()) return s;
return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
}
}