Convert the old data to our new data and move the old files.
Closes #24 but it probably has a few bugs or prisoners who were in cells are not any more, but I will only fix that if someone pursades me to.
This commit is contained in:
parent
48fe312193
commit
3e3f2d432e
@ -50,6 +50,8 @@ Notice
|
|||||||
* Old messages (language) will not be converted
|
* Old messages (language) will not be converted
|
||||||
* MaximumAFKTime setting will not convert over, the format isn't clear and the old version didn't provide a way to get values with decimal places
|
* MaximumAFKTime setting will not convert over, the format isn't clear and the old version didn't provide a way to get values with decimal places
|
||||||
* EnableLogging has been removed, we are always going to be logging (unless major request to control this)
|
* EnableLogging has been removed, we are always going to be logging (unless major request to control this)
|
||||||
|
* Prisoner's old inventory strings in the database are lost, we can not convert those
|
||||||
|
* Prisoner's Previous Position is not converted.
|
||||||
|
|
||||||
[Jail 3.0 JavaDoc](http://ci.graywolf336.com/job/Jail/javadoc)
|
[Jail 3.0 JavaDoc](http://ci.graywolf336.com/job/Jail/javadoc)
|
||||||
====
|
====
|
||||||
|
@ -126,7 +126,7 @@ public class JailIO {
|
|||||||
try {
|
try {
|
||||||
Class.forName("org.sqlite.JDBC");
|
Class.forName("org.sqlite.JDBC");
|
||||||
pl.getLogger().info("Connecting to the sqlite database.");
|
pl.getLogger().info("Connecting to the sqlite database.");
|
||||||
Connection sqliteConnection = DriverManager.getConnection("jdbc:sqlite:" + new File(pl.getDataFolder().getPath(), "jail.sqlite").getPath());
|
Connection sqliteConnection = DriverManager.getConnection("jdbc:sqlite:" + new File(pl.getDataFolder().getPath(), "jail3.sqlite").getPath());
|
||||||
sqliteConnection.setAutoCommit(true);
|
sqliteConnection.setAutoCommit(true);
|
||||||
this.con = sqliteConnection;
|
this.con = sqliteConnection;
|
||||||
pl.debug("Connection created for sqlite.");
|
pl.debug("Connection created for sqlite.");
|
||||||
@ -235,6 +235,7 @@ public class JailIO {
|
|||||||
Statement st = con.createStatement();
|
Statement st = con.createStatement();
|
||||||
switch(storage){
|
switch(storage){
|
||||||
case 1:
|
case 1:
|
||||||
|
//TODO: yeah big time!
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
String jailCreateCmd = "CREATE TABLE IF NOT EXISTS `" + prefix + "jails` ("
|
String jailCreateCmd = "CREATE TABLE IF NOT EXISTS `" + prefix + "jails` ("
|
||||||
@ -336,10 +337,8 @@ public class JailIO {
|
|||||||
public void loadJails() {
|
public void loadJails() {
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
//load the jails from sqlite
|
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
//load the jails from mysql
|
//load the jails from mysql and sqlite
|
||||||
long st = System.currentTimeMillis();
|
long st = System.currentTimeMillis();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -649,7 +648,6 @@ public class JailIO {
|
|||||||
public void saveJail(Jail j) {
|
public void saveJail(Jail j) {
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
long st = System.currentTimeMillis();
|
long st = System.currentTimeMillis();
|
||||||
|
|
||||||
@ -860,7 +858,6 @@ public class JailIO {
|
|||||||
public void saveCell(Jail j, Cell c) {
|
public void saveCell(Jail j, Cell c) {
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
try {
|
try {
|
||||||
if(con == null) this.prepareStorage(false);
|
if(con == null) this.prepareStorage(false);
|
||||||
@ -944,7 +941,6 @@ public class JailIO {
|
|||||||
public void removePrisoner(Jail j, Cell c, Prisoner p) {
|
public void removePrisoner(Jail j, Cell c, Prisoner p) {
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
try {
|
try {
|
||||||
PreparedStatement pp = con.prepareStatement("delete from `" + prefix + "prisoners` where name = ? limit 1;");
|
PreparedStatement pp = con.prepareStatement("delete from `" + prefix + "prisoners` where name = ? limit 1;");
|
||||||
@ -993,7 +989,6 @@ public class JailIO {
|
|||||||
|
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
try {
|
try {
|
||||||
PreparedStatement p = con.prepareStatement("delete from `" + prefix + "cells` where name = ? and jail = ? limit 1;");
|
PreparedStatement p = con.prepareStatement("delete from `" + prefix + "cells` where name = ? and jail = ? limit 1;");
|
||||||
@ -1034,7 +1029,6 @@ public class JailIO {
|
|||||||
|
|
||||||
switch(storage) {
|
switch(storage) {
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
for(Cell c : j.getCells()) {
|
for(Cell c : j.getCells()) {
|
||||||
removeCell(j, c);
|
removeCell(j, c);
|
||||||
|
@ -50,7 +50,7 @@ public class JailMain extends JavaPlugin {
|
|||||||
//Try to load the old stuff before we load anything, esp the storage stuff
|
//Try to load the old stuff before we load anything, esp the storage stuff
|
||||||
LegacyManager lm = new LegacyManager(this);
|
LegacyManager lm = new LegacyManager(this);
|
||||||
if(lm.doWeNeedToConvert()) {
|
if(lm.doWeNeedToConvert()) {
|
||||||
boolean converted = lm.loadOldData();
|
boolean converted = lm.convertOldData();
|
||||||
if(!converted) getLogger().severe("We was unable to convert some, or all, of the old data.");
|
if(!converted) getLogger().severe("We was unable to convert some, or all, of the old data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,21 @@ public class SimpleLocation {
|
|||||||
this.yaw = 0;
|
this.yaw = 0;
|
||||||
this.pitch = 0;
|
this.pitch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SimpleLocation with each detail provided separately.
|
||||||
|
*
|
||||||
|
* @param world as a string
|
||||||
|
* @param x coordinate as a double
|
||||||
|
* @param y coordinate as a double
|
||||||
|
* @param z coordinate as a double
|
||||||
|
*/
|
||||||
|
public SimpleLocation(String world, double x, double y, double z) {
|
||||||
|
this.world = world;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the instance from Bukkit of the world this location is in. */
|
/** Returns the instance from Bukkit of the world this location is in. */
|
||||||
public World getWorld() {
|
public World getWorld() {
|
||||||
|
@ -3,9 +3,12 @@ package com.graywolf336.jail.legacy;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.util.org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
@ -31,12 +34,10 @@ public class LegacyManager {
|
|||||||
|
|
||||||
/** Returns true/false if the old config, global.yml, exists and needs to be converted. */
|
/** Returns true/false if the old config, global.yml, exists and needs to be converted. */
|
||||||
public boolean doWeNeedToConvert() {
|
public boolean doWeNeedToConvert() {
|
||||||
File f = new File(pl.getDataFolder(), "global.yml");
|
return new File(pl.getDataFolder(), "global.yml").exists();
|
||||||
|
|
||||||
return f.exists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean loadOldData() {
|
public boolean convertOldData() {
|
||||||
File f = new File(pl.getDataFolder(), "global.yml");
|
File f = new File(pl.getDataFolder(), "global.yml");
|
||||||
|
|
||||||
if(f.exists()) {
|
if(f.exists()) {
|
||||||
@ -56,14 +57,21 @@ public class LegacyManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
pl.debug("The old config file, global.yml, was not found so not laoding anything.");
|
pl.debug("The old config file, global.yml, was not found so not loading anything.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loadOldConfig();
|
loadOldConfig();
|
||||||
|
loadOldData();
|
||||||
|
moveOldConfigs();
|
||||||
return true;
|
return true;
|
||||||
}catch (Exception e) {
|
}catch (Exception e) {
|
||||||
|
if(pl.inDebug()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
pl.debug(e.getMessage());
|
||||||
pl.getLogger().severe("Failed to load the old configuration for some reason.");
|
pl.getLogger().severe("Failed to load the old configuration for some reason.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -380,4 +388,24 @@ public class LegacyManager {
|
|||||||
pl.saveConfig();
|
pl.saveConfig();
|
||||||
pl.getLogger().info("Converted " + count + " old config value" + (count == 1 ? "" : "s") + ".");
|
pl.getLogger().info("Converted " + count + " old config value" + (count == 1 ? "" : "s") + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadOldData() throws SQLException {
|
||||||
|
OldInputOutput o = new OldInputOutput(pl, global);
|
||||||
|
|
||||||
|
o.LoadJails();
|
||||||
|
o.LoadPrisoners();
|
||||||
|
o.LoadCells();
|
||||||
|
o.freeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveOldConfigs() throws IOException {
|
||||||
|
FileUtils.moveFileToDirectory(new File(pl.getDataFolder(), "global.yml"), new File(pl.getDataFolder() + File.separator + "preJail3Data"), true);
|
||||||
|
FileUtils.moveFileToDirectory(new File(pl.getDataFolder(), "jails.yml"), new File(pl.getDataFolder() + File.separator + "preJail3Data"), true);
|
||||||
|
FileUtils.moveFileToDirectory(new File(pl.getDataFolder(), "jailLog.txt"), new File(pl.getDataFolder() + File.separator + "preJail3Data"), true);
|
||||||
|
|
||||||
|
File sqlite = new File(pl.getDataFolder(), "jail.sqlite");
|
||||||
|
if(sqlite.exists()) {
|
||||||
|
FileUtils.moveFileToDirectory(sqlite, new File(pl.getDataFolder() + File.separator + "oldData"), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
230
src/main/java/com/graywolf336/jail/legacy/OldInputOutput.java
Normal file
230
src/main/java/com/graywolf336/jail/legacy/OldInputOutput.java
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package com.graywolf336.jail.legacy;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
import com.graywolf336.jail.JailMain;
|
||||||
|
import com.graywolf336.jail.beans.Cell;
|
||||||
|
import com.graywolf336.jail.beans.Jail;
|
||||||
|
import com.graywolf336.jail.beans.Prisoner;
|
||||||
|
import com.graywolf336.jail.beans.SimpleLocation;
|
||||||
|
|
||||||
|
public class OldInputOutput {
|
||||||
|
private JailMain pl;
|
||||||
|
private Connection connection;
|
||||||
|
private YamlConfiguration global;
|
||||||
|
|
||||||
|
public OldInputOutput(JailMain plugin, YamlConfiguration global) {
|
||||||
|
this.pl = plugin;
|
||||||
|
this.global = global;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Connection getConnection() throws SQLException {
|
||||||
|
if (connection == null) connection = createConnection();
|
||||||
|
if(OldSettings.getGlobalBoolean(global, OldSetting.UseMySQL)) {
|
||||||
|
if(!connection.isValid(10)) connection = createConnection();
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Connection createConnection() {
|
||||||
|
try {
|
||||||
|
if (OldSettings.getGlobalBoolean(global, OldSetting.UseMySQL)) {
|
||||||
|
Class.forName("com.mysql.jdbc.Driver");
|
||||||
|
Connection ret = DriverManager.getConnection(OldSettings.getGlobalString(global, OldSetting.MySQLConn), OldSettings.getGlobalString(global, OldSetting.MySQLUsername), OldSettings.getGlobalString(global, OldSetting.MySQLPassword));
|
||||||
|
ret.setAutoCommit(false);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
Class.forName("org.sqlite.JDBC");
|
||||||
|
Connection ret = DriverManager.getConnection("jdbc:sqlite:" + new File(pl.getDataFolder().getPath(), "jail.sqlite").getPath());
|
||||||
|
ret.setAutoCommit(false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void freeConnection() throws SQLException {
|
||||||
|
Connection conn = getConnection();
|
||||||
|
if(conn != null) {
|
||||||
|
try {
|
||||||
|
conn.close();
|
||||||
|
conn = null;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadJails() throws SQLException {
|
||||||
|
PreparedStatement ps = getConnection().prepareStatement("SELECT * FROM jail_zones");
|
||||||
|
ResultSet set = ps.executeQuery();
|
||||||
|
while (set.next()) {
|
||||||
|
String name = set.getString("name").toLowerCase();
|
||||||
|
double X1 = set.getDouble("X1");
|
||||||
|
double Y1 = set.getDouble("Y1");
|
||||||
|
double Z1 = set.getDouble("Z1");
|
||||||
|
double X2 = set.getDouble("X2");
|
||||||
|
double Y2 = set.getDouble("Y2");
|
||||||
|
double Z2 = set.getDouble("Z2");
|
||||||
|
double teleX = set.getDouble("teleX");
|
||||||
|
double teleY = set.getDouble("teleY");
|
||||||
|
double teleZ = set.getDouble("teleZ");
|
||||||
|
double freeX = set.getDouble("freeX");
|
||||||
|
double freeY = set.getDouble("freeY");
|
||||||
|
double freeZ = set.getDouble("freeZ");
|
||||||
|
String teleWorld = set.getString("teleWorld");
|
||||||
|
String freeWorld = set.getString("freeWorld");
|
||||||
|
|
||||||
|
Jail j = new Jail(pl, name);
|
||||||
|
j.setWorld(teleWorld);
|
||||||
|
j.setMaxPoint(new Location(pl.getServer().getWorld(teleWorld), X1, Y1, Z1));
|
||||||
|
j.setMinPoint(new Location(pl.getServer().getWorld(teleWorld), X2, Y2, Z2));
|
||||||
|
j.setTeleportIn(new SimpleLocation(teleWorld, teleX, teleY, teleZ));
|
||||||
|
j.setTeleportFree(new SimpleLocation(freeWorld, freeX, freeY, freeZ));
|
||||||
|
|
||||||
|
pl.getJailManager().addJail(j, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
set.close();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadPrisoners() throws SQLException {
|
||||||
|
PreparedStatement ps = getConnection().prepareStatement("SELECT * FROM jail_prisoners");
|
||||||
|
ResultSet set = ps.executeQuery();
|
||||||
|
while (set.next()) {
|
||||||
|
Jail j = pl.getJailManager().getJail(set.getString("JailName"));
|
||||||
|
String name = set.getString("PlayerName").toLowerCase();
|
||||||
|
|
||||||
|
String transferDest = set.getString("TransferDest");
|
||||||
|
boolean transfer = false;
|
||||||
|
if(!transferDest.isEmpty()) {
|
||||||
|
j = pl.getJailManager().getJail(transferDest);
|
||||||
|
transfer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the jail exists, if not then we refuse to load it
|
||||||
|
if(j != null) {
|
||||||
|
Prisoner p = new Prisoner(name, set.getBoolean("muted"), (long) set.getInt("RemainTime"), set.getString("Jailer"), set.getString("reason"));
|
||||||
|
p.setOfflinePending(set.getBoolean("Offline"));
|
||||||
|
p.setToBeTransferred(transfer);
|
||||||
|
|
||||||
|
j.addPrisoner(p);
|
||||||
|
//String permissions = set.getString("Permissions"); TODO
|
||||||
|
}else {
|
||||||
|
pl.getLogger().warning("Failed to load the prisoner " + name + " from the old database as the Jail doesn't exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set.close();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadCells() throws SQLException {
|
||||||
|
Connection conn;
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet set = null;
|
||||||
|
|
||||||
|
conn = getConnection();
|
||||||
|
ps = conn.prepareStatement("SELECT * FROM jail_cells");
|
||||||
|
set = ps.executeQuery();
|
||||||
|
while (set.next()) {
|
||||||
|
String jailname = set.getString("JailName");
|
||||||
|
String teleport = set.getString("Teleport");
|
||||||
|
String sign = set.getString("Sign");
|
||||||
|
String chest = set.getString("Chest");
|
||||||
|
String player = set.getString("Player");
|
||||||
|
String name = set.getString("Name");
|
||||||
|
|
||||||
|
if(name.isEmpty()) {
|
||||||
|
pl.getLogger().warning("We need a cell name in Jail 3.0, refusing to load a cell due to this.");
|
||||||
|
}else if(jailname.isEmpty()) {
|
||||||
|
pl.getLogger().warning("Refusing to load a cell (" + name + ") as it does not contain a reference to a valid Jail.");
|
||||||
|
}else {
|
||||||
|
Jail j = pl.getJailManager().getJail(jailname);
|
||||||
|
|
||||||
|
if(j == null) {
|
||||||
|
pl.getLogger().warning("Refusing to load a cell (" + name + ") as it references a Jail which doesn't exist.");
|
||||||
|
}else if(j.getCell(name) != null) {
|
||||||
|
pl.getLogger().warning("Refusing to load a duplicate named cell, " + name + ", as another one exists with that same name.");
|
||||||
|
} else {
|
||||||
|
Cell c = new Cell(name);
|
||||||
|
|
||||||
|
if(!teleport.isEmpty()) {
|
||||||
|
String[] l = teleport.split(",");
|
||||||
|
c.setTeleport(new SimpleLocation(j.getWorldName(), l[0], l[1], l[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!chest.isEmpty()) {
|
||||||
|
String[] ch = chest.split(",");
|
||||||
|
c.setChestLocation(new Location(j.getWorld(), Double.valueOf(ch[0]), Double.valueOf(ch[1]), Double.valueOf(ch[2])));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!sign.isEmpty()) {
|
||||||
|
for(String s : sign.split(";")) {
|
||||||
|
pl.debug(s);
|
||||||
|
String[] si = s.split(",");
|
||||||
|
c.addSign(new SimpleLocation(j.getWorldName(), si[0], si[1], si[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load the prisoner if he is a valid prisoner
|
||||||
|
if(!player.isEmpty()) {
|
||||||
|
Prisoner p = j.getPrisoner(name);
|
||||||
|
|
||||||
|
if(p != null) {
|
||||||
|
c.setPrisoner(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j.addCell(c, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set.close();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteZone(String z) throws SQLException {
|
||||||
|
Connection conn = getConnection();
|
||||||
|
PreparedStatement ps = conn.prepareStatement("DELETE FROM jail_zones WHERE name = ?");
|
||||||
|
ps.setString(1, z);
|
||||||
|
ps.executeUpdate();
|
||||||
|
conn.commit();
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteCell(int x, int y, int z) throws SQLException {
|
||||||
|
Connection conn = getConnection();
|
||||||
|
PreparedStatement ps = conn.prepareStatement("DELETE FROM jail_cells WHERE Teleport = ?");
|
||||||
|
ps.setString(1, String.valueOf(x) + "," + String.valueOf(y) + "," + String.valueOf(z));
|
||||||
|
ps.executeUpdate();
|
||||||
|
conn.commit();
|
||||||
|
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeletePrisoner(String p) throws SQLException {
|
||||||
|
Connection conn = getConnection();
|
||||||
|
PreparedStatement ps = conn.prepareStatement("DELETE FROM jail_prisoners WHERE PlayerName = ?");
|
||||||
|
ps.setString(1, p);
|
||||||
|
ps.executeUpdate();
|
||||||
|
conn.commit();
|
||||||
|
|
||||||
|
ps.close();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user