mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 11:14:25 +00:00
Added a localized description for release types.
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
using Aaru.Localization;
|
||||
|
||||
namespace Aaru.CommonTypes.AaruMetadata;
|
||||
|
||||
@@ -48,16 +49,28 @@ namespace Aaru.CommonTypes.AaruMetadata;
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public enum ReleaseType
|
||||
{
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Retail))]
|
||||
Retail,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Bundle))]
|
||||
Bundle,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Coverdisc))]
|
||||
Coverdisc,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Subscription))]
|
||||
Subscription,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Demo))]
|
||||
Demo,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_OEM))]
|
||||
OEM,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Shareware))]
|
||||
Shareware,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_FOSS))]
|
||||
FOSS,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Adware))]
|
||||
Adware,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_Donationware))]
|
||||
Donationware,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_DigitalDownload))]
|
||||
DigitalDownload,
|
||||
[LocalizedDescription(nameof(UI.ReleaseType_SaaS))]
|
||||
SaaS
|
||||
}
|
||||
90
Aaru.Gui/Helpers/LocalizedEnumHelper.cs
Normal file
90
Aaru.Gui/Helpers/LocalizedEnumHelper.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : LocalizedEnumHelper.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : GUI helpers.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Helper class for working with localized enum values.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2025 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Aaru.Gui.Helpers;
|
||||
|
||||
/// <summary>Helper class for working with localized enum values</summary>
|
||||
public static class LocalizedEnumHelper
|
||||
{
|
||||
/// <summary>Gets the localized description for an enum value</summary>
|
||||
/// <param name="value">The enum value</param>
|
||||
/// <returns>The localized description, or the enum value's name if no description is found</returns>
|
||||
[NotNull]
|
||||
public static string GetLocalizedDescription([NotNull] Enum value)
|
||||
{
|
||||
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
|
||||
|
||||
if(fieldInfo == null) return value.ToString();
|
||||
|
||||
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Gets all values of an enum type wrapped with their localized descriptions</summary>
|
||||
/// <typeparam name="T">The enum type</typeparam>
|
||||
/// <returns>A collection of LocalizedEnumValue wrappers</returns>
|
||||
[NotNull]
|
||||
public static IEnumerable<LocalizedEnumValue<T>> GetLocalizedValues<T>() where T : struct, Enum
|
||||
{
|
||||
return Enum.GetValues<T>().Select(static value => new LocalizedEnumValue<T>(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Wrapper class for an enum value with its localized description</summary>
|
||||
/// <typeparam name="T">The enum type</typeparam>
|
||||
public class LocalizedEnumValue<T> where T : struct, Enum
|
||||
{
|
||||
/// <summary>Initializes a new instance of the LocalizedEnumValue class</summary>
|
||||
/// <param name="value">The enum value</param>
|
||||
public LocalizedEnumValue(T value)
|
||||
{
|
||||
Value = value;
|
||||
Description = LocalizedEnumHelper.GetLocalizedDescription(value);
|
||||
}
|
||||
|
||||
/// <summary>Gets the enum value</summary>
|
||||
public T Value { get; }
|
||||
|
||||
/// <summary>Gets the localized description</summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>Returns the localized description as the string representation</summary>
|
||||
public override string ToString() => Description;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.Gui.Helpers;
|
||||
using Aaru.Gui.Localization;
|
||||
using Aaru.Localization;
|
||||
using Avalonia;
|
||||
@@ -128,7 +129,7 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
ObservableCollection<RequiredOperatingSystemViewModel> _requiredOperatingSystems = [];
|
||||
|
||||
[ObservableProperty]
|
||||
ReleaseType? _selectedReleaseType;
|
||||
LocalizedEnumValue<ReleaseType> _selectedReleaseType;
|
||||
|
||||
[ObservableProperty]
|
||||
string _serialNumber;
|
||||
@@ -159,7 +160,8 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
|
||||
// Available enum values for ComboBoxes
|
||||
[NotNull]
|
||||
public IEnumerable<ReleaseType> AvailableReleaseTypes => Enum.GetValues<ReleaseType>();
|
||||
public IEnumerable<LocalizedEnumValue<ReleaseType>> AvailableReleaseTypes =>
|
||||
LocalizedEnumHelper.GetLocalizedValues<ReleaseType>();
|
||||
[NotNull]
|
||||
public IEnumerable<Language> AvailableLanguages => Enum.GetValues<Language>();
|
||||
[NotNull]
|
||||
@@ -181,7 +183,11 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
// Basic fields
|
||||
Name = metadata.Name;
|
||||
Version = metadata.Version;
|
||||
SelectedReleaseType = metadata.Release;
|
||||
|
||||
SelectedReleaseType = metadata.Release.HasValue
|
||||
? new LocalizedEnumValue<ReleaseType>(metadata.Release.Value)
|
||||
: null;
|
||||
|
||||
ReleaseDate = metadata.ReleaseDate;
|
||||
PartNumber = metadata.PartNumber;
|
||||
SerialNumber = metadata.SerialNumber;
|
||||
@@ -202,19 +208,16 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
|
||||
// Complex objects
|
||||
if(metadata.Barcodes != null)
|
||||
{
|
||||
foreach(Barcode barcode in metadata.Barcodes) Barcodes.Add(new BarcodeViewModel(barcode));
|
||||
}
|
||||
foreach(Barcode barcode in metadata.Barcodes)
|
||||
Barcodes.Add(new BarcodeViewModel(barcode));
|
||||
|
||||
if(metadata.Magazines != null)
|
||||
{
|
||||
foreach(Magazine magazine in metadata.Magazines) Magazines.Add(new MagazineViewModel(magazine));
|
||||
}
|
||||
foreach(Magazine magazine in metadata.Magazines)
|
||||
Magazines.Add(new MagazineViewModel(magazine));
|
||||
|
||||
if(metadata.Books != null)
|
||||
{
|
||||
foreach(Book book in metadata.Books) Books.Add(new BookViewModel(book));
|
||||
}
|
||||
foreach(Book book in metadata.Books)
|
||||
Books.Add(new BookViewModel(book));
|
||||
|
||||
if(metadata.RequiredOperatingSystems != null)
|
||||
{
|
||||
@@ -223,39 +226,32 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
if(metadata.UserManuals != null)
|
||||
{
|
||||
foreach(UserManual manual in metadata.UserManuals) UserManuals.Add(new UserManualViewModel(manual));
|
||||
}
|
||||
foreach(UserManual manual in metadata.UserManuals)
|
||||
UserManuals.Add(new UserManualViewModel(manual));
|
||||
|
||||
if(metadata.OpticalDiscs != null)
|
||||
{
|
||||
foreach(OpticalDisc disc in metadata.OpticalDiscs) OpticalDiscs.Add(new OpticalDiscViewModel(disc));
|
||||
}
|
||||
foreach(OpticalDisc disc in metadata.OpticalDiscs)
|
||||
OpticalDiscs.Add(new OpticalDiscViewModel(disc));
|
||||
|
||||
if(metadata.Advertisements != null)
|
||||
{
|
||||
foreach(Advertisement ad in metadata.Advertisements) Advertisements.Add(new AdvertisementViewModel(ad));
|
||||
}
|
||||
foreach(Advertisement ad in metadata.Advertisements)
|
||||
Advertisements.Add(new AdvertisementViewModel(ad));
|
||||
|
||||
if(metadata.LinearMedias != null)
|
||||
{
|
||||
foreach(LinearMedia media in metadata.LinearMedias) LinearMedias.Add(new LinearMediaViewModel(media));
|
||||
}
|
||||
foreach(LinearMedia media in metadata.LinearMedias)
|
||||
LinearMedias.Add(new LinearMediaViewModel(media));
|
||||
|
||||
if(metadata.PciCards != null)
|
||||
{
|
||||
foreach(Pci pci in metadata.PciCards) PciCards.Add(new PciViewModel(pci));
|
||||
}
|
||||
foreach(Pci pci in metadata.PciCards)
|
||||
PciCards.Add(new PciViewModel(pci));
|
||||
|
||||
if(metadata.BlockMedias != null)
|
||||
{
|
||||
foreach(BlockMedia media in metadata.BlockMedias) BlockMedias.Add(new BlockMediaViewModel(media));
|
||||
}
|
||||
foreach(BlockMedia media in metadata.BlockMedias)
|
||||
BlockMedias.Add(new BlockMediaViewModel(media));
|
||||
|
||||
if(metadata.AudioMedias != null)
|
||||
{
|
||||
foreach(AudioMedia media in metadata.AudioMedias) AudioMedias.Add(new AudioMediaViewModel(media));
|
||||
}
|
||||
foreach(AudioMedia media in metadata.AudioMedias)
|
||||
AudioMedias.Add(new AudioMediaViewModel(media));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
@@ -292,7 +288,7 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
{
|
||||
Name = Name,
|
||||
Version = Version,
|
||||
Release = SelectedReleaseType,
|
||||
Release = SelectedReleaseType?.Value,
|
||||
ReleaseDate = ReleaseDate,
|
||||
PartNumber = PartNumber,
|
||||
SerialNumber = SerialNumber,
|
||||
@@ -314,12 +310,9 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
? [..RequiredOperatingSystems.Select(static os => os.ToModel())]
|
||||
: null,
|
||||
UserManuals = UserManuals.Any() ? [..UserManuals.Select(static um => um.ToModel())] : null,
|
||||
OpticalDiscs =
|
||||
OpticalDiscs.Any() ? [..OpticalDiscs.Select(static od => od.ToModel())] : null,
|
||||
Advertisements =
|
||||
Advertisements.Any() ? [..Advertisements.Select(static a => a.ToModel())] : null,
|
||||
LinearMedias =
|
||||
LinearMedias.Any() ? [..LinearMedias.Select(static lm => lm.ToModel())] : null,
|
||||
OpticalDiscs = OpticalDiscs.Any() ? [..OpticalDiscs.Select(static od => od.ToModel())] : null,
|
||||
Advertisements = Advertisements.Any() ? [..Advertisements.Select(static a => a.ToModel())] : null,
|
||||
LinearMedias = LinearMedias.Any() ? [..LinearMedias.Select(static lm => lm.ToModel())] : null,
|
||||
PciCards = PciCards.Any() ? [..PciCards.Select(static p => p.ToModel())] : null,
|
||||
BlockMedias = BlockMedias.Any() ? [..BlockMedias.Select(static bm => bm.ToModel())] : null,
|
||||
AudioMedias = AudioMedias.Any() ? [..AudioMedias.Select(static am => am.ToModel())] : null
|
||||
@@ -346,10 +339,7 @@ public sealed partial class MetadataEditorViewModel : ViewModelBase
|
||||
[
|
||||
new FilePickerFileType(GUI.FileType_JSON)
|
||||
{
|
||||
Patterns =
|
||||
[
|
||||
"*.json"
|
||||
]
|
||||
Patterns = ["*.json"]
|
||||
}
|
||||
],
|
||||
DefaultExtension = "json"
|
||||
@@ -601,6 +591,7 @@ public sealed partial class BookViewModel : ObservableObject
|
||||
|
||||
[ObservableProperty]
|
||||
string _editorial;
|
||||
|
||||
[ObservableProperty]
|
||||
string _name;
|
||||
|
||||
@@ -610,6 +601,7 @@ public sealed partial class BookViewModel : ObservableObject
|
||||
[ObservableProperty]
|
||||
string _pageSize;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
DateTime? _publicationDate;
|
||||
|
||||
|
||||
72
Aaru.Localization/UI.Designer.cs
generated
72
Aaru.Localization/UI.Designer.cs
generated
@@ -6891,5 +6891,77 @@ namespace Aaru.Localization {
|
||||
return ResourceManager.GetString("AaruFormat_images_version_1_x_are_read_only", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Retail {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Retail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Bundle {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Bundle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Coverdisc {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Coverdisc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Subscription {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Subscription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Demo {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Demo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_OEM {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_OEM", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Shareware {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Shareware", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_FOSS {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_FOSS", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Adware {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Adware", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_Donationware {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_Donationware", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_DigitalDownload {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_DigitalDownload", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReleaseType_SaaS {
|
||||
get {
|
||||
return ResourceManager.GetString("ReleaseType_SaaS", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3446,4 +3446,40 @@ Probadores:
|
||||
<data name="AaruFormat_images_version_1_x_are_read_only" xml:space="preserve">
|
||||
<value>Las imágenes AaruFormat versión 1.x son de sólo lectura, conviértala primero a una versión más moderna.</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Retail" xml:space="preserve">
|
||||
<value>Comercial</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Bundle" xml:space="preserve">
|
||||
<value>Paquete</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Coverdisc" xml:space="preserve">
|
||||
<value>Disco de Portada</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Subscription" xml:space="preserve">
|
||||
<value>Suscripción</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Demo" xml:space="preserve">
|
||||
<value>Demostración</value>
|
||||
</data>
|
||||
<data name="ReleaseType_OEM" xml:space="preserve">
|
||||
<value>OEM</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Shareware" xml:space="preserve">
|
||||
<value>Shareware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_FOSS" xml:space="preserve">
|
||||
<value>FOSS (Software Libre/Código Abierto)</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Adware" xml:space="preserve">
|
||||
<value>Adware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Donationware" xml:space="preserve">
|
||||
<value>Donationware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_DigitalDownload" xml:space="preserve">
|
||||
<value>Descarga Digital</value>
|
||||
</data>
|
||||
<data name="ReleaseType_SaaS" xml:space="preserve">
|
||||
<value>SaaS (Software como Servicio)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -3524,4 +3524,40 @@ Do you want to continue?</value>
|
||||
<data name="AaruFormat_images_version_1_x_are_read_only" xml:space="preserve">
|
||||
<value>AaruFormat images version 1.x are read-only, convert it to a newer version first.</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Retail" xml:space="preserve">
|
||||
<value>Retail</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Bundle" xml:space="preserve">
|
||||
<value>Bundle</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Coverdisc" xml:space="preserve">
|
||||
<value>Cover Disc</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Subscription" xml:space="preserve">
|
||||
<value>Subscription</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Demo" xml:space="preserve">
|
||||
<value>Demo</value>
|
||||
</data>
|
||||
<data name="ReleaseType_OEM" xml:space="preserve">
|
||||
<value>OEM</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Shareware" xml:space="preserve">
|
||||
<value>Shareware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_FOSS" xml:space="preserve">
|
||||
<value>FOSS (Free/Open Source)</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Adware" xml:space="preserve">
|
||||
<value>Adware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_Donationware" xml:space="preserve">
|
||||
<value>Donationware</value>
|
||||
</data>
|
||||
<data name="ReleaseType_DigitalDownload" xml:space="preserve">
|
||||
<value>Digital Download</value>
|
||||
</data>
|
||||
<data name="ReleaseType_SaaS" xml:space="preserve">
|
||||
<value>SaaS (Software as a Service)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,7 +1,4 @@
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xml:space="preserve">
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:String x:Key="/Default/CodeInspection/CppInitialization/DefaultPointerInitializer/@EntryValue">Null</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=858398D1_002D7321_002D4763_002D8BAB_002D56BBFEC74E29_002Fd_003Acuetools_002Enet/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AnnotateCanBeNullTypeMember/@EntryIndexedValue"></s:String>
|
||||
@@ -1046,6 +1043,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Connectix/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=copytape/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=coredump/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Coverdisc/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=CPCDSK/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=CPCEMU/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Cplus/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
Reference in New Issue
Block a user