mirror of
https://github.com/amalthea-mc/ShopChest.git
synced 2024-11-08 19:51:05 +00:00
Add product preview in shop info
This removes shop info messages for potion effect, enchantments, music disc title and book generation.
This commit is contained in:
parent
8dd2a12375
commit
1a0920f239
@ -2046,16 +2046,10 @@ public class LanguageUtils {
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_VENDOR, langConfig.getString("message.shopInfo.vendor", "&6Vendor: &e%VENDOR%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_PRODUCT, langConfig.getString("message.shopInfo.product", "&6Product: &e%AMOUNT% x %ITEMNAME%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_STOCK, langConfig.getString("message.shopInfo.stock", "&6In Stock: &e%STOCK%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_ENCHANTMENTS, langConfig.getString("message.shopInfo.enchantments", "&6Enchantments: &e%ENCHANTMENT%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_POTION_EFFECT, langConfig.getString("message.shopInfo.potion-effect", "&6Potion Effect: &e%POTION-EFFECT% %EXTENDED%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_MUSIC_TITLE, langConfig.getString("message.shopInfo.music-disc-title", "&6Music Disc Title: &e%MUSIC-TITLE%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_BOOK_GENERATION, langConfig.getString("message.shopInfo.book-generation", "&6Generation: &e%GENERATION%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_NONE, langConfig.getString("message.shopInfo.none", "&7None")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_PRICE, langConfig.getString("message.shopInfo.price", "&6Price: Buy: &e%BUY-PRICE%&6 Sell: &e%SELL-PRICE%")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_DISABLED, langConfig.getString("message.shopInfo.disabled", "&7Disabled")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_NORMAL, langConfig.getString("message.shopInfo.is-normal", "&6Type: &eNormal")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_ADMIN, langConfig.getString("message.shopInfo.is-admin", "&6Type: &eAdmin")));
|
||||
messages.add(new LocalizedMessage(Message.SHOP_INFO_EXTENDED, langConfig.getString("message.shopInfo.extended", "(Extended)")));
|
||||
messages.add(new LocalizedMessage(Message.BUY_SELL_DISABLED, langConfig.getString("message.buy-and-sell-disabled", "&cYou can't create a shop with buying and selling disabled.")));
|
||||
messages.add(new LocalizedMessage(Message.BUY_SUCCESS, langConfig.getString("message.buy-success", "&aYou bought &6%AMOUNT% x %ITEMNAME%&a for &6%BUY-PRICE%&a from &6%VENDOR%&a.")));
|
||||
messages.add(new LocalizedMessage(Message.BUY_SUCCESS_ADMIN, langConfig.getString("message.buy-success-admin", "&aYou bought &6%AMOUNT% x %ITEMNAME%&a for &6%BUY-PRICE%&a.")));
|
||||
|
@ -13,16 +13,10 @@ public enum Message {
|
||||
SHOP_INFO_VENDOR,
|
||||
SHOP_INFO_PRODUCT,
|
||||
SHOP_INFO_STOCK,
|
||||
SHOP_INFO_ENCHANTMENTS,
|
||||
SHOP_INFO_POTION_EFFECT,
|
||||
SHOP_INFO_MUSIC_TITLE,
|
||||
SHOP_INFO_BOOK_GENERATION,
|
||||
SHOP_INFO_NONE,
|
||||
SHOP_INFO_PRICE,
|
||||
SHOP_INFO_DISABLED,
|
||||
SHOP_INFO_NORMAL,
|
||||
SHOP_INFO_ADMIN,
|
||||
SHOP_INFO_EXTENDED,
|
||||
BUY_SELL_DISABLED,
|
||||
BUY_SUCCESS,
|
||||
BUY_SUCCESS_ADMIN,
|
||||
|
@ -1,5 +1,6 @@
|
||||
package de.epiceric.shopchest.listeners;
|
||||
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.intellectualcrafters.plot.flag.Flag;
|
||||
import com.intellectualcrafters.plot.object.Plot;
|
||||
import com.palmergames.bukkit.towny.object.Resident;
|
||||
@ -26,11 +27,11 @@ import de.epiceric.shopchest.language.LanguageUtils;
|
||||
import de.epiceric.shopchest.language.Message;
|
||||
import de.epiceric.shopchest.language.Replacement;
|
||||
import de.epiceric.shopchest.nms.Hologram;
|
||||
import de.epiceric.shopchest.nms.JsonBuilder;
|
||||
import de.epiceric.shopchest.shop.Shop;
|
||||
import de.epiceric.shopchest.shop.Shop.ShopType;
|
||||
import de.epiceric.shopchest.sql.Database;
|
||||
import de.epiceric.shopchest.utils.ClickType;
|
||||
import de.epiceric.shopchest.utils.ItemUtils;
|
||||
import de.epiceric.shopchest.utils.Permissions;
|
||||
import de.epiceric.shopchest.utils.ShopUtils;
|
||||
import de.epiceric.shopchest.utils.Utils;
|
||||
@ -38,6 +39,7 @@ import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import me.ryanhamshire.GriefPrevention.Claim;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.economy.EconomyResponse;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
@ -46,7 +48,6 @@ import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Chest;
|
||||
import org.bukkit.block.DoubleChest;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -73,9 +74,14 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ShopInteractListener implements Listener {
|
||||
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile(".*([§]([a-fA-F0-9]))");
|
||||
private static final Pattern FORMAT_CODE_PATTERN = Pattern.compile(".*([§]([l-oL-OkK]))");
|
||||
|
||||
private ShopChest plugin;
|
||||
private Economy econ;
|
||||
private Database database;
|
||||
@ -826,9 +832,7 @@ public class ShopInteractListener implements Listener {
|
||||
}
|
||||
|
||||
Chest c = (Chest) shop.getLocation().getBlock().getState();
|
||||
|
||||
int amount = Utils.getAmount(c.getInventory(), shop.getProduct());
|
||||
Material type = shop.getProduct().getType();
|
||||
|
||||
String vendorName = (shop.getVendor().getName() == null ?
|
||||
shop.getVendor().getUniqueId().toString() : shop.getVendor().getName());
|
||||
@ -836,14 +840,8 @@ public class ShopInteractListener implements Listener {
|
||||
String vendorString = LanguageUtils.getMessage(Message.SHOP_INFO_VENDOR,
|
||||
new Replacement(Placeholder.VENDOR, vendorName));
|
||||
|
||||
String productString = LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT,
|
||||
new Replacement(Placeholder.AMOUNT, String.valueOf(shop.getProduct().getAmount())),
|
||||
new Replacement(Placeholder.ITEM_NAME, LanguageUtils.getItemName(shop.getProduct())));
|
||||
|
||||
String enchantmentString = "";
|
||||
String potionEffectString = "";
|
||||
String bookGenerationString = "";
|
||||
String musicDiscTitleString = "";
|
||||
// Make JSON message with item preview
|
||||
JsonBuilder jb = getProductJson(shop.getProduct());
|
||||
|
||||
String disabled = LanguageUtils.getMessage(Message.SHOP_INFO_DISABLED);
|
||||
|
||||
@ -857,49 +855,93 @@ public class ShopInteractListener implements Listener {
|
||||
String stock = LanguageUtils.getMessage(Message.SHOP_INFO_STOCK,
|
||||
new Replacement(Placeholder.STOCK, String.valueOf(amount)));
|
||||
|
||||
String potionEffectName = LanguageUtils.getPotionEffectName(shop.getProduct());
|
||||
|
||||
if (potionEffectName.length() > 0) {
|
||||
boolean potionExtended = ItemUtils.isExtendedPotion(shop.getProduct());
|
||||
|
||||
String extended = potionExtended ? LanguageUtils.getMessage(Message.SHOP_INFO_EXTENDED) : "";
|
||||
potionEffectString = LanguageUtils.getMessage(Message.SHOP_INFO_POTION_EFFECT,
|
||||
new Replacement(Placeholder.POTION_EFFECT, potionEffectName),
|
||||
new Replacement(Placeholder.EXTENDED, extended));
|
||||
}
|
||||
|
||||
if (type == Material.WRITTEN_BOOK) {
|
||||
bookGenerationString = LanguageUtils.getMessage(Message.SHOP_INFO_BOOK_GENERATION,
|
||||
new Replacement(Placeholder.GENERATION, LanguageUtils.getBookGenerationName(shop.getProduct())));
|
||||
}
|
||||
|
||||
String musicDiscName = LanguageUtils.getMusicDiscName(type);
|
||||
if (musicDiscName.length() > 0) {
|
||||
musicDiscTitleString = LanguageUtils.getMessage(Message.SHOP_INFO_MUSIC_TITLE,
|
||||
new Replacement(Placeholder.MUSIC_TITLE, musicDiscName));
|
||||
}
|
||||
|
||||
Map<Enchantment, Integer> enchantmentMap = ItemUtils.getEnchantments(shop.getProduct());
|
||||
String enchantmentList = LanguageUtils.getEnchantmentString(enchantmentMap);
|
||||
|
||||
if (enchantmentList.length() > 0) {
|
||||
enchantmentString = LanguageUtils.getMessage(Message.SHOP_INFO_ENCHANTMENTS,
|
||||
new Replacement(Placeholder.ENCHANTMENT, enchantmentList));
|
||||
}
|
||||
|
||||
executor.sendMessage(" ");
|
||||
if (shop.getShopType() != ShopType.ADMIN) executor.sendMessage(vendorString);
|
||||
executor.sendMessage(productString);
|
||||
jb.sendJson(executor);
|
||||
if (shop.getShopType() != ShopType.ADMIN) executor.sendMessage(stock);
|
||||
if (enchantmentString.length() > 0) executor.sendMessage(enchantmentString);
|
||||
if (potionEffectString.length() > 0) executor.sendMessage(potionEffectString);
|
||||
if (musicDiscTitleString.length() > 0) executor.sendMessage(musicDiscTitleString);
|
||||
if (bookGenerationString.length() > 0) executor.sendMessage(bookGenerationString);
|
||||
executor.sendMessage(priceString);
|
||||
executor.sendMessage(shopType);
|
||||
executor.sendMessage(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link JsonBuilder} containing the shop info message for the product
|
||||
* in which you can hover the item name to get a preview.
|
||||
* @param product The product of the shop
|
||||
* @return A {@link JsonBuilder} that can send the message via {@link JsonBuilder#sendJson(Player)}
|
||||
*/
|
||||
private JsonBuilder getProductJson(ItemStack product) {
|
||||
// Add spaces at start and end, so there will always be a part before and after
|
||||
// the item name after splitting at Placeholder.ITEM_NAME
|
||||
String productString = " " + LanguageUtils.getMessage(Message.SHOP_INFO_PRODUCT,
|
||||
new Replacement(Placeholder.AMOUNT, String.valueOf(product.getAmount()))) + " ";
|
||||
|
||||
String[] parts = productString.split(Placeholder.ITEM_NAME.toString());
|
||||
String productName = LanguageUtils.getItemName(product);
|
||||
String jsonItem = "";
|
||||
JsonBuilder jb = new JsonBuilder(plugin);
|
||||
JsonBuilder.PartArray rootArray = new JsonBuilder.PartArray();
|
||||
|
||||
try {
|
||||
Class<?> craftItemStackClass = Utils.getCraftClass("inventory.CraftItemStack");
|
||||
Object nmsStack = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class).invoke(null, product);
|
||||
Class<?> nbtTagCompoundClass = Utils.getNMSClass("NBTTagCompound");
|
||||
Object nbtTagCompound = nbtTagCompoundClass.getConstructor().newInstance();
|
||||
nmsStack.getClass().getMethod("save", nbtTagCompoundClass).invoke(nmsStack, nbtTagCompound);
|
||||
jsonItem = new JsonPrimitive(nbtTagCompound.toString()).toString();
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Failed to create JSON from item. Product preview will not be available.");
|
||||
plugin.debug("Failed to create JSON from item:");
|
||||
plugin.debug(e);
|
||||
jb.setRootPart(new JsonBuilder.Part(productString.replace(Placeholder.ITEM_NAME.toString(), productName)));
|
||||
return jb;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
String part = parts[i];
|
||||
|
||||
// Remove spaces at start and end that were added before
|
||||
if (i == 0 && part.startsWith(" ")) {
|
||||
part = part.substring(1);
|
||||
} else if (i == parts.length - 1 && part.endsWith(" ")) {
|
||||
part = part.substring(0, part.length() - 1);
|
||||
}
|
||||
|
||||
String formatPrefix = "";
|
||||
|
||||
// A color code resets all format codes, so only format codes
|
||||
// after the last color code have to be found.
|
||||
int lastColorGroupEndIndex = 0;
|
||||
|
||||
Matcher colorMatcher = COLOR_CODE_PATTERN.matcher(part);
|
||||
if (colorMatcher.find()) {
|
||||
formatPrefix = colorMatcher.group(1);
|
||||
lastColorGroupEndIndex = colorMatcher.end();
|
||||
}
|
||||
|
||||
Matcher formatMatcher = FORMAT_CODE_PATTERN.matcher(part);
|
||||
while (formatMatcher.find(lastColorGroupEndIndex)) {
|
||||
formatPrefix += formatMatcher.group(1);
|
||||
}
|
||||
|
||||
rootArray.addPart(new JsonBuilder.Part(part));
|
||||
|
||||
if (i < parts.length - 1) {
|
||||
JsonBuilder.PartMap hoverEvent = new JsonBuilder.PartMap();
|
||||
hoverEvent.setValue("action", new JsonBuilder.Part("show_item"));
|
||||
hoverEvent.setValue("value", new JsonBuilder.Part(jsonItem, false));
|
||||
|
||||
JsonBuilder.PartMap itemNameMap = JsonBuilder.parse(formatPrefix + productName).toMap();
|
||||
itemNameMap.setValue("hoverEvent", hoverEvent);
|
||||
|
||||
rootArray.addPart(itemNameMap);
|
||||
}
|
||||
}
|
||||
|
||||
jb.setRootPart(rootArray);
|
||||
return jb;
|
||||
}
|
||||
|
||||
/**
|
||||
* A player buys from a shop
|
||||
* @param executor Player, who executed the command and will buy the product
|
||||
|
@ -10,16 +10,10 @@ message.shop-create-not-enough-money=&cNicht genug Geld. Du brauchst &6%CREATION
|
||||
message.shopInfo.vendor=&6Verkäufer: &e%VENDOR%
|
||||
message.shopInfo.product=&6Produkt: &e%AMOUNT% x %ITEMNAME%
|
||||
message.shopInfo.stock=&6Auf Lager: &e%STOCK%
|
||||
message.shopInfo.enchantments=&6Verzauberungen: &e%ENCHANTMENT%
|
||||
message.shopInfo.potion-effect=&6Trank-Effekte: &e%POTION-EFFECT% %EXTENDED%
|
||||
message.shopInfo.music-disc-title=&6Schallplattentitel: &e%MUSIC-TITLE%
|
||||
message.shopInfo.book-generation=&6Generation: &e%GENERATION%
|
||||
message.shopInfo.none=&7Keine
|
||||
message.shopInfo.price=&6Preis: Kauf: &e%BUY-PRICE%&6 Verkauf: &e%SELL-PRICE%
|
||||
message.shopInfo.disabled=&7Deaktiviert
|
||||
message.shopInfo.is-normal=&6Typ: &eNormal
|
||||
message.shopInfo.is-admin=&6Typ: &eAdmin
|
||||
message.shopInfo.extended=(Verlängert)
|
||||
message.buy-and-sell-disabled=&cDu kannst keinen Shop ohne Kauf- und Verkaufspreis erstellen.
|
||||
message.buy-success=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE%&a von &6%VENDOR% &agekauft.
|
||||
message.buy-success-admin=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE% &agekauft.
|
||||
|
@ -10,16 +10,10 @@ message.shop-create-not-enough-money=&cNicht genug Geld. Du brauchst &6%CREATION
|
||||
message.shopInfo.vendor=&6Verkäufer: &e%VENDOR%
|
||||
message.shopInfo.product=&6Produkt: &e%AMOUNT% x %ITEMNAME%
|
||||
message.shopInfo.stock=&6Auf Lager: &e%STOCK%
|
||||
message.shopInfo.enchantments=&6Verzauberungen: &e%ENCHANTMENT%
|
||||
message.shopInfo.potion-effect=&6Trank-Effekte: &e%POTION-EFFECT% %EXTENDED%
|
||||
message.shopInfo.music-disc-title=&6Schallplattentitel: &e%MUSIC-TITLE%
|
||||
message.shopInfo.book-generation=&6Generation: &e%GENERATION%
|
||||
message.shopInfo.none=&7Keine
|
||||
message.shopInfo.price=&6Preis: Kauf: &e%BUY-PRICE%&6 Verkauf: &e%SELL-PRICE%
|
||||
message.shopInfo.disabled=&7Deaktiviert
|
||||
message.shopInfo.is-normal=&6Typ: &eNormal
|
||||
message.shopInfo.is-admin=&6Typ: &eAdmin
|
||||
message.shopInfo.extended=(Verlängert)
|
||||
message.buy-and-sell-disabled=&cDu kannst keinen Shop ohne Kauf- und Verkaufspreis erstellen.
|
||||
message.buy-success=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE%&a von &6%VENDOR% &agekauft.
|
||||
message.buy-success-admin=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE% &agekauft.
|
||||
|
@ -49,25 +49,6 @@ message.shopInfo.product=&6Product: &e%AMOUNT% x %ITEMNAME%
|
||||
# Usable Placeholders=%STOCK%
|
||||
message.shopInfo.stock=&6In Stock: &e%STOCK%
|
||||
|
||||
# Set the enchantments message the player gets after entering '/shop info' if the product is enchanted
|
||||
# Usable Placeholders: %ENCHANTMENT%
|
||||
message.shopInfo.enchantments=&6Enchantments: &e%ENCHANTMENT%
|
||||
|
||||
# Set the potion effect message the player gets after entering '/shop info' if the product has a potion effect
|
||||
# Usable Placeholders: %POTION-EFFECT%, %EXTENDED%
|
||||
message.shopInfo.potion-effect=&6Potion Effect: &e%POTION-EFFECT% %EXTENDED%
|
||||
|
||||
# Set the music disc title message the player gets after entering '/shop info' if the product is a music disc
|
||||
# Usable Placeholders: %MUSIC-TITLE%
|
||||
message.shopInfo.music-disc-title=&6Music Disc Title: &e%MUSIC-TITLE%
|
||||
|
||||
# Set the generation message the player gets after entering '/shop info' if the product is a written book
|
||||
# Usable Placeholders: %GENERATION%
|
||||
message.shopInfo.book-generation=&6Generation: &e%GENERATION%
|
||||
|
||||
# If the product is a tipped arrow but it doesn't have an effect, this gets displayed instead of the arrow effect
|
||||
message.shopInfo.none=&7None
|
||||
|
||||
# Set the price message the player gets after entering '/shop info'.
|
||||
# Usable Placeholders: %BUY-PRICE%, %SELL-PRICE%
|
||||
message.shopInfo.price=&6Price: Buy: &e%BUY-PRICE%&6 Sell: &e%SELL-PRICE%
|
||||
@ -82,10 +63,6 @@ message.shopInfo.is-normal=&6Type: &eNormal
|
||||
# ... when the shop is an admin shop.
|
||||
message.shopInfo.is-admin=&6Type: &eAdmin
|
||||
|
||||
# If the potion effect it extended, %EXTENDED% will be replaced by this.
|
||||
# If not, this will be empty.
|
||||
message.shopInfo.extended=(Extended)
|
||||
|
||||
# Set the message when the player tries to create a shop with sell price and buy price set to 0
|
||||
message.buy-and-sell-disabled=&cYou can't create a shop with buying and selling disabled.
|
||||
|
||||
|
@ -42,25 +42,6 @@ message.shopInfo.product=&6Product: &e%AMOUNT% x %ITEMNAME%
|
||||
# Usable Placeholders=%STOCK%
|
||||
message.shopInfo.stock=&6In Stock: &e%STOCK%
|
||||
|
||||
# Set the enchantments message the player gets after entering '/shop info' if the product is enchanted
|
||||
# Usable Placeholders: %ENCHANTMENT%
|
||||
message.shopInfo.enchantments=&6Enchantments: &e%ENCHANTMENT%
|
||||
|
||||
# Set the potion effect message the player gets after entering '/shop info' if the product has a potion effect
|
||||
# Usable Placeholders: %POTION-EFFECT%, %EXTENDED%
|
||||
message.shopInfo.potion-effect=&6Potion Effect: &e%POTION-EFFECT% %EXTENDED%
|
||||
|
||||
# Set the music disc title message the player gets after entering '/shop info' if the product is a music disc
|
||||
# Usable Placeholders: %MUSIC-TITLE%
|
||||
message.shopInfo.music-disc-title=&6Music Disc Title: &e%MUSIC-TITLE%
|
||||
|
||||
# Set the generation message the player gets after entering '/shop info' if the product is a written book
|
||||
# Usable Placeholders: %GENERATION%
|
||||
message.shopInfo.book-generation=&6Generation: &e%GENERATION%
|
||||
|
||||
# If the product is a tipped arrow but it doesn't have an effect, this gets displayed instead of the arrow effect
|
||||
message.shopInfo.none=&7None
|
||||
|
||||
# Set the price message the player gets after entering '/shop info'.
|
||||
# Usable Placeholders: %BUY-PRICE%, %SELL-PRICE%
|
||||
message.shopInfo.price=&6Price: Buy: &e%BUY-PRICE%&6 Sell: &e%SELL-PRICE%
|
||||
@ -75,10 +56,6 @@ message.shopInfo.is-normal=&6Type: &eNormal
|
||||
# ... when the shop is an admin shop.
|
||||
message.shopInfo.is-admin=&6Type: &eAdmin
|
||||
|
||||
# If the potion effect it extended, %EXTENDED% will be replaced by this.
|
||||
# If not, this will be empty.
|
||||
message.shopInfo.extended=(Extended)
|
||||
|
||||
# Set the message when the player tries to create a shop with sell price and buy price set to 0
|
||||
message.buy-and-sell-disabled=&cYou can't create a shop with buying and selling disabled.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user