Dieser Commit ist enthalten in:
Ursprung
c1e175c3e8
Commit
1758fc4297
71
src/de/steamwar/sql/CheckedSchematic.java
Normale Datei
71
src/de/steamwar/sql/CheckedSchematic.java
Normale Datei
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CheckedSchematic {
|
||||||
|
|
||||||
|
private static final Table<CheckedSchematic> table = new Table<>(CheckedSchematic.class);
|
||||||
|
private static final SelectStatement<CheckedSchematic> statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
|
||||||
|
|
||||||
|
public static List<CheckedSchematic> getLastDeclinedOfNode(int node){
|
||||||
|
return statusOfNode.listSelect(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Field(nullable = true)
|
||||||
|
private final Integer nodeId;
|
||||||
|
@Field
|
||||||
|
private final int nodeOwner;
|
||||||
|
@Field
|
||||||
|
private final String nodeName;
|
||||||
|
@Getter
|
||||||
|
@Field
|
||||||
|
private final int validator;
|
||||||
|
@Getter
|
||||||
|
@Field
|
||||||
|
private final Timestamp startTime;
|
||||||
|
@Getter
|
||||||
|
@Field
|
||||||
|
private final Timestamp endTime;
|
||||||
|
@Getter
|
||||||
|
@Field
|
||||||
|
private final String declineReason;
|
||||||
|
|
||||||
|
public int getNode() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSchemName() {
|
||||||
|
return nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSchemOwner() {
|
||||||
|
return nodeOwner;
|
||||||
|
}
|
||||||
|
}
|
69
src/de/steamwar/sql/NodeDownload.java
Normale Datei
69
src/de/steamwar/sql/NodeDownload.java
Normale Datei
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Timestamp;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class NodeDownload {
|
||||||
|
|
||||||
|
private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||||
|
private static final String LINK_BASE = "https://steamwar.de/download.php?schem=";
|
||||||
|
|
||||||
|
private static final Table<NodeDownload> table = new Table<>(NodeDownload.class);
|
||||||
|
private static final Statement insert = table.insertFields("NodeId", "Link");
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int nodeId;
|
||||||
|
@Field
|
||||||
|
private final String link;
|
||||||
|
@Field(def = "CURRENT_TIMESTAMP")
|
||||||
|
private final Timestamp timestamp;
|
||||||
|
|
||||||
|
public static String getLink(SchematicNode schem){
|
||||||
|
if(schem.isDir())
|
||||||
|
throw new SecurityException("Can not Download Directorys");
|
||||||
|
MessageDigest digest;
|
||||||
|
try {
|
||||||
|
digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
digest.reset();
|
||||||
|
digest.update((Instant.now().toString() + schem.getOwner() + schem.getId()).getBytes());
|
||||||
|
String hash = base16encode(digest.digest());
|
||||||
|
insert.update(schem.getId(), hash);
|
||||||
|
return LINK_BASE + hash;
|
||||||
|
}
|
||||||
|
public static String base16encode(byte[] byteArray) {
|
||||||
|
StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2);
|
||||||
|
for (byte b : byteArray)
|
||||||
|
hexBuffer.append(HEX[b >> 4]).append(HEX[b & 0xF]);
|
||||||
|
return hexBuffer.toString();
|
||||||
|
}
|
||||||
|
}
|
119
src/de/steamwar/sql/Punishment.java
Normale Datei
119
src/de/steamwar/sql/Punishment.java
Normale Datei
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Punishment {
|
||||||
|
|
||||||
|
static {
|
||||||
|
SqlTypeMapper.nameEnumMapper(PunishmentType.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments");
|
||||||
|
private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
|
||||||
|
private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||||
|
private final int punishmentId;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final int user;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final int punisher;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final PunishmentType type;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final Timestamp startTime;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final Timestamp endTime;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final boolean perma;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final String reason;
|
||||||
|
|
||||||
|
public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
|
||||||
|
return getPunishment.select(user, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
|
||||||
|
return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer<Punishment> callback) {
|
||||||
|
Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
|
||||||
|
if(punishment == null || !punishment.isCurrent()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
callback.accept(punishment);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // Not multiling, misleading title
|
||||||
|
public String getBantime(Timestamp endTime, boolean perma) {
|
||||||
|
if (perma) {
|
||||||
|
return "permanent";
|
||||||
|
} else {
|
||||||
|
return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrent() {
|
||||||
|
return isPerma() || getEndTime().after(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum PunishmentType {
|
||||||
|
Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
|
||||||
|
Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
|
||||||
|
NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
|
||||||
|
NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
|
||||||
|
NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
|
||||||
|
NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER");
|
||||||
|
|
||||||
|
private final boolean needsAdmin;
|
||||||
|
private final String teamMessage;
|
||||||
|
private final String playerMessagePerma;
|
||||||
|
private final String playerMessageUntil;
|
||||||
|
private final String usageNotPunished;
|
||||||
|
private final String unpunishmentMessage;
|
||||||
|
}
|
||||||
|
}
|
@ -28,4 +28,6 @@ public interface SQLWrapper {
|
|||||||
SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
|
SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
|
||||||
|
|
||||||
void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB);
|
void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB);
|
||||||
|
|
||||||
|
void additionalExceptionMetadata(StringBuilder builder);
|
||||||
}
|
}
|
||||||
|
52
src/de/steamwar/sql/SWException.java
Normale Datei
52
src/de/steamwar/sql/SWException.java
Normale Datei
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SWException {
|
||||||
|
|
||||||
|
private static final String CWD = System.getProperty("user.dir");
|
||||||
|
private static final String SERVER_NAME = new File(CWD).getName();
|
||||||
|
|
||||||
|
private static final Table<SWException> table = new Table<>(SWException.class, "Exception");
|
||||||
|
private static final Statement insert = table.insertAll();
|
||||||
|
|
||||||
|
@Field
|
||||||
|
private final String server;
|
||||||
|
@Field
|
||||||
|
private final String message;
|
||||||
|
@Field
|
||||||
|
private final String stacktrace;
|
||||||
|
|
||||||
|
public static void log(String message, String stacktrace){
|
||||||
|
StringBuilder msgBuilder = new StringBuilder(message);
|
||||||
|
SQLWrapper.impl.additionalExceptionMetadata(msgBuilder);
|
||||||
|
msgBuilder.append("\nCWD: ").append(CWD);
|
||||||
|
|
||||||
|
insert.update(SERVER_NAME, msgBuilder.toString(), stacktrace);
|
||||||
|
}
|
||||||
|
}
|
43
src/de/steamwar/sql/SchemElo.java
Normale Datei
43
src/de/steamwar/sql/SchemElo.java
Normale Datei
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SchemElo {
|
||||||
|
|
||||||
|
private static final Table<SchemElo> table = new Table<>(SchemElo.class);
|
||||||
|
private static final SelectStatement<SchemElo> select = table.select(Table.PRIMARY);
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int schemId;
|
||||||
|
@Field
|
||||||
|
private final int elo;
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int season;
|
||||||
|
|
||||||
|
public static int getElo(SchematicNode node, int season) {
|
||||||
|
return select.select(node, season).elo;
|
||||||
|
}
|
||||||
|
}
|
543
src/de/steamwar/sql/SchematicNode.java
Normale Datei
543
src/de/steamwar/sql/SchematicNode.java
Normale Datei
@ -0,0 +1,543 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.*;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SchematicNode {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>();
|
||||||
|
public static void clear() {
|
||||||
|
TAB_CACHE.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"};
|
||||||
|
private static String nodeSelectCreator(String itemPrefix) {
|
||||||
|
return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode ";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
|
||||||
|
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType");
|
||||||
|
private static final Statement update = table.insertAll();
|
||||||
|
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||||
|
|
||||||
|
private static final SelectStatement<SchematicNode> byId = table.select(Table.PRIMARY);
|
||||||
|
private static final SelectStatement<SchematicNode> byOwnerNameParent = table.select("OwnerNameParent");
|
||||||
|
private static final SelectStatement<SchematicNode> byOwnerNameParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL");
|
||||||
|
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> byParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> byParentName = table.selectFields("NodeName", "ParentNode");
|
||||||
|
private static final SelectStatement<SchematicNode> byParentName_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeName = ? AND ParentNode is NULL");
|
||||||
|
private static final SelectStatement<SchematicNode> accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> accessibleByUserTypeParent_Null = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode is null ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName");
|
||||||
|
private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1");
|
||||||
|
private static final SelectStatement<SchematicNode> allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName");
|
||||||
|
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName");
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||||
|
private final int nodeId;
|
||||||
|
@Field(keys = {"OwnerNameParent"})
|
||||||
|
private final int nodeOwner;
|
||||||
|
@Field(keys = {"OwnerNameParent"})
|
||||||
|
private String nodeName;
|
||||||
|
@Field(keys = {"OwnerNameParent"}, nullable = true)
|
||||||
|
private Integer parentNode;
|
||||||
|
@Field(def = "CURRENT_TIMESTAMP")
|
||||||
|
private Timestamp lastUpdate;
|
||||||
|
@Field(def = "''")
|
||||||
|
private String nodeItem;
|
||||||
|
@Field(def = "'normal'", nullable = true)
|
||||||
|
private SchematicType nodeType;
|
||||||
|
@Field(def = "0")
|
||||||
|
private int nodeRank;
|
||||||
|
@Field(def = "1")
|
||||||
|
private boolean replaceColor;
|
||||||
|
@Field(def = "1")
|
||||||
|
private boolean allowReplay;
|
||||||
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@Field(def = "1")
|
||||||
|
private boolean nodeFormat;
|
||||||
|
|
||||||
|
private final Map<Integer, String> brCache = new HashMap<>();
|
||||||
|
|
||||||
|
public SchematicNode(
|
||||||
|
int nodeId,
|
||||||
|
int nodeOwner,
|
||||||
|
String nodeName,
|
||||||
|
Integer parentNode,
|
||||||
|
Timestamp lastUpdate,
|
||||||
|
String nodeItem,
|
||||||
|
SchematicType nodeType,
|
||||||
|
int nodeRank,
|
||||||
|
boolean replaceColor,
|
||||||
|
boolean allowReplay,
|
||||||
|
boolean nodeFormat
|
||||||
|
) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
this.nodeOwner = nodeOwner;
|
||||||
|
this.nodeName = nodeName;
|
||||||
|
this.parentNode = parentNode;
|
||||||
|
this.nodeItem = nodeItem;
|
||||||
|
this.nodeType = nodeType;
|
||||||
|
this.lastUpdate = lastUpdate;
|
||||||
|
this.nodeRank = nodeRank;
|
||||||
|
this.replaceColor = replaceColor;
|
||||||
|
this.allowReplay = allowReplay;
|
||||||
|
this.nodeFormat = nodeFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode createSchematic(int owner, String name, Integer parent) {
|
||||||
|
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) {
|
||||||
|
return createSchematicNode(owner, name, parent, null, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) {
|
||||||
|
if (parent != null && parent == 0)
|
||||||
|
parent = null;
|
||||||
|
int nodeId = create.insertGetKey(owner, name, parent, type, item);
|
||||||
|
return getSchematicNode(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) {
|
||||||
|
return getSchematicNode(owner, name, parent.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode getSchematicNode(int owner, String name, Integer parent) {
|
||||||
|
if (parent == null || parent == 0)
|
||||||
|
return byOwnerNameParent_null.select(owner, name);
|
||||||
|
return byOwnerNameParent.select(owner, name, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
|
||||||
|
return getSchematicNodeInNode(parent.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getSchematicNodeInNode(Integer parent) {
|
||||||
|
if(parent == null || parent == 0) {
|
||||||
|
rootWarning();
|
||||||
|
return byParent_null.listSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return byParent.listSelect(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) {
|
||||||
|
if(parent == null || parent == 0) {
|
||||||
|
rootWarning();
|
||||||
|
return dirsByParent_null.listSelect();
|
||||||
|
}
|
||||||
|
return dirsByParent.listSelect(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) {
|
||||||
|
return getSchematicNode(name, parent.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static SchematicNode getSchematicDirectory(String name, Integer parent) {
|
||||||
|
return getSchematicNode(name, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode getSchematicNode(String name, Integer parent) {
|
||||||
|
if(parent == null || parent == 0) {
|
||||||
|
rootWarning();
|
||||||
|
return byParentName_null.select(name);
|
||||||
|
}
|
||||||
|
return byParentName.select(name, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode getSchematicNode(int id) {
|
||||||
|
return byId.select(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) {
|
||||||
|
if(parent == null || parent == 0)
|
||||||
|
return accessibleByUserTypeParent_Null.listSelect(owner, owner, schemType);
|
||||||
|
return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
|
||||||
|
return accessibleByUserType.listSelect(user, user, schemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
|
||||||
|
return byOwnerType.listSelect(owner, schemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static List<SchematicNode> getAllSchematicsOfType(String schemType) {
|
||||||
|
return byType.listSelect(schemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
|
||||||
|
return byType.listSelect(schemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) {
|
||||||
|
List<SchematicNode> finalList = new ArrayList<>();
|
||||||
|
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
|
||||||
|
nodes.forEach(node -> {
|
||||||
|
if (node.isDir()) {
|
||||||
|
finalList.addAll(deepGet(node.getId(), filter));
|
||||||
|
} else {
|
||||||
|
if (filter.test(node))
|
||||||
|
finalList.add(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return finalList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
|
||||||
|
if (parent == null || parent == 0)
|
||||||
|
return accessibleByUser.listSelect(user, user, user, user);
|
||||||
|
|
||||||
|
if(schematicAccessibleForUser.select(rs -> {
|
||||||
|
rs.next();
|
||||||
|
return rs.getInt("Accessible") > 0;
|
||||||
|
}, parent, user, user))
|
||||||
|
return getSchematicNodeInNode(parent);
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
|
||||||
|
return allAccessibleByUser.listSelect(user, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
|
||||||
|
return getAllParentsOfNode(node.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> getAllParentsOfNode(int node) {
|
||||||
|
return allParentsOfNode.listSelect(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchematicNode getNodeFromPath(SteamwarUser user, String s) {
|
||||||
|
if (s.startsWith("/")) {
|
||||||
|
s = s.substring(1);
|
||||||
|
}
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (s.contains("/")) {
|
||||||
|
String[] layers = s.split("/");
|
||||||
|
SchematicNode currentNode = null;
|
||||||
|
for (int i = 0; i < layers.length; i++) {
|
||||||
|
int finalI = i;
|
||||||
|
Optional<SchematicNode> node;
|
||||||
|
if (currentNode == null) {
|
||||||
|
node = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny();
|
||||||
|
} else {
|
||||||
|
node = Optional.ofNullable(SchematicNode.getSchematicNode(layers[i], currentNode.getId()));
|
||||||
|
}
|
||||||
|
if (!node.isPresent()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
currentNode = node.get();
|
||||||
|
if (!currentNode.isDir() && i != layers.length - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentNode;
|
||||||
|
} else {
|
||||||
|
String finalS = s;
|
||||||
|
return SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(finalS)).findAny().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) {
|
||||||
|
List<SchematicNode> finalList = new ArrayList<>();
|
||||||
|
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, null);
|
||||||
|
nodes.forEach(node -> {
|
||||||
|
if (node.isDir()) {
|
||||||
|
finalList.addAll(deepGet(node.getId(), filter));
|
||||||
|
} else {
|
||||||
|
if (filter.test(node))
|
||||||
|
finalList.add(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return finalList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static Integer countNodes() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOwner() {
|
||||||
|
return nodeOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.nodeName = name;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getParent() {
|
||||||
|
return parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Integer parent) {
|
||||||
|
this.parentNode = parent;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getItem() {
|
||||||
|
if (nodeItem.isEmpty()) {
|
||||||
|
return isDir() ? "CHEST" : "CAULDRON_ITEM";
|
||||||
|
}
|
||||||
|
return nodeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(String item) {
|
||||||
|
this.nodeItem = item;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public String getType() {
|
||||||
|
return nodeType.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setType(String type) {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Node is Directory");
|
||||||
|
this.nodeType = SchematicType.fromDB(type);
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDir() {
|
||||||
|
return nodeType == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSchemFormat() {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Node is Directory");
|
||||||
|
return nodeFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRank() {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Node is Directory");
|
||||||
|
return nodeRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public int getRankUnsafe() {
|
||||||
|
return nodeRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRank(int rank) {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Node is Directory");
|
||||||
|
this.nodeRank = rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchematicType getSchemtype() {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Is Directory");
|
||||||
|
return nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchemtype(SchematicType type) {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Is Directory");
|
||||||
|
this.nodeType = type;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean replaceColor() {
|
||||||
|
return replaceColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplaceColor(boolean replaceColor) {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Is Directory");
|
||||||
|
this.replaceColor = replaceColor;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowReplay() {
|
||||||
|
return allowReplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowReplay(boolean allowReplay) {
|
||||||
|
if(isDir())
|
||||||
|
throw new SecurityException("Is Directory");
|
||||||
|
this.allowReplay = allowReplay;
|
||||||
|
updateDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchematicNode getParentNode() {
|
||||||
|
if(parentNode == null) return null;
|
||||||
|
return SchematicNode.getSchematicNode(parentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getElo(int season) {
|
||||||
|
return SchemElo.getElo(this, season);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean accessibleByUser(int user) {
|
||||||
|
return NodeMember.getNodeMember(nodeId, user) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<NodeMember> getMembers() {
|
||||||
|
return NodeMember.getNodeMembers(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timestamp getLastUpdate() {
|
||||||
|
return lastUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDB() {
|
||||||
|
this.lastUpdate = Timestamp.from(Instant.now());
|
||||||
|
update.update(nodeId, nodeOwner, nodeName, parentNode, nodeItem, nodeType, lastUpdate, nodeRank, replaceColor, allowReplay, nodeFormat);
|
||||||
|
this.brCache.clear();
|
||||||
|
TAB_CACHE.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
delete.update(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof SchematicNode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ((SchematicNode) obj).getId() == nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateBreadcrumbs(SteamwarUser user) {
|
||||||
|
return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generateBreadcrumbs(String split, SteamwarUser user) {
|
||||||
|
StringBuilder builder = new StringBuilder(getName());
|
||||||
|
SchematicNode currentNode = this;
|
||||||
|
if (currentNode.isDir()) builder.append("/");
|
||||||
|
final Set<NodeMember> nodeMembers = NodeMember.getSchematics(user.getId());
|
||||||
|
AtomicInteger i = new AtomicInteger();
|
||||||
|
i.set(currentNode.getId());
|
||||||
|
while (currentNode.getParentNode() != null && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) {
|
||||||
|
currentNode = currentNode.getParentNode();
|
||||||
|
i.set(currentNode.getId());
|
||||||
|
builder.insert(0, split)
|
||||||
|
.insert(0, currentNode.getName());
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
|
||||||
|
public static boolean invalidSchemName(String[] layers) {
|
||||||
|
for (String layer : layers) {
|
||||||
|
if (layer.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (layer.contains("/") ||
|
||||||
|
layer.contains("\\") ||
|
||||||
|
layer.contains("<") ||
|
||||||
|
layer.contains(">") ||
|
||||||
|
layer.contains("^") ||
|
||||||
|
layer.contains("°") ||
|
||||||
|
layer.contains("'") ||
|
||||||
|
layer.contains("\"") ||
|
||||||
|
layer.contains(" ")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getNodeTabcomplete(SteamwarUser user, String s) {
|
||||||
|
boolean sws = s.startsWith("/");
|
||||||
|
if (sws) {
|
||||||
|
s = s.substring(1);
|
||||||
|
}
|
||||||
|
int index = s.lastIndexOf("/");
|
||||||
|
String cacheKey = index == -1 ? "" : s.substring(0, index);
|
||||||
|
if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) {
|
||||||
|
return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey));
|
||||||
|
}
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
if (s.contains("/")) {
|
||||||
|
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
|
||||||
|
SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab);
|
||||||
|
if (pa == null) return Collections.emptyList();
|
||||||
|
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(pa);
|
||||||
|
nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user)));
|
||||||
|
} else {
|
||||||
|
List<SchematicNode> nodes = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0);
|
||||||
|
nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : "")));
|
||||||
|
}
|
||||||
|
list.remove("//copy");
|
||||||
|
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void rootWarning() {
|
||||||
|
ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
|
||||||
|
new Throwable().printStackTrace(new PrintStream(stacktraceOutput));
|
||||||
|
SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -101,7 +101,7 @@ public class SchematicType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static SchematicType fromDB(String input){
|
public static SchematicType fromDB(String input){
|
||||||
return fromDB.getOrDefault(input.toLowerCase(), null);
|
return fromDB.get(input.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<SchematicType> values(){
|
public static List<SchematicType> values(){
|
||||||
|
54
src/de/steamwar/sql/Season.java
Normale Datei
54
src/de/steamwar/sql/Season.java
Normale Datei
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
public class Season {
|
||||||
|
private Season() {}
|
||||||
|
|
||||||
|
public static int getSeason() {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
int yearIndex = calendar.get(Calendar.MONTH) / 4;
|
||||||
|
return (calendar.get(Calendar.YEAR) * 3 + yearIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSeasonStart() {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) / 4 * 3 + 1) + "-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String convertSeasonToString(int season){
|
||||||
|
if (season == -1) return "";
|
||||||
|
int yearSeason = season % 3;
|
||||||
|
int year = (season - yearSeason) / 3;
|
||||||
|
return String.format("%d-%d", year, yearSeason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int convertSeasonToNumber(String season){
|
||||||
|
if (season.isEmpty()) return -1;
|
||||||
|
String[] split = season.split("-");
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(split[0]) * 3 + Integer.parseInt(split[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,7 @@ public class SteamwarUser {
|
|||||||
private static final SelectStatement<SteamwarUser> byUUID = table.selectFields("UUID");
|
private static final SelectStatement<SteamwarUser> byUUID = table.selectFields("UUID");
|
||||||
private static final SelectStatement<SteamwarUser> byName = table.selectFields("UserName");
|
private static final SelectStatement<SteamwarUser> byName = table.selectFields("UserName");
|
||||||
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
|
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
|
||||||
|
private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team");
|
||||||
private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
|
private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
|
||||||
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
|
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
|
||||||
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
|
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
|
||||||
@ -147,7 +148,15 @@ public class SteamwarUser {
|
|||||||
return byDiscord.select(discordId);
|
return byDiscord.select(discordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void createOrUpdateUsername(UUID uuid, String userName) {
|
||||||
|
insert.update(uuid, userName);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<SteamwarUser> getServerTeam() {
|
public static List<SteamwarUser> getServerTeam() {
|
||||||
return getServerTeam.listSelect();
|
return getServerTeam.listSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<SteamwarUser> getTeam(int teamId) {
|
||||||
|
return byTeam.listSelect(teamId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
61
src/de/steamwar/sql/Team.java
Normale Datei
61
src/de/steamwar/sql/Team.java
Normale Datei
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
This file is a part of the SteamWar software.
|
||||||
|
|
||||||
|
Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Team {
|
||||||
|
|
||||||
|
private static final Table<Team> table = new Table<>(Team.class);
|
||||||
|
private static final SelectStatement<Team> select = table.select(Table.PRIMARY);
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
@Getter
|
||||||
|
private final int teamId;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final String teamKuerzel;
|
||||||
|
@Field
|
||||||
|
@Getter
|
||||||
|
private final String teamName;
|
||||||
|
@Field(def = "'8'")
|
||||||
|
@Getter
|
||||||
|
private final String teamColor;
|
||||||
|
|
||||||
|
private static final Team pub = new Team(0, "PUB", "Öffentlich", "8");
|
||||||
|
|
||||||
|
public static Team get(int id) {
|
||||||
|
if(id == 0)
|
||||||
|
return pub;
|
||||||
|
return select.select(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getMembers(){
|
||||||
|
return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
54
src/de/steamwar/sql/TeamTeilnahme.java
Normale Datei
54
src/de/steamwar/sql/TeamTeilnahme.java
Normale Datei
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TeamTeilnahme {
|
||||||
|
|
||||||
|
private static final Table<TeamTeilnahme> table = new Table<>(TeamTeilnahme.class);
|
||||||
|
private static final SelectStatement<TeamTeilnahme> select = table.select(Table.PRIMARY);
|
||||||
|
private static final SelectStatement<TeamTeilnahme> selectTeams = table.selectFields("EventID");
|
||||||
|
private static final SelectStatement<TeamTeilnahme> selectEvents = table.selectFields("TeamID");
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int teamId;
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int eventId;
|
||||||
|
|
||||||
|
public static boolean nimmtTeil(int teamID, int eventID){
|
||||||
|
return select.select(teamID, eventID) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Team> getTeams(int eventID){
|
||||||
|
return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Event> getEvents(int teamID){
|
||||||
|
return selectEvents.listSelect(teamID).stream().map(tt -> Event.get(tt.eventId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
|
||||||
|
}
|
||||||
|
}
|
72
src/de/steamwar/sql/UserConfig.java
Normale Datei
72
src/de/steamwar/sql/UserConfig.java
Normale Datei
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.Field;
|
||||||
|
import de.steamwar.sql.internal.SelectStatement;
|
||||||
|
import de.steamwar.sql.internal.Statement;
|
||||||
|
import de.steamwar.sql.internal.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UserConfig {
|
||||||
|
|
||||||
|
private static final Table<UserConfig> table = new Table<>(UserConfig.class);
|
||||||
|
private static final SelectStatement<UserConfig> select = table.select(Table.PRIMARY);
|
||||||
|
private static final Statement insert = table.insertAll();
|
||||||
|
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||||
|
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final int user;
|
||||||
|
@Field(keys = {Table.PRIMARY})
|
||||||
|
private final String config;
|
||||||
|
@Field
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public static String getConfig(UUID player, String config) {
|
||||||
|
return getConfig(SteamwarUser.get(player).getId(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getConfig(int player, String config) {
|
||||||
|
return select.select(player, config).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updatePlayerConfig(UUID uuid, String config, String value) {
|
||||||
|
updatePlayerConfig(SteamwarUser.get(uuid).getId(), config, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updatePlayerConfig(int id, String config, String value) {
|
||||||
|
if (value == null) {
|
||||||
|
removePlayerConfig(id, config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
insert.update(id, config, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePlayerConfig(UUID uuid, String config) {
|
||||||
|
removePlayerConfig(SteamwarUser.get(uuid).getId(), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePlayerConfig(int id, String config) {
|
||||||
|
delete.update(id, config);
|
||||||
|
}
|
||||||
|
}
|
@ -39,10 +39,14 @@ public class Statement implements AutoCloseable {
|
|||||||
private static final Supplier<Connection> conProvider;
|
private static final Supplier<Connection> conProvider;
|
||||||
static final Consumer<Table<?>> schemaCreator;
|
static final Consumer<Table<?>> schemaCreator;
|
||||||
|
|
||||||
|
private static final boolean mysqlMode;
|
||||||
|
private static final boolean productionDatabase;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
File file = new File(System.getProperty("user.home"), "mysql.properties");
|
File file = new File(System.getProperty("user.home"), "mysql.properties");
|
||||||
|
mysqlMode = file.exists();
|
||||||
|
|
||||||
if(file.exists()) {
|
if(mysqlMode) {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
try {
|
try {
|
||||||
properties.load(new FileReader(file));
|
properties.load(new FileReader(file));
|
||||||
@ -54,6 +58,7 @@ public class Statement implements AutoCloseable {
|
|||||||
String user = properties.getProperty("user");
|
String user = properties.getProperty("user");
|
||||||
String password = properties.getProperty("password");
|
String password = properties.getProperty("password");
|
||||||
|
|
||||||
|
productionDatabase = "core".equals(properties.getProperty("database"));
|
||||||
MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
|
MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
|
||||||
conProvider = () -> {
|
conProvider = () -> {
|
||||||
try {
|
try {
|
||||||
@ -64,7 +69,6 @@ public class Statement implements AutoCloseable {
|
|||||||
};
|
};
|
||||||
schemaCreator = table -> {};
|
schemaCreator = table -> {};
|
||||||
} else {
|
} else {
|
||||||
MAX_CONNECTIONS = 1;
|
|
||||||
Connection connection;
|
Connection connection;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -74,6 +78,8 @@ public class Statement implements AutoCloseable {
|
|||||||
throw new SecurityException("Could not create sqlite connection", e);
|
throw new SecurityException("Could not create sqlite connection", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
productionDatabase = false;
|
||||||
|
MAX_CONNECTIONS = 1;
|
||||||
conProvider = () -> connection;
|
conProvider = () -> connection;
|
||||||
schemaCreator = Table::ensureExistanceInSqlite;
|
schemaCreator = Table::ensureExistanceInSqlite;
|
||||||
}
|
}
|
||||||
@ -92,6 +98,14 @@ public class Statement implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean mysqlMode() {
|
||||||
|
return mysqlMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean productionDatabase() {
|
||||||
|
return productionDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
private final boolean returnGeneratedKeys;
|
private final boolean returnGeneratedKeys;
|
||||||
private final String sql;
|
private final String sql;
|
||||||
private final Map<Connection, PreparedStatement> cachedStatements = new HashMap<>();
|
private final Map<Connection, PreparedStatement> cachedStatements = new HashMap<>();
|
||||||
|
@ -59,7 +59,6 @@ public class Table<T> {
|
|||||||
Statement.schemaCreator.accept(this);
|
Statement.schemaCreator.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SelectStatement<T> select(String name) {
|
public SelectStatement<T> select(String name) {
|
||||||
return selectFields(keyFields(name));
|
return selectFields(keyFields(name));
|
||||||
}
|
}
|
||||||
@ -108,7 +107,7 @@ public class Table<T> {
|
|||||||
List<TableField<?>> primaryKey = keys.containsKey(PRIMARY) ? Arrays.asList(keys.get(PRIMARY)) : Collections.emptyList();
|
List<TableField<?>> primaryKey = keys.containsKey(PRIMARY) ? Arrays.asList(keys.get(PRIMARY)) : Collections.emptyList();
|
||||||
try (Statement statement = new Statement(
|
try (Statement statement = new Statement(
|
||||||
"CREATE TABLE IF NOT EXISTS " + name + "(" +
|
"CREATE TABLE IF NOT EXISTS " + name + "(" +
|
||||||
Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? "" : " NOT NULL DEFAULT NULL") + (!field.field.nullable() && field.field.def().equals("") ? "" : " DEFAULT " + field.field.def()) + (primaryKey.contains(field) ? " PRIMARY KEY" : "") + (field.field.autoincrement() ? " AUTOINCREMENT" : "")).collect(Collectors.joining(", ")) +
|
Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def()) + (primaryKey.contains(field) ? " PRIMARY KEY" : "") + (field.field.autoincrement() ? " AUTOINCREMENT" : "")).collect(Collectors.joining(", ")) +
|
||||||
keys.entrySet().stream().filter(entry -> !entry.getKey().equals(PRIMARY)).map(key -> ", UNIQUE (" + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
|
keys.entrySet().stream().filter(entry -> !entry.getKey().equals(PRIMARY)).map(key -> ", UNIQUE (" + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
|
||||||
") STRICT, WITHOUT ROWID")) {
|
") STRICT, WITHOUT ROWID")) {
|
||||||
statement.update();
|
statement.update();
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren