mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-02-23 15:51:20 +01:00
Towny Data
This commit is contained in:
parent
c16d85b9e5
commit
97bdfe1ec8
@ -17,7 +17,7 @@ import java.util.*;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* //TODO Class Javadoc Comment
|
* Creates Faction accordion for Factions tab.
|
||||||
*
|
*
|
||||||
* @author Rsl1122
|
* @author Rsl1122
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +9,6 @@ import main.java.com.djrapitops.plan.data.additional.AnalysisContainer;
|
|||||||
import main.java.com.djrapitops.plan.data.additional.ContainerSize;
|
import main.java.com.djrapitops.plan.data.additional.ContainerSize;
|
||||||
import main.java.com.djrapitops.plan.data.additional.InspectContainer;
|
import main.java.com.djrapitops.plan.data.additional.InspectContainer;
|
||||||
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
||||||
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -43,12 +42,14 @@ public class SuperbVoteData extends PluginData {
|
|||||||
@Override
|
@Override
|
||||||
public AnalysisContainer getServerData(Collection<UUID> uuids, AnalysisContainer analysisContainer) throws Exception {
|
public AnalysisContainer getServerData(Collection<UUID> uuids, AnalysisContainer analysisContainer) throws Exception {
|
||||||
Map<UUID, Integer> votes = new HashMap<>();
|
Map<UUID, Integer> votes = new HashMap<>();
|
||||||
|
long total = 0;
|
||||||
for (UUID uuid : uuids) {
|
for (UUID uuid : uuids) {
|
||||||
votes.put(uuid, store.getVotes(uuid));
|
int votesCount = store.getVotes(uuid);
|
||||||
|
votes.put(uuid, votesCount);
|
||||||
|
total += votesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
long totalVotes = MathUtils.sumLong(votes.values().stream().map(i -> i));
|
analysisContainer.addValue(getWithIcon("Total Votes", "check", "teal"), total);
|
||||||
analysisContainer.addValue(getWithIcon("Total Votes", "check", "teal"), totalVotes);
|
|
||||||
|
|
||||||
analysisContainer.addPlayerTableValues(getWithIcon("Votes", "check"), votes);
|
analysisContainer.addPlayerTableValues(getWithIcon("Votes", "check"), votes);
|
||||||
|
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Licence is provided in the jar as license.yml also here:
|
||||||
|
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
||||||
|
*/
|
||||||
|
package com.djrapitops.pluginbridge.plan.towny;
|
||||||
|
|
||||||
|
import com.palmergames.bukkit.towny.exceptions.TownyException;
|
||||||
|
import com.palmergames.bukkit.towny.object.Coord;
|
||||||
|
import com.palmergames.bukkit.towny.object.Resident;
|
||||||
|
import com.palmergames.bukkit.towny.object.Town;
|
||||||
|
import main.java.com.djrapitops.plan.data.PlayerProfile;
|
||||||
|
import main.java.com.djrapitops.plan.data.ServerProfile;
|
||||||
|
import main.java.com.djrapitops.plan.data.Session;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.analysis.Analysis;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.html.HtmlStructure;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Town Accordion for Towny tab.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class TownAccordionCreator {
|
||||||
|
|
||||||
|
public static String createAccordion(List<Town> towns) {
|
||||||
|
StringBuilder html = new StringBuilder("<div class=\"panel-group scrollbar\" id=\"towny_accordion\" role=\"tablist\" aria-multiselectable=\"true\">");
|
||||||
|
|
||||||
|
ServerProfile serverProfile = Analysis.getServerProfile();
|
||||||
|
List<PlayerProfile> players = serverProfile != null ? serverProfile.getPlayers() : new ArrayList<>();
|
||||||
|
|
||||||
|
for (Town town : towns) {
|
||||||
|
String townName = town.getName();
|
||||||
|
String mayorName = town.getMayor().getName();
|
||||||
|
|
||||||
|
String coordinates = "";
|
||||||
|
try {
|
||||||
|
Coord homeBlock = town.getHomeBlock().getCoord();
|
||||||
|
coordinates = "x: " + homeBlock.getX() + " z: " + homeBlock.getZ();
|
||||||
|
} catch (TownyException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Resident> residents = town.getResidents();
|
||||||
|
int residentsNum = residents.size();
|
||||||
|
String landCount = town.getPurchasedBlocks() + " / " + town.getTotalBlocks();
|
||||||
|
|
||||||
|
Set<UUID> members = new HashSet<>();
|
||||||
|
for (Resident resident : residents) {
|
||||||
|
members.add(UUIDUtility.getUUIDOf(resident.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PlayerProfile> memberProfiles = players.stream().filter(p -> members.contains(p.getUniqueId())).collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<Session> sessions = memberProfiles.stream()
|
||||||
|
.map(PlayerProfile::getSessions)
|
||||||
|
.map(Map::values)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
long playerKills = ServerProfile.getPlayerKills(sessions).size();
|
||||||
|
long mobKills = ServerProfile.getMobKillCount(sessions);
|
||||||
|
long deaths = ServerProfile.getDeathCount(sessions);
|
||||||
|
|
||||||
|
String separated = HtmlStructure.separateWithDots(String.valueOf(residentsNum), mayorName);
|
||||||
|
|
||||||
|
String htmlID = "town_" + townName.replace(" ", "-");
|
||||||
|
|
||||||
|
// Accordion panel header
|
||||||
|
html.append("<div class=\"panel panel-col-brown\">")
|
||||||
|
.append("<div class=\"panel-heading\" role=\"tab\" id=\"heading_").append(htmlID).append("\">")
|
||||||
|
.append("<h4 class=\"panel-title\">")
|
||||||
|
.append("<a class=\"collapsed\" role=\"button\" data-toggle=\"collapse\" data-parent=\"#session_accordion\" ")
|
||||||
|
.append("href=\"#session_").append(htmlID).append("\" aria-expanded=\"false\" ")
|
||||||
|
.append("aria-controls=\"session_").append(htmlID).append("\">")
|
||||||
|
.append(townName).append("<span class=\"pull-right\">").append(separated).append("</span>") // Title (header)
|
||||||
|
.append("</a></h4>") // Closes collapsed, panel title
|
||||||
|
.append("</div>"); // Closes panel heading
|
||||||
|
|
||||||
|
// Content
|
||||||
|
html.append("<div id=\"session_").append(htmlID).append("\" class=\"panel-collapse collapse\" role=\"tabpanel\"")
|
||||||
|
.append(" aria-labelledby=\"heading_").append(htmlID).append("\">")
|
||||||
|
.append("<div class=\"panel-body\"><div class=\"row clearfix\">")
|
||||||
|
.append("<div class=\"col-xs-12 col-sm-6 col-md-6 col-lg-6\">") // Left col-6
|
||||||
|
// Sessions
|
||||||
|
.append("<p><i class=\"col-brown fa fa-user\"></i> Mayor <span class=\"pull-right\">").append(mayorName).append("</span></p>")
|
||||||
|
// Playtime
|
||||||
|
.append("<p><i class=\"col-brown fa fa-users\"></i> Residents<span class=\"pull-right\"><b>").append(residentsNum).append("</b></span></p>")
|
||||||
|
.append("<p><i class=\"col-brown fa fa-map\"></i> Town Blocks<span class=\"pull-right\"><b>").append(landCount).append("</b></span></p>")
|
||||||
|
.append("<p><i class=\"col-brown fa fa-map-pin\"></i> Members<span class=\"pull-right\"><b>").append(coordinates).append("</b></span></p>")
|
||||||
|
.append("</div>") // Closes Left col-6
|
||||||
|
.append("<div class=\"col-xs-12 col-sm-6 col-md-6 col-lg-6\">") // Right col-6
|
||||||
|
// Player Kills
|
||||||
|
.append("<p><i class=\"col-red fa fa-crosshairs\"></i> Player Kills<span class=\"pull-right\"><b>").append(playerKills).append("</b></span></p>")
|
||||||
|
.append("<p><i class=\"col-green fa fa-crosshairs\"></i> Mob Kills<span class=\"pull-right\"><b>").append(mobKills).append("</b></span></p>")
|
||||||
|
// Deaths
|
||||||
|
.append("<p><i class=\"col-red fa fa-frown-o\"></i> Deaths<span class=\"pull-right\"><b>").append(deaths).append("</b></span></p>")
|
||||||
|
.append("</div>") // Right col-6
|
||||||
|
.append("</div>") // Closes row clearfix
|
||||||
|
.append("</div>") // Closes panel-body
|
||||||
|
.append("</div>") // Closes panel-collapse
|
||||||
|
.append("</div>"); // Closes panel
|
||||||
|
}
|
||||||
|
return html.append("</div>").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Licence is provided in the jar as license.yml also here:
|
||||||
|
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
||||||
|
*/
|
||||||
|
package com.djrapitops.pluginbridge.plan.towny;
|
||||||
|
|
||||||
|
import com.palmergames.bukkit.towny.exceptions.TownyException;
|
||||||
|
import com.palmergames.bukkit.towny.object.Coord;
|
||||||
|
import com.palmergames.bukkit.towny.object.Resident;
|
||||||
|
import com.palmergames.bukkit.towny.object.Town;
|
||||||
|
import com.palmergames.bukkit.towny.object.TownyUniverse;
|
||||||
|
import main.java.com.djrapitops.plan.Plan;
|
||||||
|
import main.java.com.djrapitops.plan.Settings;
|
||||||
|
import main.java.com.djrapitops.plan.data.additional.AnalysisContainer;
|
||||||
|
import main.java.com.djrapitops.plan.data.additional.ContainerSize;
|
||||||
|
import main.java.com.djrapitops.plan.data.additional.InspectContainer;
|
||||||
|
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.html.Html;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.uuid.UUIDUtility;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PluginData for Towny plugin.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class TownyData extends PluginData {
|
||||||
|
|
||||||
|
|
||||||
|
public TownyData() {
|
||||||
|
super(ContainerSize.TAB, "Towny");
|
||||||
|
super.setPluginIcon("bank");
|
||||||
|
super.setIconColor("brown");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) throws Exception {
|
||||||
|
String playerName = Plan.getInstance().getDataCache().getName(uuid);
|
||||||
|
|
||||||
|
Resident resident = TownyUniverse.getDataSource().getResident(playerName);
|
||||||
|
|
||||||
|
if (resident.hasTown()) {
|
||||||
|
Town town = resident.getTown();
|
||||||
|
String townName = town.getName();
|
||||||
|
String mayorName = town.getMayor().getName();
|
||||||
|
String townMayor = Html.LINK.parse(Plan.getPlanAPI().getPlayerInspectPageLink(mayorName), mayorName);
|
||||||
|
|
||||||
|
inspectContainer.addValue(getWithIcon("Town", "bank", "brown"), townName);
|
||||||
|
inspectContainer.addValue(getWithIcon("Mayor", "user", "brown"), townMayor);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Coord homeBlock = town.getHomeBlock().getCoord();
|
||||||
|
String coordinates = "x: " + homeBlock.getX() + " z: " + homeBlock.getZ();
|
||||||
|
inspectContainer.addValue(getWithIcon("Coordinates", "map-pin", "red"), coordinates);
|
||||||
|
} catch (TownyException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int residents = town.getResidents().size();
|
||||||
|
inspectContainer.addValue(getWithIcon("Residents", "users", "brown"), residents);
|
||||||
|
} else {
|
||||||
|
inspectContainer.addValue(getWithIcon("Town", "bank", "brown"), "No Town");
|
||||||
|
}
|
||||||
|
|
||||||
|
return inspectContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnalysisContainer getServerData(Collection<UUID> collection, AnalysisContainer analysisContainer) throws Exception {
|
||||||
|
List<Town> towns = getTopTowns();
|
||||||
|
|
||||||
|
Map<UUID, String> userTowns = new HashMap<>();
|
||||||
|
for (Town town : towns) {
|
||||||
|
String townName = town.getName();
|
||||||
|
town.getResidents().stream()
|
||||||
|
.map(Resident::getName)
|
||||||
|
.map(UUIDUtility::getUUIDOf)
|
||||||
|
.forEach(uuid -> userTowns.put(uuid, townName));
|
||||||
|
}
|
||||||
|
analysisContainer.addPlayerTableValues(getWithIcon("Town", "bank"), userTowns);
|
||||||
|
|
||||||
|
analysisContainer.addHtml("townAccordion", TownAccordionCreator.createAccordion(towns));
|
||||||
|
|
||||||
|
return analysisContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Town> getTopTowns() {
|
||||||
|
List<Town> topTowns = TownyUniverse.getDataSource().getTowns();
|
||||||
|
topTowns.sort(new TownComparator());
|
||||||
|
List<String> hide = Settings.HIDE_TOWNS.getStringList();
|
||||||
|
return topTowns.stream()
|
||||||
|
.filter(town -> !hide.contains(town.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package com.djrapitops.pluginbridge.plan.towny;
|
package com.djrapitops.pluginbridge.plan.towny;
|
||||||
|
|
||||||
import main.java.com.djrapitops.plan.data.additional.HookHandler;
|
|
||||||
import com.djrapitops.pluginbridge.plan.Hook;
|
import com.djrapitops.pluginbridge.plan.Hook;
|
||||||
import main.java.com.djrapitops.plan.api.API;
|
import main.java.com.djrapitops.plan.api.API;
|
||||||
|
import main.java.com.djrapitops.plan.data.additional.HookHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Class responsible for hooking to Towny and registering 2 data sources.
|
* A Class responsible for hooking to Towny and registering 2 data sources.
|
||||||
@ -27,8 +27,7 @@ public class TownyHook extends Hook {
|
|||||||
|
|
||||||
public void hook() throws NoClassDefFoundError {
|
public void hook() throws NoClassDefFoundError {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
addPluginDataSource(new TownyTable());
|
addPluginDataSource(new TownyData());
|
||||||
addPluginDataSource(new TownyTown());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
package com.djrapitops.pluginbridge.plan.towny;
|
|
||||||
|
|
||||||
import com.massivecraft.factions.entity.MPlayer;
|
|
||||||
import com.palmergames.bukkit.towny.object.Resident;
|
|
||||||
import com.palmergames.bukkit.towny.object.Town;
|
|
||||||
import com.palmergames.bukkit.towny.object.TownyUniverse;
|
|
||||||
import main.java.com.djrapitops.plan.Plan;
|
|
||||||
import main.java.com.djrapitops.plan.Settings;
|
|
||||||
import main.java.com.djrapitops.plan.data.additional.AnalysisType;
|
|
||||||
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
|
||||||
import main.java.com.djrapitops.plan.utilities.html.Html;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PluginData class for Towny-plugin.
|
|
||||||
*
|
|
||||||
* Registered to the plugin by TownyHook
|
|
||||||
*
|
|
||||||
* @author Rsl1122
|
|
||||||
* @since 3.1.0
|
|
||||||
* @see TownyHook
|
|
||||||
*/
|
|
||||||
public class TownyTable extends PluginData {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Constructor, sets the parameters of the PluginData object.
|
|
||||||
*
|
|
||||||
* Uses Html to easily parse Html for the table.
|
|
||||||
*
|
|
||||||
* @see TownyHook
|
|
||||||
* @see Html
|
|
||||||
*/
|
|
||||||
public TownyTable() {
|
|
||||||
super("Towny", "towns_table", AnalysisType.HTML);
|
|
||||||
super.setPrefix(Html.TABLE_TOWNS_START.parse());
|
|
||||||
super.setSuffix(Html.TABLE_END.parse());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHtmlReplaceValue(String modifierPrefix, UUID uuid) {
|
|
||||||
StringBuilder html = new StringBuilder();
|
|
||||||
List<Town> towns = getTopTowns();
|
|
||||||
if (towns.isEmpty()) {
|
|
||||||
html.append(Html.TABLELINE_4.parse("No Towns", "", "", ""));
|
|
||||||
} else {
|
|
||||||
for (Town t : towns) {
|
|
||||||
if (t == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String name = t.getName();
|
|
||||||
Resident mayorR = t.getMayor();
|
|
||||||
String mayor;
|
|
||||||
if (mayorR != null) {
|
|
||||||
mayor = mayorR.getName();
|
|
||||||
} else {
|
|
||||||
mayor = "None";
|
|
||||||
}
|
|
||||||
int residents = t.getNumResidents();
|
|
||||||
int land = t.getPurchasedBlocks();
|
|
||||||
String leaderPage = Html.LINK.parse(Plan.getPlanAPI().getPlayerInspectPageLink(mayor), mayor);
|
|
||||||
html.append(Html.TABLELINE_4.parse(name, residents, land, leaderPage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parseContainer(modifierPrefix, html.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to get the list of Towns and filter out unnecessary ones.
|
|
||||||
*
|
|
||||||
* @return List of Towns sorted by amount of residents.
|
|
||||||
*/
|
|
||||||
public List<Town> getTopTowns() {
|
|
||||||
List<Town> topTowns = TownyUniverse.getDataSource().getTowns();
|
|
||||||
topTowns.sort(new TownComparator());
|
|
||||||
List<String> hide = Settings.HIDE_TOWNS.getStringList();
|
|
||||||
return topTowns.stream()
|
|
||||||
.filter(town -> !hide.contains(town.getName()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Serializable getValue(UUID uuid) {
|
|
||||||
MPlayer mPlayer = MPlayer.get(uuid);
|
|
||||||
return mPlayer.getPower();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package com.djrapitops.pluginbridge.plan.towny;
|
|
||||||
|
|
||||||
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
|
|
||||||
import com.palmergames.bukkit.towny.object.Resident;
|
|
||||||
import com.palmergames.bukkit.towny.object.TownyUniverse;
|
|
||||||
import main.java.com.djrapitops.plan.data.additional.PluginData;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PluginData class for Towny-plugin.
|
|
||||||
* <p>
|
|
||||||
* Registered to the plugin by TownyHook
|
|
||||||
* <p>
|
|
||||||
* Gives Town name as String.
|
|
||||||
*
|
|
||||||
* @author Rsl1122
|
|
||||||
* @see TownyHook
|
|
||||||
* @since 3.1.0
|
|
||||||
*/
|
|
||||||
public class TownyTown extends PluginData {
|
|
||||||
|
|
||||||
private final String notInTown = "Not in a Town";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class Constructor, sets the parameters of the PluginData object.
|
|
||||||
*/
|
|
||||||
public TownyTown() {
|
|
||||||
super("Towny", "town");
|
|
||||||
super.setAnalysisOnly(false);
|
|
||||||
super.setIcon("bank");
|
|
||||||
super.setPrefix("Town: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHtmlReplaceValue(String modifierPrefix, UUID uuid) {
|
|
||||||
String name = getNameOf(uuid);
|
|
||||||
try {
|
|
||||||
Resident res = TownyUniverse.getDataSource().getResident(name);
|
|
||||||
String town;
|
|
||||||
if (res.hasTown()) {
|
|
||||||
town = res.getTown().getName();
|
|
||||||
} else {
|
|
||||||
town = notInTown;
|
|
||||||
}
|
|
||||||
return parseContainer("", town);
|
|
||||||
} catch (NotRegisteredException ex) {
|
|
||||||
return parseContainer(modifierPrefix, notInTown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Serializable getValue(UUID uuid) {
|
|
||||||
String name = getNameOf(uuid);
|
|
||||||
try {
|
|
||||||
Resident res = TownyUniverse.getDataSource().getResident(name);
|
|
||||||
String town;
|
|
||||||
if (res.hasTown()) {
|
|
||||||
town = res.getTown().getName();
|
|
||||||
} else {
|
|
||||||
town = notInTown;
|
|
||||||
}
|
|
||||||
return town;
|
|
||||||
} catch (NotRegisteredException ex) {
|
|
||||||
return ex + "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user