diff --git a/Marechai/Areas/Admin/Views/People/Edit.cshtml b/Marechai/Areas/Admin/Views/People/Edit.cshtml deleted file mode 100644 index 904e17c5..00000000 --- a/Marechai/Areas/Admin/Views/People/Edit.cshtml +++ /dev/null @@ -1,96 +0,0 @@ -@model Marechai.Database.Models.Person - -@{ - ViewData["Title"] = "Edit"; -} -

Edit

-

Person

-
-
-
-
-
-
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- - -
- - -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} \ No newline at end of file diff --git a/Marechai/Marechai.csproj b/Marechai/Marechai.csproj index 420603f0..b1fd1f4e 100644 --- a/Marechai/Marechai.csproj +++ b/Marechai/Marechai.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 3.0.99.1224 + 3.0.99.1225 Canary Islands Computer Museum Copyright © 2003-2020 Natalia Portillo Canary Islands Computer Museum Website diff --git a/Marechai/Pages/Admin/Details/Person.razor b/Marechai/Pages/Admin/Details/Person.razor index 137b73c8..193d5325 100644 --- a/Marechai/Pages/Admin/Details/Person.razor +++ b/Marechai/Pages/Admin/Details/Person.razor @@ -31,9 +31,11 @@ } @page "/admin/people/details/{Id:int}" +@page "/admin/people/edit/{Id:int}" @inherits OwningComponentBase @inject IStringLocalizer L @inject Iso31661NumericService CountriesService +@inject NavigationManager NavigationManager @attribute [Authorize(Roles = "UberAdmin, Admin")]

@L["Person details"]


@@ -46,80 +48,213 @@ }
- @if (_editable || _model.Name != null) + @if (_editing || _model.Name != null) { @L["Name"] - + @if (_editing) + { + @L["Unknown (name)"] + } + @if (!_editing || + !_unknownName) + { + + + + @L["Please enter a valid name."] + + + + } } - @if (_editable || _model.Surname != null) + @if (_editing || _model.Surname != null) { @L["Surname"] - + @if (_editing) + { + @L["Unknown (surname)"] + } + @if (!_editing || + !_unknownSurname) + { + + + + @L["Please enter a valid surname."] + + + + } } - @if (_editable || _model.Alias != null) + @if (_editing || _model.Alias != null) { @L["Alias"] - + @if (_editing) + { + @L["Unknown (alias)"] + } + @if (!_editing || + !_unknownAlias) + { + + + + @L["Please enter a valid alias."] + + + + } } - @if (_editable || _model.DisplayName != null) + @if (_editing || _model.DisplayName != null) { @L["Display name"] - + @if (_editing) + { + @L["Unknown (display name)"] + } + @if (!_editing || + !_unknownDisplayName) + { + + + + @L["Please enter a valid display name."] + + + + } } - @if (_editable || _model.CountryOfBirthId != null) + @if (_editing || _model.CountryOfBirthId != null) { @L["Country of birth"] - + @if (_editing) + { + @L["Unknown (country of birth)"] + } + @if (!_editing || + !_unknownCountry) + { + + } } @L["Birth date"] - + + + + @L["Please enter a valid birth date."] + + + - @if (_editable || _model.DeathDate != null) + @if (_editing || _model.DeathDate != null) { @L["Date of death"] - + @if (_editing) + { + @L["Unknown (death date)"] + } + @if (!_editing || !_unknownDeathDate) + { + + + + @L["Please enter a valid death date."] + + + + } } - @if (_editable || _model.Webpage != null) + @if (_editing || _model.Webpage != null) { @L["Webpage"] - + @if (_editing) + { + @L["Unknown (webpage)"] + } + @if (!_editing || + !_unknownWebpage) + { + + + + @L["Please enter a valid webpage."] + + + + } } - @if (_editable || _model.Twitter != null) + @if (_editing || _model.Twitter != null) { @L["Twitter"] - + @if (_editing) + { + @L["Unknown (twitter)"] + } + @if (!_editing || + !_unknownTwitter) + { + + + + @L["Please enter a valid Twitter handle."] + + + + } } - @if (_editable || _model.Facebook != null) + @if (_editing || _model.Facebook != null) { - @L["Facebook"] - + + @if (_editing) + { + @L["Unknown (facebook)"] + } + @if (!_editing || + !_unknownFacebook) + { + + + + @L["Please enter a valid Facebook user name."] + + + + } }
- @L["Edit"] + @if (!_editing) + { + + } + else + { + + + } @L["Back to list"]
\ No newline at end of file diff --git a/Marechai/Pages/Admin/Details/Person.razor.cs b/Marechai/Pages/Admin/Details/Person.razor.cs index 1c327864..2b113ffd 100644 --- a/Marechai/Pages/Admin/Details/Person.razor.cs +++ b/Marechai/Pages/Admin/Details/Person.razor.cs @@ -1,16 +1,33 @@ +using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using System.Threading.Tasks; +using Blazorise; using Marechai.Database.Models; +using Marechai.Shared; +using Marechai.ViewModels; using Microsoft.AspNetCore.Components; +using Match = System.Text.RegularExpressions.Match; namespace Marechai.Pages.Admin.Details { public partial class Person { - List _countries; - bool _editable; - bool _loaded; - Database.Models.Person _model; + const string _webpageRegex = + @"^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$"; + List _countries; + bool _editing; + bool _loaded; + PersonViewModel _model; + bool _unknownAlias; + bool _unknownCountry; + bool _unknownDeathDate; + bool _unknownDisplayName; + bool _unknownFacebook; + bool _unknownName; + bool _unknownSurname; + bool _unknownTwitter; + bool _unknownWebpage; [Parameter] public int Id { get; set; } @@ -27,7 +44,275 @@ namespace Marechai.Pages.Admin.Details _countries = await CountriesService.GetAsync(); _model = await Service.GetAsync(Id); + _editing = NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLowerInvariant(). + StartsWith("admin/people/edit/", StringComparison.InvariantCulture); + + if(_editing) + SetCheckboxes(); + StateHasChanged(); } + + void SetCheckboxes() + { + _unknownAlias = string.IsNullOrWhiteSpace(_model.Alias); + _unknownCountry = !_model.CountryOfBirthId.HasValue; + _unknownDeathDate = !_model.DeathDate.HasValue; + _unknownDisplayName = string.IsNullOrWhiteSpace(_model.DisplayName); + _unknownFacebook = string.IsNullOrWhiteSpace(_model.Facebook); + _unknownName = string.IsNullOrWhiteSpace(_model.Name); + _unknownSurname = string.IsNullOrWhiteSpace(_model.Surname); + _unknownTwitter = string.IsNullOrWhiteSpace(_model.Twitter); + _unknownWebpage = string.IsNullOrWhiteSpace(_model.Webpage); + } + + void OnEditClicked() + { + _editing = true; + SetCheckboxes(); + StateHasChanged(); + } + + async void OnCancelClicked() + { + _editing = false; + _model = await Service.GetAsync(Id); + SetCheckboxes(); + StateHasChanged(); + } + + async void OnSaveClicked() + { + if(_unknownAlias) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownCountry) + _model.CountryOfBirthId = null; + else if(_model.CountryOfBirthId < 0) + return; + + if(_model.BirthDate.Date >= DateTime.UtcNow.Date) + return; + + if(_unknownDeathDate) + _model.DeathDate = null; + else if(_model.DeathDate?.Date >= DateTime.UtcNow.Date) + return; + else if(_model.DeathDate?.Date <= _model.BirthDate.Date) + return; + + if(_unknownAlias) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownDisplayName) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownFacebook) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownName) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownSurname) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if(_unknownWebpage) + _model.Alias = null; + else if(string.IsNullOrWhiteSpace(_model.Alias)) + return; + + if((_unknownName && !_unknownSurname) || + (!_unknownName && _unknownSurname)) + return; + + // TODO: Show error here + if(_unknownName && + _unknownSurname && + _unknownAlias && + _unknownDisplayName) + return; + + _editing = false; + await Service.UpdateAsync(_model); + _model = await Service.GetAsync(Id); + SetCheckboxes(); + StateHasChanged(); + } + + void ValidateName(ValidatorEventArgs e) + { + if(!(e.Value is string name)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(name.Length < 1 || + name.Length > 256) + { + e.ErrorText = L["Name must be smaller than 256 characters."]; + e.Status = ValidationStatus.Error; + + return; + } + + if(!string.IsNullOrWhiteSpace(_model.Surname) && + !_unknownSurname) + return; + + e.ErrorText = L["Both name and surname must be known and filled, or both unknown."]; + e.Status = ValidationStatus.Error; + } + + void ValidateSurname(ValidatorEventArgs e) + { + if(!(e.Value is string surname)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(surname.Length < 1 || + surname.Length > 256) + { + e.ErrorText = L["Surname must be smaller than 256 characters."]; + e.Status = ValidationStatus.Error; + + return; + } + + if(!string.IsNullOrWhiteSpace(_model.Surname) && + !_unknownSurname) + return; + + e.ErrorText = L["Both name and surname must be known and filled, or both unknown."]; + e.Status = ValidationStatus.Error; + } + + void ValidateAlias(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Alias must be smaller than 256 characters."], 256); + + void ValidateDisplayName(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Display name must be smaller than 256 characters."], 256); + + void ValidateBirthDate(ValidatorEventArgs e) + { + if(!(e.Value is DateTime date)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(date.Date >= DateTime.UtcNow.Date) + { + e.ErrorText = L["Birth date must be before today."]; + e.Status = ValidationStatus.Error; + + return; + } + + if(_unknownDeathDate || !_model.DeathDate.HasValue) + return; + + if(date.Date < _model.DeathDate?.Date) + return; + + e.ErrorText = L["Birth date must be before death date."]; + e.Status = ValidationStatus.Error; + } + + void ValidateDeathDate(ValidatorEventArgs e) + { + if(!(e.Value is DateTime date)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(date.Date >= DateTime.UtcNow.Date) + { + e.ErrorText = L["Death date must be before today."]; + e.Status = ValidationStatus.Error; + + return; + } + + if(date.Date > _model.BirthDate.Date) + return; + + e.ErrorText = L["Death date must be after birth date."]; + e.Status = ValidationStatus.Error; + } + + void ValidateWebpage(ValidatorEventArgs e) + { + if(!(e.Value is string webpage)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(webpage.Length < 1 || + webpage.Length > 255) + { + e.ErrorText = L["Webpage must be smaller than 255 characters."]; + e.Status = ValidationStatus.Error; + + return; + } + + var rx = new Regex(_webpageRegex); + Match m = rx.Match(webpage); + + if(m.Success) + return; + + e.Status = ValidationStatus.Error; + } + + void ValidateTwitter(ValidatorEventArgs e) + { + if(!(e.Value is string twitter)) + { + e.Status = ValidationStatus.Error; + + return; + } + + if(twitter.Length < 1 || + twitter.Length > 255) + { + e.ErrorText = L["Twitter handle must be smaller than 255 characters."]; + e.Status = ValidationStatus.Error; + + return; + } + + if(twitter[0] == '@') + return; + + e.ErrorText = L["Invalid Twitter handle."]; + e.Status = ValidationStatus.Error; + } + + void ValidateFacebook(ValidatorEventArgs e) => + Validators.ValidateString(e, L["Facebook username must be smaller than 256 characters."], 256); } } \ No newline at end of file diff --git a/Marechai/Pages/Admin/People.razor b/Marechai/Pages/Admin/People.razor index e0f2a8e4..83c16b9d 100644 --- a/Marechai/Pages/Admin/People.razor +++ b/Marechai/Pages/Admin/People.razor @@ -106,9 +106,7 @@ @L["Details"] - - @L["Edit"] - + @L["Edit"] diff --git a/Marechai/Resources/Services/PeopleService.en.resx b/Marechai/Resources/Services/PeopleService.en.resx new file mode 100644 index 00000000..1223d13d --- /dev/null +++ b/Marechai/Resources/Services/PeopleService.en.resx @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unknown + Unknown, referring to a name + + + Unknown + Unknown, referring to one or more surnames + + + Unknown + Unknown, referring to an alias + + + Unknown + Unknown, referring to a display name + + + Unknown + Unknown, referring to a country + + + Unknown + Unknown, referring to the death date + + + Unknown + Unknown, referring to a webpage + + + Unknown + Unknown, referring to a twitter handle + + + Unknown + Unknown, referring to a facebook username + + \ No newline at end of file diff --git a/Marechai/Resources/Services/PeopleService.es.resx b/Marechai/Resources/Services/PeopleService.es.resx index 2b6d2972..ada0b8d2 100644 --- a/Marechai/Resources/Services/PeopleService.es.resx +++ b/Marechai/Resources/Services/PeopleService.es.resx @@ -188,7 +188,7 @@ Apellido - Surnam + Surname Alias @@ -206,4 +206,132 @@ Volver a la lista Back to list + + Guardar + Save + + + Desconocido + Unknown, referring to a name + + + Desconocidos + Unknown, referring to one or more surnames + + + Desconocido + Unknown, referring to an alias + + + Desconocido + Unknown, referring to a display name + + + Desconocido + Unknown, referring to a country + + + Desconocida + Unknown, referring to the death date + + + Desconocida + Unknown, referring to a webpage + + + Desconocido + Unknown, referring to a twitter handle + + + Desconocido + Unknown, referring to a facebook username + + + Por favor introduce un nombre válido. + Please enter a valid name. + + + Por favor introduce un apellido (o varios) válido. + Please enter a valid surname. + + + Por favor introduce un alias válido. + Please enter a valid alias. + + + Por favor introduce un nombre para mostrar válido. + Please enter a valid display name. + + + Por favor introduce una fecha de nacimiento válida. + Please enter a valid birth date. + + + Por favor introduce una fecha de fallecimiento válida. + Please enter a valid death date. + + + Por favor introduce una página web válida. + Please enter a valid webpage. + + + Por favor introduce un identificador de Twitter válido. + Please enter a valid Twitter handle. + + + Por favor introduce un nombre de usuario de Facebook válido. + Please enter a valid Facebook user name. + + + El nombre debe contener menos de 256 caracteres. + Name must be smaller than 256 characters. + + + Tanto el nombre como el/los apellido(s) deben rellenarse, or ser desconocidos. + Both name and surname must be known and filled, or both unknown. + + + El/los apellido(s) deben contener menos de 256 caracteres. + Surname must be smaller than 256 characters. + + + El alias debe contener menos de 256 caracteres. + Alias must be smaller than 256 characters. + + + El nombre para mostrar debe contener menos de 256 caracteres. + Display name must be smaller than 256 characters. + + + La fecha de nacimiento debe ser anterior a hoy. + Birth date must be before today. + + + La fecha de nacimiento debe ser anterior a la de fallecimiento + Birth date must be before death date. + + + La fecha de fallecimiento debe ser anterior a hoy. + Death date must be before today. + + + La fecha de fallecimiento debe ser posterior a la de nacimiento. + Death date must be after birth date. + + + La página web debe contener menos de 255 caracteres. + Webpage must be smaller than 255 characters. + + + El identificador de Twitter debe contener menos de 255 caracteres. + Twitter handle must be smaller than 255 characters. + + + Identificador de Twitter inválido. + Invalid Twitter handle. + + + El nombre de usuario de Facebook debe contener menos de 256 caracteres. + Facebook username must be smaller than 256 characters. + \ No newline at end of file diff --git a/Marechai/Services/PeopleService.cs b/Marechai/Services/PeopleService.cs index 881d9a42..4de900bf 100644 --- a/Marechai/Services/PeopleService.cs +++ b/Marechai/Services/PeopleService.cs @@ -23,7 +23,35 @@ namespace Marechai.Services DisplayName = p.DisplayName }).ToListAsync(); - public async Task GetAsync(int id) => await _context.People.FindAsync(id); + public async Task GetAsync(int id) => + await _context.People.Where(p => p.Id == id).Select(p => new PersonViewModel + { + Id = p.Id, Name = p.Name, Surname = p.Surname, CountryOfBirthId = p.CountryOfBirthId, + BirthDate = p.BirthDate, DeathDate = p.DeathDate, Webpage = p.Webpage, Twitter = p.Twitter, + Facebook = p.Facebook, Photo = p.Photo, Alias = p.Alias, DisplayName = p.DisplayName + }).FirstOrDefaultAsync(); + + public async Task UpdateAsync(PersonViewModel viewModel) + { + Person model = await _context.People.FindAsync(viewModel.Id); + + if(model is null) + return; + + model.Name = viewModel.Name; + model.Surname = viewModel.Surname; + model.CountryOfBirthId = viewModel.CountryOfBirthId; + model.BirthDate = viewModel.BirthDate; + model.DeathDate = viewModel.DeathDate; + model.Webpage = viewModel.Webpage; + model.Twitter = viewModel.Twitter; + model.Facebook = viewModel.Facebook; + model.Photo = viewModel.Photo; + model.Alias = viewModel.Alias; + model.DisplayName = viewModel.DisplayName; + + await _context.SaveChangesAsync(); + } public async Task DeleteAsync(int id) { diff --git a/Marechai/ViewModels/PersonViewModel.cs b/Marechai/ViewModels/PersonViewModel.cs index 459baa64..415edda2 100644 --- a/Marechai/ViewModels/PersonViewModel.cs +++ b/Marechai/ViewModels/PersonViewModel.cs @@ -4,17 +4,18 @@ namespace Marechai.ViewModels { public class PersonViewModel : BaseViewModel { - public string Name { get; set; } - public string Surname { get; set; } - public string CountryOfBirth { get; set; } - public DateTime BirthDate { get; set; } - public DateTime? DeathDate { get; set; } - public string Webpage { get; set; } - public string Twitter { get; set; } - public string Facebook { get; set; } - public Guid Photo { get; set; } - public string Alias { get; set; } - public string DisplayName { get; set; } + public string Name { get; set; } + public string Surname { get; set; } + public string CountryOfBirth { get; set; } + public DateTime BirthDate { get; set; } + public DateTime? DeathDate { get; set; } + public string Webpage { get; set; } + public string Twitter { get; set; } + public string Facebook { get; set; } + public Guid Photo { get; set; } + public string Alias { get; set; } + public string DisplayName { get; set; } + public short? CountryOfBirthId { get; set; } public string FullName => DisplayName ?? Alias ?? $"{Name} {Surname}"; }