diff --git a/src/EventHub.Application.Contracts/Events/IEventAppService.cs b/src/EventHub.Application.Contracts/Events/IEventAppService.cs index 433a476..07db0ad 100644 --- a/src/EventHub.Application.Contracts/Events/IEventAppService.cs +++ b/src/EventHub.Application.Contracts/Events/IEventAppService.cs @@ -17,5 +17,9 @@ namespace EventHub.Events Task GetLocationAsync(Guid id); Task> GetCountriesLookupAsync(); + + Task IsEventOwnerAsync(Guid id); + + Task UpdateAsync(Guid id, UpdateEventDto input); } } diff --git a/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs b/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs new file mode 100644 index 0000000..b768cd5 --- /dev/null +++ b/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs @@ -0,0 +1,36 @@ +using System; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace EventHub.Events +{ + public class UpdateEventDto + { + [Required] + [StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)] + public string Title { get; set; } + + [Required] + [StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)] + public string Description { get; set; } + + public bool IsOnline { get; set; } + + [CanBeNull] + [StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)] + public string OnlineLink { get; set; } + + public Guid? CountryId { get; set; } + + [CanBeNull] + [StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)] + public string City { get; set; } + + [CanBeNull] + [StringLength(EventConsts.MaxLanguageLength, MinimumLength = EventConsts.MinLanguageLength)] + public string Language { get; set; } + + [Range(1, int.MaxValue)] + public int? Capacity { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Application/Events/EventAppService.cs b/src/EventHub.Application/Events/EventAppService.cs index 4fcb8ae..5b325f4 100644 --- a/src/EventHub.Application/Events/EventAppService.cs +++ b/src/EventHub.Application/Events/EventAppService.cs @@ -171,5 +171,39 @@ namespace EventHub.Events return ObjectMapper.Map, List>(countries); } + + public async Task IsEventOwnerAsync(Guid id) + { + var @event = await _eventRepository.GetAsync(id); + var organization = await _organizationRepository.GetAsync(@event.OrganizationId); + + return CurrentUser.Id.HasValue && organization.OwnerUserId == CurrentUser.GetId(); + } + + public async Task UpdateAsync(Guid id, UpdateEventDto input) + { + var @event = await _eventRepository.GetAsync(id); + var organization = await _organizationRepository.GetAsync(@event.OrganizationId); + + if (organization.OwnerUserId != CurrentUser.GetId()) + { + throw new BusinessException(EventHubErrorCodes.NotAuthorizedToUpdateEventProfile) + .WithData("EventTitle", @event.Title); + } + + var updatedEvent = await _eventManager.UpdateAsync( + id, + input.CountryId, + input.Title, + input.Description, + input.Language, + input.IsOnline, + input.OnlineLink, + input.City, + input.Capacity + ); + + await _eventRepository.UpdateAsync(updatedEvent); + } } } \ No newline at end of file diff --git a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs index f5ddc5b..e8dcd99 100644 --- a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs +++ b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs @@ -8,5 +8,7 @@ public const string CantRegisterOrUnregisterForAPastEvent = "EventHub:CantRegisterOrUnregisterForAPastEvent"; public const string NotAuthorizedToUpdateOrganizationProfile = "EventHub:NotAuthorizedToUpdateOrganizationProfile"; public const string CapacityOfEventFull = "EventHub:CapacityOfEventFull"; + public const string CapacityCantBeLowerThanRegisteredUserCount = "EventHub:CapacityCantBeLowerThanRegisteredUserCount"; + public const string NotAuthorizedToUpdateEventProfile = "EventHub:NotAuthorizedToUpdateEventProfile"; } } diff --git a/src/EventHub.Domain.Shared/Localization/EventHub/en.json b/src/EventHub.Domain.Shared/Localization/EventHub/en.json index 3042806..2c97ceb 100644 --- a/src/EventHub.Domain.Shared/Localization/EventHub/en.json +++ b/src/EventHub.Domain.Shared/Localization/EventHub/en.json @@ -61,6 +61,8 @@ "AttendeesCanSeeEventLocation": "Only attendees can see the event's {0}.", "LoginToSeeLocation": "Login to see location", "LocationHasNotSpecifiedYet": "Event {0} has not been specified yet.", - "Language": "Language" + "Language": "Language", + "EventHub:CapacityCantBeLowerThanRegisteredUserCount": "Capacity can not be lower than the currently registered user count.", + "EventHub:NotAuthorizedToUpdateEventProfile": "You are not authorized to update the \"{EventTitle}\" event." } } diff --git a/src/EventHub.Domain/Events/Event.cs b/src/EventHub.Domain/Events/Event.cs index b79b422..42633aa 100644 --- a/src/EventHub.Domain/Events/Event.cs +++ b/src/EventHub.Domain/Events/Event.cs @@ -94,6 +94,8 @@ namespace EventHub.Events OnlineLink = onlineLink; } + CountryId = null; + City = null; return this; } @@ -103,6 +105,7 @@ namespace EventHub.Events City = city; } + OnlineLink = null; return this; } } diff --git a/src/EventHub.Domain/Events/EventManager.cs b/src/EventHub.Domain/Events/EventManager.cs index d6f7703..1bbb0f1 100644 --- a/src/EventHub.Domain/Events/EventManager.cs +++ b/src/EventHub.Domain/Events/EventManager.cs @@ -1,6 +1,10 @@ using System; +using System.Linq; using System.Threading.Tasks; +using EventHub.Events.Registrations; using EventHub.Organizations; +using Volo.Abp; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; namespace EventHub.Events @@ -8,10 +12,18 @@ namespace EventHub.Events public class EventManager : DomainService { private readonly EventUrlCodeGenerator _eventUrlCodeGenerator; + private readonly IRepository _eventRepository; + private readonly IRepository _eventRegistrationRepository; - public EventManager(EventUrlCodeGenerator eventUrlCodeGenerator) + public EventManager( + EventUrlCodeGenerator eventUrlCodeGenerator, + IRepository eventRepository, + IRepository eventRegistrationRepository + ) { _eventUrlCodeGenerator = eventUrlCodeGenerator; + _eventRepository = eventRepository; + _eventRegistrationRepository = eventRegistrationRepository; } public async Task CreateAsync( @@ -31,5 +43,34 @@ namespace EventHub.Events description ); } + + public async Task UpdateAsync( + Guid id, + Guid? countryId, + string title, + string description, + string language, + bool isOnline, + string onlineLink, + string city, + int? capacity + ) + { + var @event = await _eventRepository.GetAsync(id); + var registeredUserCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); + + if (capacity < registeredUserCount) + { + throw new BusinessException(EventHubErrorCodes.CapacityCantBeLowerThanRegisteredUserCount); + } + + @event.SetTitle(title); + @event.SetDescription(description); + @event.SetLocation(isOnline, onlineLink, countryId, city); + @event.Capacity = capacity; + @event.Language = language; + + return @event; + } } } diff --git a/src/EventHub.Web/EventHubWebAutoMapperProfile.cs b/src/EventHub.Web/EventHubWebAutoMapperProfile.cs index d0e9a0b..dbc4988 100644 --- a/src/EventHub.Web/EventHubWebAutoMapperProfile.cs +++ b/src/EventHub.Web/EventHubWebAutoMapperProfile.cs @@ -2,6 +2,7 @@ using EventHub.Events; using EventHub.Organizations; using EventHub.Web.Pages.Organizations; +using EditPageModel = EventHub.Web.Pages.Events.EditPageModel; namespace EventHub.Web { @@ -13,6 +14,8 @@ namespace EventHub.Web CreateMap(); CreateMap(); CreateMap(); + CreateMap(); + CreateMap(); } } } diff --git a/src/EventHub.Web/Pages/Events/Detail.cshtml b/src/EventHub.Web/Pages/Events/Detail.cshtml index 25886c0..ac086ea 100644 --- a/src/EventHub.Web/Pages/Events/Detail.cshtml +++ b/src/EventHub.Web/Pages/Events/Detail.cshtml @@ -14,7 +14,21 @@ @Model.Event.Title -

@Model.Event.Title

+ @if (Model.IsEventOwner) + { +
+
+

@Model.Event.Title

+
+ +
+ } + else + { +

@Model.Event.Title

+ }
@EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime)
diff --git a/src/EventHub.Web/Pages/Events/Detail.cshtml.cs b/src/EventHub.Web/Pages/Events/Detail.cshtml.cs index 7721093..5a3e021 100644 --- a/src/EventHub.Web/Pages/Events/Detail.cshtml.cs +++ b/src/EventHub.Web/Pages/Events/Detail.cshtml.cs @@ -11,6 +11,8 @@ namespace EventHub.Web.Pages.Events [BindProperty(SupportsGet = true)] public string Url { get; set; } + public bool IsEventOwner { get; set; } + public EventDetailDto Event { get; set; } private readonly IEventAppService _eventAppService; @@ -43,6 +45,8 @@ namespace EventHub.Web.Pages.Events ); } + IsEventOwner = await _eventAppService.IsEventOwnerAsync(Event.Id); + return Page(); } } diff --git a/src/EventHub.Web/Pages/Events/Edit.cshtml b/src/EventHub.Web/Pages/Events/Edit.cshtml new file mode 100644 index 0000000..a17c4ed --- /dev/null +++ b/src/EventHub.Web/Pages/Events/Edit.cshtml @@ -0,0 +1,49 @@ +@page "/event/edit/{url}" +@inject IHtmlLocalizer L +@using EventHub.Localization +@using Microsoft.AspNetCore.Mvc.Localization +@model EventHub.Web.Pages.Events.EditPageModel + +@section scripts { + +} + +
+

Edit Event

+ + +
+
+
+ + + + + + + +
+ + +
+ + + + + + +
+
+ //TODO +
+
+
\ No newline at end of file diff --git a/src/EventHub.Web/Pages/Events/Edit.cshtml.cs b/src/EventHub.Web/Pages/Events/Edit.cshtml.cs new file mode 100644 index 0000000..abcae34 --- /dev/null +++ b/src/EventHub.Web/Pages/Events/Edit.cshtml.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using EventHub.Events; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using NUglify.Helpers; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; + +namespace EventHub.Web.Pages.Events +{ + public class EditPageModel : EventHubPageModel + { + [BindProperty(SupportsGet = true)] + public string Url { get; set; } + + [BindProperty] + public EditEventViewModel Event { get; set; } + + public List Countries { get; private set; } + public List Languages { get; private set; } + + private readonly IEventAppService _eventAppService; + + public EditPageModel(IEventAppService eventAppService) + { + _eventAppService = eventAppService; + } + + public async Task OnGetAsync() + { + var urlCode = EventUrlCodeHelper.GetCodeFromUrl(Url); + + var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(urlCode); + Event = ObjectMapper.Map(eventDetailDto); + + FillLanguages(); + await FillCountriesAsync(); + await GetLocationInfoAsync(); + } + + public async Task OnPostAsync() + { + try + { + ValidateModel(); + + var input = ObjectMapper.Map(Event); + await _eventAppService.UpdateAsync(Event.Id, input); + + return RedirectToPage("./Detail", new { url = Url }); + } + catch (Exception exception) + { + ShowAlert(exception); + FillLanguages(); + await FillCountriesAsync(); + return Page(); + } + } + + private async Task FillCountriesAsync() + { + var result = await _eventAppService.GetCountriesLookupAsync(); + + Countries = result.Select( + country => new SelectListItem + { + Value = country.Id.ToString(), + Text = country.Name + } + ).ToList(); + } + + private void FillLanguages() + { + var result = CultureInfo.GetCultures(CultureTypes.NeutralCultures) + .DistinctBy(x => x.EnglishName) + .OrderBy(x => x.EnglishName) + .ToList(); + result.Remove(result.Single(x => x.TwoLetterISOLanguageName == "iv")); // Invariant Language + + Languages = result.Select( + cultureInfo => new SelectListItem + { + Value = cultureInfo.TwoLetterISOLanguageName, + Text = cultureInfo.EnglishName + } + ).ToList(); + } + + private async Task GetLocationInfoAsync() + { + var eventLocationDto = await _eventAppService.GetLocationAsync(Event.Id); + Event.City = eventLocationDto.City; + Event.OnlineLink = eventLocationDto.OnlineLink; + + Event.CountryId = Guid.TryParse( + Countries.FirstOrDefault(x => x.Text == eventLocationDto.Country)?.Value, + out var countryId + ) + ? countryId + : null; + } + + public class EditEventViewModel + { + [HiddenInput] + public Guid Id { get; set; } + + [Required] + [StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)] + public string Title { get; set; } + + [Required] + [StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)] + [TextArea] + public string Description { get; set; } + + public bool IsOnline { get; set; } + + [CanBeNull] + [StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)] + public string OnlineLink { get; set; } + + [SelectItems(nameof(Countries))] + [DisplayName("Country")] + public Guid? CountryId { get; set; } + + [CanBeNull] + [StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)] + public string City { get; set; } + + [SelectItems(nameof(Languages))] + [DisplayName("Language")] + public string Language { get; set; } + + [Range(1, int.MaxValue)] + public int? Capacity { get; set; } + } + } +} \ No newline at end of file diff --git a/src/EventHub.Web/Pages/Events/Edit.js b/src/EventHub.Web/Pages/Events/Edit.js new file mode 100644 index 0000000..7e78206 --- /dev/null +++ b/src/EventHub.Web/Pages/Events/Edit.js @@ -0,0 +1,19 @@ +$(function () { + $("#Event_CountryId").prepend(""); + $("#Event_Language").prepend(""); + + if($("#Event_IsOnline").is(":checked")) { + $("#event-link-group").show(); + $("#event-location-group").hide(); + } + + $('#Event_IsOnline').click(function() { + if ($(this).is(':checked')) { + $("#event-link-group").show(); + $("#event-location-group").hide(); + }else{ + $("#event-link-group").hide(); + $("#event-location-group").show(); + } + }); +}); \ No newline at end of file diff --git a/src/EventHub.Web/Pages/Organizations/Edit.cshtml b/src/EventHub.Web/Pages/Organizations/Edit.cshtml index da45267..08e7943 100644 --- a/src/EventHub.Web/Pages/Organizations/Edit.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Edit.cshtml @@ -5,9 +5,7 @@ @model EventHub.Web.Pages.Organizations.EditPageModel @section scripts { - - - + }