mirror of
https://github.com/filoghost/ChestCommands.git
synced 2025-02-17 03:51:29 +01:00
Add dynamic placeholders, and refresh option.
This commit is contained in:
parent
0950262d9f
commit
db7fa8c7e8
@ -34,6 +34,7 @@ import com.gmail.filoghost.chestcommands.nms.Fallback;
|
||||
import com.gmail.filoghost.chestcommands.serializer.CommandSerializer;
|
||||
import com.gmail.filoghost.chestcommands.serializer.MenuSerializer;
|
||||
import com.gmail.filoghost.chestcommands.task.ErrorLoggerTask;
|
||||
import com.gmail.filoghost.chestcommands.task.RefreshMenusTask;
|
||||
import com.gmail.filoghost.chestcommands.util.CaseInsensitiveMap;
|
||||
import com.gmail.filoghost.chestcommands.util.ErrorLogger;
|
||||
import com.gmail.filoghost.chestcommands.util.Utils;
|
||||
@ -102,11 +103,11 @@ public class ChestCommands extends JavaPlugin {
|
||||
if (settings.use_console_colors) {
|
||||
Bukkit.getConsoleSender().sendMessage(CHAT_PREFIX + "Found a new version: " + newVersion + ChatColor.WHITE + " (yours: v" + getDescription().getVersion() + ")");
|
||||
Bukkit.getConsoleSender().sendMessage(CHAT_PREFIX + ChatColor.WHITE + "Download it on Bukkit Dev:");
|
||||
Bukkit.getConsoleSender().sendMessage(CHAT_PREFIX + ChatColor.WHITE + "dev.bukkit.org/bukkit-plugins/chest-commands");
|
||||
Bukkit.getConsoleSender().sendMessage(CHAT_PREFIX + ChatColor.WHITE + "dev.bukkit.org/bukkit-plugins/chest-commands");
|
||||
} else {
|
||||
getLogger().info("Found a new version available: " + newVersion);
|
||||
getLogger().info("Download it on Bukkit Dev:");
|
||||
getLogger().info("dev.bukkit.org/bukkit-plugins/chest-commands");
|
||||
getLogger().info("dev.bukkit.org/bukkit-plugins/chest-commands");
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -132,6 +133,8 @@ public class ChestCommands extends JavaPlugin {
|
||||
if (errorLogger.hasErrors()) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(this, new ErrorLoggerTask(errorLogger), 10L);
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new RefreshMenusTask(), 2L, 2L);
|
||||
}
|
||||
|
||||
public void load(ErrorLogger errorLogger) {
|
||||
@ -219,6 +222,8 @@ public class ChestCommands extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
iconMenu.setRefreshTicks(data.getRefreshTenths());
|
||||
|
||||
if (data.getOpenActions() != null) {
|
||||
iconMenu.setOpenActions(data.getOpenActions());
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ package com.gmail.filoghost.chestcommands.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Color;
|
||||
@ -16,6 +18,7 @@ import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
|
||||
import com.gmail.filoghost.chestcommands.internal.Variable;
|
||||
import com.gmail.filoghost.chestcommands.util.Utils;
|
||||
|
||||
public class Icon {
|
||||
@ -33,11 +36,18 @@ public class Icon {
|
||||
protected boolean closeOnClick;
|
||||
private ClickHandler clickHandler;
|
||||
|
||||
private Map<Integer, Set<Variable>> variables;
|
||||
private ItemStack cachedItem; // When there are no variables, we don't recreate the item.
|
||||
|
||||
public Icon() {
|
||||
enchantments = new HashMap<Enchantment, Integer>();
|
||||
closeOnClick = true;
|
||||
}
|
||||
|
||||
public boolean hasVariables() {
|
||||
return variables != null;
|
||||
}
|
||||
|
||||
public void setMaterial(Material material) {
|
||||
if (material == Material.AIR) material = null;
|
||||
this.material = material;
|
||||
@ -70,6 +80,28 @@ public class Icon {
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Variable variable : Variable.values()) {
|
||||
if (name.contains(variable.getText())) {
|
||||
|
||||
if (variables == null) {
|
||||
variables = new HashMap<Integer, Set<Variable>>();
|
||||
}
|
||||
|
||||
Set<Variable> nameVariables = variables.get(-1);
|
||||
|
||||
if (nameVariables == null) {
|
||||
nameVariables = new HashSet<Variable>();
|
||||
variables.put(-1, nameVariables);
|
||||
}
|
||||
|
||||
nameVariables.add(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasName() {
|
||||
@ -78,12 +110,36 @@ public class Icon {
|
||||
|
||||
public void setLore(String... lore) {
|
||||
if (lore != null) {
|
||||
this.lore = Arrays.asList(lore);
|
||||
setLore(Arrays.asList(lore));
|
||||
}
|
||||
}
|
||||
|
||||
public void setLore(List<String> lore) {
|
||||
this.lore = lore;
|
||||
|
||||
if (lore == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < lore.size(); i++) {
|
||||
for (Variable variable : Variable.values()) {
|
||||
if (lore.get(i).contains(variable.getText())) {
|
||||
|
||||
if (variables == null) {
|
||||
variables = new HashMap<Integer, Set<Variable>>();
|
||||
}
|
||||
|
||||
Set<Variable> lineVariables = variables.get(i);
|
||||
|
||||
if (lineVariables == null) {
|
||||
lineVariables = new HashSet<Variable>();
|
||||
variables.put(i, lineVariables);
|
||||
}
|
||||
|
||||
lineVariables.add(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasLore() {
|
||||
@ -150,9 +206,21 @@ public class Icon {
|
||||
return clickHandler;
|
||||
}
|
||||
|
||||
protected String calculateName() {
|
||||
protected String calculateName(Player pov) {
|
||||
if (hasName()) {
|
||||
// TODO some magic
|
||||
|
||||
String name = this.name;
|
||||
|
||||
if (pov != null && variables != null) {
|
||||
|
||||
Set<Variable> nameVariables = variables.get(-1); // Name variables have index -1.
|
||||
if (nameVariables != null) {
|
||||
for (Variable nameVariable : nameVariables) {
|
||||
name = name.replace(nameVariable.getText(), nameVariable.getReplacement(pov));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
// Add a color to display the name empty.
|
||||
return ChatColor.WHITE.toString();
|
||||
@ -164,22 +232,39 @@ public class Icon {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected List<String> calculateLore() {
|
||||
protected List<String> calculateLore(Player pov) {
|
||||
|
||||
List<String> output = null;
|
||||
|
||||
if (hasLore()) {
|
||||
|
||||
output = Utils.newArrayList();
|
||||
// TODO some magic
|
||||
for (String line : lore) {
|
||||
output.add(line);
|
||||
|
||||
if (pov != null && variables != null) {
|
||||
for (int i = 0; i < lore.size(); i++) {
|
||||
|
||||
String line = lore.get(i);
|
||||
|
||||
Set<Variable> lineVariables = variables.get(i);
|
||||
if (lineVariables != null) {
|
||||
for (Variable lineVariable : lineVariables) {
|
||||
line = line.replace(lineVariable.getText(), lineVariable.getReplacement(pov));
|
||||
}
|
||||
}
|
||||
|
||||
output.add(line);
|
||||
}
|
||||
} else {
|
||||
// Otherwise just copy the lines.
|
||||
output.addAll(lore);
|
||||
}
|
||||
}
|
||||
|
||||
if (material == null) {
|
||||
|
||||
if (output == null) output = Utils.newArrayList();
|
||||
if (output == null) {
|
||||
output = Utils.newArrayList();
|
||||
}
|
||||
|
||||
// Add an error message.
|
||||
output.add(ChatColor.RED + "(Invalid material)");
|
||||
@ -188,7 +273,12 @@ public class Icon {
|
||||
return output;
|
||||
}
|
||||
|
||||
public ItemStack createItemstack() {
|
||||
public ItemStack createItemstack(Player pov) {
|
||||
|
||||
if (variables == null && cachedItem != null) {
|
||||
// Performance.
|
||||
return cachedItem;
|
||||
}
|
||||
|
||||
// If the material is not set, display BEDROCK.
|
||||
ItemStack itemStack = (material != null) ? new ItemStack(material, amount, dataValue) : new ItemStack(Material.BEDROCK, amount);
|
||||
@ -196,8 +286,8 @@ public class Icon {
|
||||
// Apply name, lore and color.
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
|
||||
itemMeta.setDisplayName(calculateName());
|
||||
itemMeta.setLore(calculateLore());
|
||||
itemMeta.setDisplayName(calculateName(pov));
|
||||
itemMeta.setLore(calculateLore(pov));
|
||||
|
||||
if (color != null && itemMeta instanceof LeatherArmorMeta) {
|
||||
((LeatherArmorMeta) itemMeta).setColor(color);
|
||||
@ -216,6 +306,11 @@ public class Icon {
|
||||
}
|
||||
}
|
||||
|
||||
if (variables == null) {
|
||||
// If there are no variables, cache the item.
|
||||
cachedItem = itemStack;
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,11 @@ import com.gmail.filoghost.chestcommands.util.Validate;
|
||||
|
||||
/*
|
||||
* MEMO: Raw slot numbers
|
||||
*
|
||||
*
|
||||
* | 0| 1| 2| 3| 4| 5| 6| 7| 8|
|
||||
* | 9|10|11|12|13|14|15|16|17|
|
||||
* ...
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class IconMenu {
|
||||
|
||||
@ -79,7 +79,7 @@ public class IconMenu {
|
||||
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
if (icons[i] != null) {
|
||||
inventory.setItem(i, ChestCommands.getAttributeRemover().removeAttributes(icons[i].createItemstack()));
|
||||
inventory.setItem(i, ChestCommands.getAttributeRemover().removeAttributes(icons[i].createItemstack(player)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,5 +89,5 @@ public class IconMenu {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IconMenu [title=" + title + ", icons=" + Arrays.toString(icons) + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,12 @@ package com.gmail.filoghost.chestcommands.internal;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.gmail.filoghost.chestcommands.ChestCommands;
|
||||
import com.gmail.filoghost.chestcommands.Permissions;
|
||||
@ -19,6 +22,8 @@ public class ExtendedIconMenu extends IconMenu {
|
||||
private String permission;
|
||||
private List<IconCommand> openActions;
|
||||
|
||||
private int refreshTicks;
|
||||
|
||||
public ExtendedIconMenu(String title, int rows, String fileName) {
|
||||
super(title, rows);
|
||||
this.fileName = fileName;
|
||||
@ -41,42 +46,85 @@ public class ExtendedIconMenu extends IconMenu {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int getRefreshTicks() {
|
||||
return refreshTicks;
|
||||
}
|
||||
|
||||
public void setRefreshTicks(int refreshTicks) {
|
||||
this.refreshTicks = refreshTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(Player player) {
|
||||
if (openActions != null) {
|
||||
for (IconCommand openAction : openActions) {
|
||||
openAction.execute(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Inventory inventory = Bukkit.createInventory(new MenuInventoryHolder(this), icons.length, title);
|
||||
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
if (icons[i] != null) {
|
||||
|
||||
if (icons[i] instanceof ExtendedIcon) {
|
||||
ExtendedIcon extIcon = (ExtendedIcon) icons[i];
|
||||
|
||||
if (!extIcon.canViewIcon(player)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setItem(i, ChestCommands.getAttributeRemover().removeAttributes(icons[i].createItemstack()));
|
||||
try {
|
||||
if (openActions != null) {
|
||||
for (IconCommand openAction : openActions) {
|
||||
openAction.execute(player);
|
||||
}
|
||||
}
|
||||
|
||||
player.openInventory(inventory);
|
||||
Inventory inventory = Bukkit.createInventory(new MenuInventoryHolder(this), icons.length, title);
|
||||
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
if (icons[i] != null) {
|
||||
|
||||
if (icons[i] instanceof ExtendedIcon && !((ExtendedIcon) icons[i]).canViewIcon(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inventory.setItem(i, ChestCommands.getAttributeRemover().removeAttributes(icons[i].createItemstack(player)));
|
||||
}
|
||||
}
|
||||
|
||||
player.openInventory(inventory);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
player.sendMessage(ChatColor.RED + "An internal error occurred while opening the menu. The staff should check the console for errors.");
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh(Player player, Inventory inventory) {
|
||||
|
||||
try {
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
if (icons[i] != null && icons[i] instanceof ExtendedIcon) {
|
||||
|
||||
ExtendedIcon extIcon = (ExtendedIcon) icons[i];
|
||||
|
||||
if (extIcon.hasViewPermission() || extIcon.hasVariables()) {
|
||||
// Then we have to refresh it
|
||||
if (extIcon.canViewIcon(player)) {
|
||||
|
||||
if (inventory.getItem(i) == null) {
|
||||
ItemStack updatedIcon = ChestCommands.getAttributeRemover().removeAttributes(extIcon.createItemstack(player));
|
||||
inventory.setItem(i, updatedIcon);
|
||||
}
|
||||
|
||||
// Performance, only update name and lore.
|
||||
ItemStack inventoryItem = inventory.getItem(i);
|
||||
ItemMeta meta = inventoryItem.getItemMeta();
|
||||
meta.setDisplayName(extIcon.calculateName(player));
|
||||
meta.setLore(extIcon.calculateLore(player));
|
||||
inventoryItem.setItemMeta(meta);
|
||||
|
||||
} else {
|
||||
inventory.setItem(i, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
player.sendMessage(ChatColor.RED + "An internal error occurred while refreshing the menu. The staff should check the console for errors.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendNoPermissionMessage(CommandSender sender) {
|
||||
String noPermMessage = ChestCommands.getLang().no_open_permission;
|
||||
if (noPermMessage != null && !noPermMessage.isEmpty()) {
|
||||
sender.sendMessage(noPermMessage.replace("{permission}", this.permission));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ public class MenuData {
|
||||
private short boundDataValue;
|
||||
private ClickType clickType;
|
||||
private List<IconCommand> openActions;
|
||||
private int refreshTenths;
|
||||
|
||||
public MenuData(String title, int rows) {
|
||||
this.title = title;
|
||||
@ -85,4 +86,12 @@ public class MenuData {
|
||||
public void setOpenActions(List<IconCommand> openAction) {
|
||||
this.openActions = openAction;
|
||||
}
|
||||
|
||||
public int getRefreshTenths() {
|
||||
return refreshTenths;
|
||||
}
|
||||
|
||||
public void setRefreshTenths(int refreshTenths) {
|
||||
this.refreshTenths = refreshTenths;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.gmail.filoghost.chestcommands.internal.icon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@ -64,6 +66,10 @@ public class ExtendedIcon extends Icon {
|
||||
this.permissionMessage = permissionMessage;
|
||||
}
|
||||
|
||||
public boolean hasViewPermission() {
|
||||
return viewPermission != null;
|
||||
}
|
||||
|
||||
public boolean canViewIcon(Player player) {
|
||||
if (viewPermission == null) {
|
||||
return true;
|
||||
@ -121,6 +127,14 @@ public class ExtendedIcon extends Icon {
|
||||
public void setRequiredItem(RequiredItem requiredItem) {
|
||||
this.requiredItem = requiredItem;
|
||||
}
|
||||
|
||||
public String calculateName(Player pov) {
|
||||
return super.calculateName(pov);
|
||||
}
|
||||
|
||||
public List<String> calculateLore(Player pov) {
|
||||
return super.calculateLore(pov);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
|
@ -1,25 +0,0 @@
|
||||
package com.gmail.filoghost.chestcommands.internal.icon;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* An icon that will not change material, name, lore, ...
|
||||
*/
|
||||
public class StaticExtendedIcon extends ExtendedIcon {
|
||||
|
||||
private ItemStack cachedItem;
|
||||
|
||||
public StaticExtendedIcon() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createItemstack() {
|
||||
if (cachedItem == null) {
|
||||
cachedItem = super.createItemstack();
|
||||
}
|
||||
|
||||
return cachedItem;
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,6 @@ import com.gmail.filoghost.chestcommands.internal.CommandsClickHandler;
|
||||
import com.gmail.filoghost.chestcommands.internal.RequiredItem;
|
||||
import com.gmail.filoghost.chestcommands.internal.icon.ExtendedIcon;
|
||||
import com.gmail.filoghost.chestcommands.internal.icon.IconCommand;
|
||||
import com.gmail.filoghost.chestcommands.internal.icon.StaticExtendedIcon;
|
||||
import com.gmail.filoghost.chestcommands.util.ErrorLogger;
|
||||
import com.gmail.filoghost.chestcommands.util.ItemStackReader;
|
||||
import com.gmail.filoghost.chestcommands.util.Utils;
|
||||
@ -21,7 +20,7 @@ public class IconSerializer {
|
||||
|
||||
private static class Nodes {
|
||||
|
||||
public static final
|
||||
public static final
|
||||
String ID = "ID",
|
||||
DATA_VALUE = "DATA-VALUE",
|
||||
AMOUNT = "AMOUNT",
|
||||
@ -66,14 +65,14 @@ public class IconSerializer {
|
||||
|
||||
public Integer getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Icon loadIconFromSection(ConfigurationSection section, String iconName, String menuFileName, ErrorLogger errorLogger) {
|
||||
Validate.notNull(section, "ConfigurationSection cannot be null");
|
||||
|
||||
// The icon is valid even without a Material.
|
||||
ExtendedIcon icon = new StaticExtendedIcon();
|
||||
ExtendedIcon icon = new ExtendedIcon();
|
||||
|
||||
if (section.isSet(Nodes.ID)) {
|
||||
try {
|
||||
|
@ -28,6 +28,8 @@ public class MenuSerializer {
|
||||
public static final String OPEN_ITEM_LEFT_CLICK = "menu-settings.open-with-item.left-click";
|
||||
public static final String OPEN_ITEM_RIGHT_CLICK = "menu-settings.open-with-item.right-click";
|
||||
|
||||
public static final String AUTO_REFRESH = "menu-settings.auto-refresh";
|
||||
|
||||
}
|
||||
|
||||
public static ExtendedIconMenu loadMenu(PluginConfig config, String title, int rows, ErrorLogger errorLogger) {
|
||||
@ -117,6 +119,14 @@ public class MenuSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isSet(Nodes.AUTO_REFRESH)) {
|
||||
|
||||
double autoRefresh = config.getDouble(Nodes.AUTO_REFRESH);
|
||||
int tenthsToRefresh = autoRefresh <= 0.1 ? 1 : (int) (autoRefresh * 10.0);
|
||||
menuData.setRefreshTenths(tenthsToRefresh);
|
||||
|
||||
}
|
||||
|
||||
return menuData;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
package com.gmail.filoghost.chestcommands.task;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
|
||||
import com.gmail.filoghost.chestcommands.internal.ExtendedIconMenu;
|
||||
import com.gmail.filoghost.chestcommands.internal.MenuInventoryHolder;
|
||||
|
||||
public class RefreshMenusTask implements Runnable {
|
||||
|
||||
private long elapsedTenths;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
|
||||
InventoryView view = player.getOpenInventory();
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory topInventory = view.getTopInventory();
|
||||
if (topInventory.getHolder() instanceof MenuInventoryHolder) {
|
||||
MenuInventoryHolder menuHolder = (MenuInventoryHolder) topInventory.getHolder();
|
||||
|
||||
if (menuHolder.getIconMenu() instanceof ExtendedIconMenu) {
|
||||
ExtendedIconMenu extMenu = (ExtendedIconMenu) menuHolder.getIconMenu();
|
||||
|
||||
if (extMenu.getRefreshTicks() > 0) {
|
||||
if (elapsedTenths % extMenu.getRefreshTicks() == 0) {
|
||||
extMenu.refresh(player, topInventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elapsedTenths++;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user