mirror of
https://github.com/SabreTools/SabreTools.RedumpLib.git
synced 2026-04-05 22:01:29 +00:00
Add undocumented, unused inputs
This commit is contained in:
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace RedumpTool.Features
|
||||
@@ -32,8 +33,8 @@ namespace RedumpTool.Features
|
||||
private const string _queryName = "query";
|
||||
internal readonly StringInput QueryInput = new(_queryName, ["-q", "--query"], "Redump-compatible query to run");
|
||||
|
||||
private const string _quickSearchName = "quicksearch";
|
||||
internal readonly FlagInput QuickSearchInput = new(_quickSearchName, ["-s", "--quick"], "Indicate a query is for the 'quicksearch' path, not 'discs'");
|
||||
private const string _quickQueryName = "quickquery";
|
||||
internal readonly FlagInput QuickQueryName = new(_quickQueryName, ["-s", "--quick"], "Indicate a query is for the 'quicksearch' path, not 'discs'");
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -52,9 +53,35 @@ namespace RedumpTool.Features
|
||||
Add(ForceDownloadInput);
|
||||
Add(ForceContinueInput);
|
||||
|
||||
// Discs Path
|
||||
Add(AntiModchipInput);
|
||||
Add(BarcodeInput);
|
||||
Add(CategoryInput);
|
||||
Add(CommentsInput);
|
||||
Add(ContentsInput);
|
||||
Add(DiscTypeInput);
|
||||
Add(DumperInput);
|
||||
Add(EdcInput);
|
||||
Add(EditionInput);
|
||||
Add(ErrorsInput);
|
||||
Add(LanguageInput);
|
||||
Add(LetterInput);
|
||||
Add(LibCryptInput);
|
||||
Add(MediaInput);
|
||||
Add(OffsetInput);
|
||||
Add(ProtectionInput);
|
||||
Add(QuickSearchInput);
|
||||
Add(RegionInput);
|
||||
Add(RingcodeInput);
|
||||
Add(SortInput);
|
||||
Add(SortDirInput);
|
||||
Add(StatusInput);
|
||||
Add(SystemInput);
|
||||
Add(TracksInput);
|
||||
|
||||
// Specific
|
||||
Add(QueryInput);
|
||||
Add(QuickSearchInput);
|
||||
Add(QuickQueryName);
|
||||
Add(ListInput);
|
||||
Add(NoSlashInput);
|
||||
Add(LimitInput);
|
||||
@@ -72,10 +99,51 @@ namespace RedumpTool.Features
|
||||
bool forceDownload = ForceDownloadInput.Value;
|
||||
bool forceContinue = ForceContinueInput.Value;
|
||||
|
||||
// Get discs path values
|
||||
bool? antiModchip = AntiModchipInput.Value;
|
||||
bool barcode = BarcodeInput.Value;
|
||||
string? categoryString = CategoryInput.Value;
|
||||
DiscCategory? category = categoryString.ToDiscCategory();
|
||||
bool comments = CommentsInput.Value;
|
||||
bool contents = ContentsInput.Value;
|
||||
string? discTypeString = DiscTypeInput.Value;
|
||||
DiscType? discType = discTypeString.ToDiscType();
|
||||
string? dumper = DumperInput.Value;
|
||||
bool? edc = EdcInput.Value;
|
||||
string? edition = EditionInput.Value;
|
||||
string? errors = ErrorsInput.Value;
|
||||
string? languageString = LanguageInput.Value;
|
||||
Language? language = languageString.ToLanguage();
|
||||
char? letter = string.IsNullOrEmpty(LetterInput.Value)
|
||||
? null
|
||||
: LetterInput.Value![0];
|
||||
bool? libcrypt = LibCryptInput.Value;
|
||||
MediaType? media = MediaInput.Value?.ToLowerInvariant() switch
|
||||
{
|
||||
"cd" => MediaType.CDROM,
|
||||
"dvd" => MediaType.DVD,
|
||||
_ => null,
|
||||
};
|
||||
int? offset = OffsetInput.Value;
|
||||
bool protection = ProtectionInput.Value;
|
||||
string? quicksearch = QuickSearchInput.Value;
|
||||
string? regionString = RegionInput.Value;
|
||||
Region? region = regionString.ToRegion();
|
||||
string? ringcode = RingcodeInput.Value;
|
||||
string? sortString = SortInput.Value;
|
||||
SortCategory? sort = sortString.ToSortCategory();
|
||||
string? sortDirString = SortDirInput.Value;
|
||||
SortDirection? sortDir = sortDirString.ToSortDirection();
|
||||
string? statusString = StatusInput.Value;
|
||||
DumpStatus? status = statusString.ToDumpStatus();
|
||||
string? systemString = SystemInput.Value;
|
||||
RedumpSystem? system = systemString.ToRedumpSystem();
|
||||
int? tracks = TracksInput.Value;
|
||||
|
||||
// Get specific values
|
||||
bool onlyList = ListInput.Value;
|
||||
string? queryString = QueryInput.Value;
|
||||
bool quick = QuickSearchInput.Value;
|
||||
bool quick = QuickQueryName.Value;
|
||||
bool convertForwardSlashes = !NoSlashInput.Value;
|
||||
int limit = LimitInput.Value ?? -1;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace RedumpTool.Features
|
||||
@@ -49,6 +50,32 @@ namespace RedumpTool.Features
|
||||
Add(ForceDownloadInput);
|
||||
Add(ForceContinueInput);
|
||||
|
||||
// Discs Path
|
||||
Add(AntiModchipInput);
|
||||
Add(BarcodeInput);
|
||||
Add(CategoryInput);
|
||||
Add(CommentsInput);
|
||||
Add(ContentsInput);
|
||||
Add(DiscTypeInput);
|
||||
Add(DumperInput);
|
||||
Add(EdcInput);
|
||||
Add(EditionInput);
|
||||
Add(ErrorsInput);
|
||||
Add(LanguageInput);
|
||||
Add(LetterInput);
|
||||
Add(LibCryptInput);
|
||||
Add(MediaInput);
|
||||
Add(OffsetInput);
|
||||
Add(ProtectionInput);
|
||||
Add(QuickSearchInput);
|
||||
Add(RegionInput);
|
||||
Add(RingcodeInput);
|
||||
Add(SortInput);
|
||||
Add(SortDirInput);
|
||||
Add(StatusInput);
|
||||
Add(SystemInput);
|
||||
Add(TracksInput);
|
||||
|
||||
// Specific
|
||||
Add(MinimumInput);
|
||||
Add(MaximumInput);
|
||||
@@ -68,6 +95,47 @@ namespace RedumpTool.Features
|
||||
bool forceDownload = ForceDownloadInput.Value;
|
||||
bool forceContinue = ForceContinueInput.Value;
|
||||
|
||||
// Get discs path values
|
||||
bool? antiModchip = AntiModchipInput.Value;
|
||||
bool barcode = BarcodeInput.Value;
|
||||
string? categoryString = CategoryInput.Value;
|
||||
DiscCategory? category = categoryString.ToDiscCategory();
|
||||
bool comments = CommentsInput.Value;
|
||||
bool contents = ContentsInput.Value;
|
||||
string? discTypeString = DiscTypeInput.Value;
|
||||
DiscType? discType = discTypeString.ToDiscType();
|
||||
string? dumper = DumperInput.Value;
|
||||
bool? edc = EdcInput.Value;
|
||||
string? edition = EditionInput.Value;
|
||||
string? errors = ErrorsInput.Value;
|
||||
string? languageString = LanguageInput.Value;
|
||||
Language? language = languageString.ToLanguage();
|
||||
char? letter = string.IsNullOrEmpty(LetterInput.Value)
|
||||
? null
|
||||
: LetterInput.Value![0];
|
||||
bool? libcrypt = LibCryptInput.Value;
|
||||
MediaType? media = MediaInput.Value?.ToLowerInvariant() switch
|
||||
{
|
||||
"cd" => MediaType.CDROM,
|
||||
"dvd" => MediaType.DVD,
|
||||
_ => null,
|
||||
};
|
||||
int? offset = OffsetInput.Value;
|
||||
bool protection = ProtectionInput.Value;
|
||||
string? quicksearch = QuickSearchInput.Value;
|
||||
string? regionString = RegionInput.Value;
|
||||
Region? region = regionString.ToRegion();
|
||||
string? ringcode = RingcodeInput.Value;
|
||||
string? sortString = SortInput.Value;
|
||||
SortCategory? sort = sortString.ToSortCategory();
|
||||
string? sortDirString = SortDirInput.Value;
|
||||
SortDirection? sortDir = sortDirString.ToSortDirection();
|
||||
string? statusString = StatusInput.Value;
|
||||
DumpStatus? status = statusString.ToDumpStatus();
|
||||
string? systemString = SystemInput.Value;
|
||||
RedumpSystem? system = systemString.ToRedumpSystem();
|
||||
int? tracks = TracksInput.Value;
|
||||
|
||||
// Get specific values
|
||||
int minId = MinimumInput.Value ?? -1;
|
||||
int maxId = MaximumInput.Value ?? -1;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace RedumpTool.Features
|
||||
@@ -46,6 +47,32 @@ namespace RedumpTool.Features
|
||||
Add(ForceDownloadInput);
|
||||
Add(ForceContinueInput);
|
||||
|
||||
// Discs Path
|
||||
Add(AntiModchipInput);
|
||||
Add(BarcodeInput);
|
||||
Add(CategoryInput);
|
||||
Add(CommentsInput);
|
||||
Add(ContentsInput);
|
||||
Add(DiscTypeInput);
|
||||
Add(DumperInput);
|
||||
Add(EdcInput);
|
||||
Add(EditionInput);
|
||||
Add(ErrorsInput);
|
||||
Add(LanguageInput);
|
||||
Add(LetterInput);
|
||||
Add(LibCryptInput);
|
||||
Add(MediaInput);
|
||||
Add(OffsetInput);
|
||||
Add(ProtectionInput);
|
||||
Add(QuickSearchInput);
|
||||
Add(RegionInput);
|
||||
Add(RingcodeInput);
|
||||
Add(SortInput);
|
||||
Add(SortDirInput);
|
||||
Add(StatusInput);
|
||||
Add(SystemInput);
|
||||
Add(TracksInput);
|
||||
|
||||
// Specific
|
||||
Add(OnlyNewInput);
|
||||
Add(ListInput);
|
||||
@@ -64,6 +91,47 @@ namespace RedumpTool.Features
|
||||
bool forceDownload = ForceDownloadInput.Value;
|
||||
bool forceContinue = ForceContinueInput.Value;
|
||||
|
||||
// Get discs path values
|
||||
bool? antiModchip = AntiModchipInput.Value;
|
||||
bool barcode = BarcodeInput.Value;
|
||||
string? categoryString = CategoryInput.Value;
|
||||
DiscCategory? category = categoryString.ToDiscCategory();
|
||||
bool comments = CommentsInput.Value;
|
||||
bool contents = ContentsInput.Value;
|
||||
string? discTypeString = DiscTypeInput.Value;
|
||||
DiscType? discType = discTypeString.ToDiscType();
|
||||
string? dumper = DumperInput.Value;
|
||||
bool? edc = EdcInput.Value;
|
||||
string? edition = EditionInput.Value;
|
||||
string? errors = ErrorsInput.Value;
|
||||
string? languageString = LanguageInput.Value;
|
||||
Language? language = languageString.ToLanguage();
|
||||
char? letter = string.IsNullOrEmpty(LetterInput.Value)
|
||||
? null
|
||||
: LetterInput.Value![0];
|
||||
bool? libcrypt = LibCryptInput.Value;
|
||||
MediaType? media = MediaInput.Value?.ToLowerInvariant() switch
|
||||
{
|
||||
"cd" => MediaType.CDROM,
|
||||
"dvd" => MediaType.DVD,
|
||||
_ => null,
|
||||
};
|
||||
int? offset = OffsetInput.Value;
|
||||
bool protection = ProtectionInput.Value;
|
||||
string? quicksearch = QuickSearchInput.Value;
|
||||
string? regionString = RegionInput.Value;
|
||||
Region? region = regionString.ToRegion();
|
||||
string? ringcode = RingcodeInput.Value;
|
||||
string? sortString = SortInput.Value;
|
||||
SortCategory? sort = sortString.ToSortCategory();
|
||||
string? sortDirString = SortDirInput.Value;
|
||||
SortDirection? sortDir = sortDirString.ToSortDirection();
|
||||
string? statusString = StatusInput.Value;
|
||||
DumpStatus? status = statusString.ToDumpStatus();
|
||||
string? systemString = SystemInput.Value;
|
||||
RedumpSystem? system = systemString.ToRedumpSystem();
|
||||
int? tracks = TracksInput.Value;
|
||||
|
||||
// Get specific values
|
||||
bool onlyNew = OnlyNewInput.Value;
|
||||
bool onlyList = ListInput.Value;
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace SabreTools.RedumpLib.Test.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region DiscSubpath
|
||||
#region Disc Subpath
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DiscSubpath has a long name provided
|
||||
@@ -382,6 +382,117 @@ namespace SabreTools.RedumpLib.Test.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dump Status
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DumpStatus has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus">DumpStatus value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDumpStatusTestData))]
|
||||
public void DumpStatus_LongName(DumpStatus? dumpStatus, bool expectNull)
|
||||
{
|
||||
var actual = dumpStatus.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DumpStatus has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus">DumpStatus value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDumpStatusTestData))]
|
||||
public void DumpStatus_ShortName(DumpStatus? dumpStatus, bool expectNull)
|
||||
{
|
||||
var actual = dumpStatus.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that every DumpStatus that has a short name that is unique
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DumpStatus_ShortName_NoDuplicates()
|
||||
{
|
||||
var fullDumpStatuses = Enum.GetValues<DumpStatus>().Cast<DumpStatus>().ToList();
|
||||
var filteredDumpStatuses = new Dictionary<string, DumpStatus?>();
|
||||
|
||||
int totalCount = 0;
|
||||
foreach (DumpStatus? dumpStatus in fullDumpStatuses)
|
||||
{
|
||||
var code = dumpStatus.ShortName();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
if (filteredDumpStatuses.ContainsKey(code))
|
||||
throw new DuplicateNameException($"Code {code} already in dictionary");
|
||||
|
||||
filteredDumpStatuses[code] = dumpStatus;
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
Assert.Equal(totalCount, filteredDumpStatuses.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DumpStatus can be mapped from a string
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus">DumpStatus value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDumpStatusTestData))]
|
||||
public void DumpStatus_ToDumpStatus(DumpStatus? dumpStatus, bool expectNull)
|
||||
{
|
||||
string? longName = dumpStatus.LongName();
|
||||
string? longNameSpaceless = longName?.Replace(" ", string.Empty);
|
||||
string? possibleInteger = $"{(int?)dumpStatus}";
|
||||
|
||||
var actualNormal = longName.ToDumpStatus();
|
||||
var actualSpaceless = longNameSpaceless.ToDumpStatus();
|
||||
var actualInteger = possibleInteger.ToDumpStatus();
|
||||
|
||||
if (expectNull)
|
||||
{
|
||||
Assert.Null(actualNormal);
|
||||
Assert.Null(actualSpaceless);
|
||||
Assert.Null(actualInteger);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(dumpStatus, actualNormal);
|
||||
Assert.Equal(dumpStatus, actualSpaceless);
|
||||
Assert.Equal(dumpStatus, actualInteger);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DumpStatus values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DumpStatus values</returns>
|
||||
public static TheoryData<DumpStatus?, bool> GenerateDumpStatusTestData()
|
||||
{
|
||||
var testData = new TheoryData<DumpStatus?, bool>() { { null, true } };
|
||||
foreach (DumpStatus? dumpStatus in Enum.GetValues<DumpStatus>().Cast<DumpStatus?>())
|
||||
{
|
||||
testData.Add(dumpStatus, false);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language
|
||||
|
||||
/// <summary>
|
||||
@@ -674,7 +785,7 @@ namespace SabreTools.RedumpLib.Test.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region PackType
|
||||
#region Pack Type
|
||||
|
||||
/// <summary>
|
||||
/// Check that every PackType has a long name provided
|
||||
@@ -1198,7 +1309,7 @@ namespace SabreTools.RedumpLib.Test.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region SortCategory
|
||||
#region Sort Category
|
||||
|
||||
/// <summary>
|
||||
/// Check that every SortCategory has a long name provided
|
||||
@@ -1305,7 +1416,7 @@ namespace SabreTools.RedumpLib.Test.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region SortDirection
|
||||
#region Sort Direction
|
||||
|
||||
/// <summary>
|
||||
/// Check that every SortDirection has a long name provided
|
||||
|
||||
@@ -873,7 +873,7 @@ namespace SabreTools.RedumpLib.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region DiscSubpath
|
||||
#region Disc Subpath
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a DiscSubpath
|
||||
@@ -1003,6 +1003,74 @@ namespace SabreTools.RedumpLib.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dump Status
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a DumpStatus
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus"></param>
|
||||
/// <returns></returns>
|
||||
public static string? LongName(this DumpStatus dumpStatus)
|
||||
=> AttributeHelper<DumpStatus>.GetAttribute(dumpStatus)?.LongName;
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a DumpStatus
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus"></param>
|
||||
/// <returns></returns>
|
||||
public static string? LongName(this DumpStatus? dumpStatus)
|
||||
=> AttributeHelper<DumpStatus?>.GetAttribute(dumpStatus)?.LongName;
|
||||
|
||||
/// <summary>
|
||||
/// Get the URL path part for a DumpStatus
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus"></param>
|
||||
/// <returns></returns>
|
||||
public static string? ShortName(this DumpStatus dumpStatus)
|
||||
=> AttributeHelper<DumpStatus>.GetAttribute(dumpStatus)?.ShortName;
|
||||
|
||||
/// <summary>
|
||||
/// Get the URL path part for a DumpStatus
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus"></param>
|
||||
/// <returns></returns>
|
||||
public static string? ShortName(this DumpStatus? dumpStatus)
|
||||
=> AttributeHelper<DumpStatus?>.GetAttribute(dumpStatus)?.ShortName;
|
||||
|
||||
/// <summary>
|
||||
/// Get the Region enum value for a given string
|
||||
/// </summary>
|
||||
/// <param name="dumpStatus">String value to convert</param>
|
||||
/// <returns>Region represented by the string, if possible</returns>
|
||||
public static DumpStatus? ToDumpStatus(this string? dumpStatus)
|
||||
{
|
||||
// No value means no match
|
||||
if (dumpStatus is null || dumpStatus.Length == 0)
|
||||
return null;
|
||||
|
||||
dumpStatus = dumpStatus.ToLowerInvariant();
|
||||
var dumpStatuses = (DumpStatus[])Enum.GetValues(typeof(DumpStatus));
|
||||
|
||||
// Check short names
|
||||
int index = Array.FindIndex(dumpStatuses, s => dumpStatus == s.ShortName()?.ToLowerInvariant());
|
||||
if (index > -1)
|
||||
return dumpStatuses[index];
|
||||
|
||||
// Check long names
|
||||
index = Array.FindIndex(dumpStatuses, s => dumpStatus == s.LongName()?.ToLowerInvariant()
|
||||
|| dumpStatus == s.LongName()?.Replace(" ", string.Empty)?.ToLowerInvariant());
|
||||
if (index > -1)
|
||||
return dumpStatuses[index];
|
||||
|
||||
// Check numeric values
|
||||
if (int.TryParse(dumpStatus, out int dumpStatusInt) && Enum.IsDefined(typeof(DumpStatus), dumpStatusInt))
|
||||
return (DumpStatus)dumpStatusInt;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language
|
||||
|
||||
/// <summary>
|
||||
@@ -1204,7 +1272,7 @@ namespace SabreTools.RedumpLib.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region PackType
|
||||
#region Pack Type
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a PackType
|
||||
@@ -1585,7 +1653,7 @@ namespace SabreTools.RedumpLib.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region SortCategory
|
||||
#region Sort Category
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a SortCategory
|
||||
@@ -1649,7 +1717,7 @@ namespace SabreTools.RedumpLib.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region SortDirection
|
||||
#region Sort Direction
|
||||
|
||||
/// <summary>
|
||||
/// Get the human readable name for a SortDirection
|
||||
|
||||
Reference in New Issue
Block a user