Browse Source

Merge pull request #49 from volosoft/berkan/admin-manage-organizations

Admin: Manage organizations
pull/50/head
Halil İbrahim Kalkan 5 years ago
committed by GitHub
parent
commit
73fa1f3b0b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      src/EventHub.Admin.Application.Contracts/Organizations/IOrganizationAppService.cs
  2. 11
      src/EventHub.Admin.Application.Contracts/Organizations/Memberships/IOrganizationMembershipAppService.cs
  3. 18
      src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberDto.cs
  4. 12
      src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberListFilterDto.cs
  5. 14
      src/EventHub.Admin.Application.Contracts/Organizations/OrganizationInListDto.cs
  6. 15
      src/EventHub.Admin.Application.Contracts/Organizations/OrganizationListFilterDto.cs
  7. 32
      src/EventHub.Admin.Application.Contracts/Organizations/OrganizationProfileDto.cs
  8. 44
      src/EventHub.Admin.Application.Contracts/Organizations/UpdateOrganizationDto.cs
  9. 9
      src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs
  10. 14
      src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs
  11. 13
      src/EventHub.Admin.Application/EventHubAdminAppService.cs
  12. 11
      src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs
  13. 24
      src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs
  14. 53
      src/EventHub.Admin.Application/Organizations/Memberships/OrganizationMembershipAppService.cs
  15. 165
      src/EventHub.Admin.Application/Organizations/OrganizationAppService.cs
  16. 28
      src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs
  17. 56
      src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/OrganizationController.cs
  18. 4
      src/EventHub.Admin.Web/EventHub.Admin.Web.csproj
  19. 5
      src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs
  20. 26
      src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs
  21. 8
      src/EventHub.Admin.Web/Menus/EventHubMenus.cs
  22. 268
      src/EventHub.Admin.Web/Pages/OrganizationManagement.razor
  23. 167
      src/EventHub.Admin.Web/Pages/OrganizationManagement.razor.cs
  24. 78
      src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor
  25. 98
      src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor.cs
  26. 1
      src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs
  27. 2
      src/EventHub.Domain.Shared/EventHubErrorCodes.cs
  28. 35
      src/EventHub.Domain.Shared/Localization/EventHub/en.json
  29. 3
      src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs
  30. 24
      src/EventHub.Domain/Organizations/Memberships/IOrganizationMembershipRepository.cs
  31. 18
      src/EventHub.Domain/Organizations/Memberships/OrganizationMemberWithDetails.cs
  32. 40
      src/EventHub.Domain/Organizations/Memberships/OrganizationMembershipChangedEventHandler.cs
  33. 2
      src/EventHub.Domain/Organizations/Organization.cs
  34. 77
      src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Organizations/Memberships/OrganizationMembershipRepository.cs
  35. 2628
      src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.Designer.cs
  36. 24
      src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.cs
  37. 5
      src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs

20
src/EventHub.Admin.Application.Contracts/Organizations/IOrganizationAppService.cs

@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace EventHub.Admin.Organizations
{
public interface IOrganizationAppService : IApplicationService
{
Task<PagedResultDto<OrganizationInListDto>> GetListAsync(OrganizationListFilterDto input);
Task<OrganizationProfileDto> GetAsync(Guid id);
Task<OrganizationProfileDto> GetByNameAsync(string name);
Task<OrganizationProfileDto> UpdateAsync(Guid id, UpdateOrganizationDto input);
Task DeleteAsync(Guid id);
}
}

11
src/EventHub.Admin.Application.Contracts/Organizations/Memberships/IOrganizationMembershipAppService.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace EventHub.Admin.Organizations.Memberships
{
public interface IOrganizationMembershipAppService : IApplicationService
{
Task<PagedResultDto<OrganizationMemberDto>> GetListAsync(OrganizationMemberListFilterDto input);
}
}

18
src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberDto.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Organizations.Memberships
{
public class OrganizationMemberDto : EntityDto<Guid>
{
public string OrganizationName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}

12
src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberListFilterDto.cs

@ -0,0 +1,12 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Organizations.Memberships
{
public class OrganizationMemberListFilterDto : PagedResultRequestDto
{
public Guid? OrganizationId { get; set; }
public string UserName { get; set; }
}
}

14
src/EventHub.Admin.Application.Contracts/Organizations/OrganizationInListDto.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Organizations
{
public class OrganizationInListDto : EntityDto<Guid>
{
public string Name { get; set; }
public string DisplayName { get; set; }
public int MemberCount { get; set; }
}
}

15
src/EventHub.Admin.Application.Contracts/Organizations/OrganizationListFilterDto.cs

@ -0,0 +1,15 @@
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Organizations
{
public class OrganizationListFilterDto : PagedAndSortedResultRequestDto
{
public string Name { get; set; }
public string DisplayName { get; set; }
public int? MinMemberCount { get; set; }
public int? MaxMemberCount { get; set; }
}
}

32
src/EventHub.Admin.Application.Contracts/Organizations/OrganizationProfileDto.cs

@ -0,0 +1,32 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Organizations
{
public class OrganizationProfileDto : EntityDto<Guid>
{
public string Name { get; set; }
public string DisplayName { get; set; }
public byte[] ProfilePictureContent { get; set; }
public string OwnerUserName { get; set; }
public string OwnerEmail { get; set; }
public string Description { get; set; }
public string Website { get; set; }
public string TwitterUsername { get; set; }
public string GitHubUsername { get; set; }
public string FacebookUsername { get; set; }
public string InstagramUsername { get; set; }
public string MediumUsername { get; set; }
}
}

44
src/EventHub.Admin.Application.Contracts/Organizations/UpdateOrganizationDto.cs

@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations;
using EventHub.Organizations;
using JetBrains.Annotations;
namespace EventHub.Admin.Organizations
{
public class UpdateOrganizationDto
{
[Required]
[StringLength(OrganizationConsts.MaxDisplayNameLength, MinimumLength = OrganizationConsts.MinDisplayNameLength)]
public string DisplayName { get; set; }
[Required]
[StringLength(OrganizationConsts.MaxDescriptionNameLength, MinimumLength = OrganizationConsts.MinDescriptionNameLength)]
public string Description { get; set; }
[CanBeNull]
public byte[] ProfilePictureContent { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxWebsiteLength)]
public string Website { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxTwitterUsernameLength)]
public string TwitterUsername { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxGitHubUsernameLength)]
public string GitHubUsername { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxFacebookUsernameLength)]
public string FacebookUsername { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxInstagramUsernameLength)]
public string InstagramUsername { get; set; }
[CanBeNull]
[StringLength(OrganizationConsts.MaxMediumUsernameLength)]
public string MediumUsername { get; set; }
}
}

9
src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs

@ -8,10 +8,13 @@ namespace EventHub.Admin.Permissions
{ {
public override void Define(IPermissionDefinitionContext context) public override void Define(IPermissionDefinitionContext context)
{ {
var myGroup = context.AddGroup(EventHubPermissions.GroupName); var eventHubGroup = context.AddGroup(EventHubPermissions.GroupName);
//Define your own permissions here. Example: var organizationsPermission = eventHubGroup.AddPermission(EventHubPermissions.Organizations.Default, L("Permission:OrganizationManagement"));
//myGroup.AddPermission(EventHubPermissions.MyPermission1, L("Permission:MyPermission1")); organizationsPermission.AddChild(EventHubPermissions.Organizations.Create, L("Permission:Create"));
organizationsPermission.AddChild(EventHubPermissions.Organizations.Update, L("Permission:Edit"));
organizationsPermission.AddChild(EventHubPermissions.Organizations.Delete, L("Permission:Delete"));
organizationsPermission.AddChild(EventHubPermissions.Organizations.Memberships.Default, L("Permission:MembershipManagement"));
} }
private static LocalizableString L(string name) private static LocalizableString L(string name)

14
src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs

@ -4,7 +4,17 @@
{ {
public const string GroupName = "EventHub"; public const string GroupName = "EventHub";
//Add your own permission names. Example: public static class Organizations
//public const string MyPermission1 = GroupName + ".MyPermission1"; {
public const string Default = GroupName + ".Organizations";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public static class Memberships
{
public const string Default = Organizations.Default + ".Memberships";
}
}
} }
} }

13
src/EventHub.Admin.Application/EventHubAdminAppService.cs

@ -0,0 +1,13 @@
using EventHub.Localization;
using Volo.Abp.Application.Services;
namespace EventHub.Admin
{
public class EventHubAdminAppService : ApplicationService
{
protected EventHubAdminAppService()
{
LocalizationResource = typeof(EventHubResource);
}
}
}

11
src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.Account; using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement;
@ -10,9 +11,17 @@ namespace EventHub.Admin
typeof(AbpAccountApplicationModule), typeof(AbpAccountApplicationModule),
typeof(EventHubAdminApplicationContractsModule), typeof(EventHubAdminApplicationContractsModule),
typeof(AbpIdentityApplicationModule), typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule) typeof(AbpPermissionManagementApplicationModule),
typeof(AbpAutoMapperModule)
)] )]
public class EventHubAdminApplicationModule : AbpModule public class EventHubAdminApplicationModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<EventHubAdminApplicationAutoMapperProfile>(validate: true);
});
}
} }
} }

24
src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs

@ -0,0 +1,24 @@
using AutoMapper;
using EventHub.Admin.Organizations;
using EventHub.Admin.Organizations.Memberships;
using EventHub.Organizations;
using EventHub.Organizations.Memberships;
using Volo.Abp.AutoMapper;
namespace EventHub.Admin
{
public class EventHubAdminApplicationAutoMapperProfile : Profile
{
public EventHubAdminApplicationAutoMapperProfile()
{
CreateMap<Organization, OrganizationInListDto>();
CreateMap<Organization, OrganizationProfileDto>()
.Ignore(x => x.OwnerUserName)
.Ignore(x => x.OwnerEmail)
.Ignore(x => x.ProfilePictureContent);
CreateMap<OrganizationMemberWithDetails, OrganizationMemberDto>();
}
}
}

53
src/EventHub.Admin.Application/Organizations/Memberships/OrganizationMembershipAppService.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EventHub.Admin.Permissions;
using EventHub.Organizations.Memberships;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
namespace EventHub.Admin.Organizations.Memberships
{
[Authorize(EventHubPermissions.Organizations.Memberships.Default)]
public class OrganizationMembershipAppService : ApplicationService, IOrganizationMembershipAppService
{
private readonly IOrganizationMembershipRepository _organizationMembershipRepository;
private readonly IRepository<IdentityUser, Guid> _userRepository;
public OrganizationMembershipAppService(
IOrganizationMembershipRepository organizationMembershipRepository,
IRepository<IdentityUser, Guid> userRepository)
{
_organizationMembershipRepository = organizationMembershipRepository;
_userRepository = userRepository;
}
public async Task<PagedResultDto<OrganizationMemberDto>> GetListAsync(OrganizationMemberListFilterDto input)
{
IdentityUser user = null;
if (!input.UserName.IsNullOrWhiteSpace())
{
input.UserName = input.UserName.ToLower();
user = await _userRepository.SingleOrDefaultAsync(x => x.UserName.ToLower() == input.UserName);
if (user is null)
{
throw new BusinessException(EventHubErrorCodes.UserNotFound)
.WithData("UserName", input.UserName);
}
}
var members = await _organizationMembershipRepository.GetMemberListAsync(input.OrganizationId, user?.Id, input.SkipCount, input.MaxResultCount);
var totalCount = await _organizationMembershipRepository.GetCountAsync(input.OrganizationId, user?.Id);
return new PagedResultDto<OrganizationMemberDto>(
totalCount,
ObjectMapper.Map<List<OrganizationMemberWithDetails>, List<OrganizationMemberDto>>(members)
);
}
}
}

165
src/EventHub.Admin.Application/Organizations/OrganizationAppService.cs

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using EventHub.Admin.Permissions;
using EventHub.Organizations;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.BlobStoring;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
namespace EventHub.Admin.Organizations
{
[Authorize(EventHubPermissions.Organizations.Default)]
public class OrganizationAppService : EventHubAdminAppService, IOrganizationAppService
{
private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly IRepository<IdentityUser, Guid> _identityUserRepository;
private readonly IBlobContainer<OrganizationProfilePictureContainer> _organizationBlobContainer;
public OrganizationAppService(
IRepository<Organization, Guid> organizationRepository,
IRepository<IdentityUser, Guid> identityUserRepository,
IBlobContainer<OrganizationProfilePictureContainer> organizationBlobContainer)
{
_organizationRepository = organizationRepository;
_identityUserRepository = identityUserRepository;
_organizationBlobContainer = organizationBlobContainer;
}
public async Task<PagedResultDto<OrganizationInListDto>> GetListAsync(OrganizationListFilterDto input)
{
var query = await _organizationRepository.GetQueryableAsync();
if (!input.Name.IsNullOrWhiteSpace())
{
input.Name = input.Name.ToLower();
query = query.Where(o => o.Name.ToLower().Contains(input.Name));
}
if (!input.DisplayName.IsNullOrWhiteSpace())
{
input.DisplayName = input.DisplayName.ToLower();
query = query.Where(o => o.DisplayName.ToLower().Contains(input.DisplayName));
}
if (input.MinMemberCount != null)
{
query = query.Where(o => o.MemberCount >= input.MinMemberCount);
}
if (input.MaxMemberCount != null)
{
query = query.Where(o => o.MemberCount <= input.MaxMemberCount);
}
var totalCount = await AsyncExecuter.CountAsync(query);
query = query.OrderBy(string.IsNullOrWhiteSpace(input.Sorting)
? OrganizationConsts.DefaultSorting
: input.Sorting);
query = query.PageBy(input);
var organizationListDto = ObjectMapper
.Map<List<Organization>, List<OrganizationInListDto>>(await AsyncExecuter.ToListAsync(query));
return new PagedResultDto<OrganizationInListDto>(totalCount, organizationListDto);
}
public async Task<OrganizationProfileDto> GetAsync(Guid id)
{
var organization = await _organizationRepository.GetAsync(id);
var dto = ObjectMapper.Map<Organization, OrganizationProfileDto>(organization);
var user = await _identityUserRepository.GetAsync(organization.OwnerUserId);
dto.OwnerUserName = user.UserName;
dto.OwnerEmail = user.Email;
dto.ProfilePictureContent = await GetCoverImageAsync(organization.Id);
return dto;
}
public async Task<OrganizationProfileDto> GetByNameAsync(string name)
{
var organization = await _organizationRepository.FindAsync(x => x.Name.ToLower() == name.ToLower());
if (organization is null)
{
throw new BusinessException(EventHubErrorCodes.OrganizationNotFound)
.WithData("OrganizationName", name);
}
var dto = ObjectMapper.Map<Organization, OrganizationProfileDto>(organization);
var user = await _identityUserRepository.GetAsync(organization.OwnerUserId);
dto.OwnerUserName = user.UserName;
dto.OwnerEmail = user.Email;
dto.ProfilePictureContent = await GetCoverImageAsync(organization.Id);
return dto;
}
[Authorize(EventHubPermissions.Organizations.Update)]
public async Task<OrganizationProfileDto> UpdateAsync(Guid id, UpdateOrganizationDto input)
{
var organization = await _organizationRepository.GetAsync(id);
organization.SetDisplayName(input.DisplayName);
organization.SetDescription(input.Description);
organization.Website = input.Website;
organization.TwitterUsername = input.TwitterUsername;
organization.GitHubUsername = input.GitHubUsername;
organization.FacebookUsername = input.FacebookUsername;
organization.InstagramUsername = input.InstagramUsername;
organization.MediumUsername = input.MediumUsername;
if (input.ProfilePictureContent?.Length > 0)
{
await SaveCoverImageAsync(organization.Id, input.ProfilePictureContent);
}
else
{
await DeleteCoverImageAsync(organization.Id);
}
await _organizationRepository.UpdateAsync(organization);
return ObjectMapper.Map<Organization, OrganizationProfileDto>(organization);
}
[Authorize(EventHubPermissions.Organizations.Delete)]
public async Task DeleteAsync(Guid id)
{
await _organizationRepository.DeleteAsync(id);
}
private async Task<byte[]> GetCoverImageAsync(Guid id)
{
var blobName = id.ToString();
return await _organizationBlobContainer.GetAllBytesOrNullAsync(blobName);
}
private async Task SaveCoverImageAsync(Guid id, byte[] coverImageContent)
{
var blobName = id.ToString();
await _organizationBlobContainer.SaveAsync(blobName, coverImageContent, overrideExisting: true);
}
private async Task DeleteCoverImageAsync(Guid id)
{
var blobName = id.ToString();
await _organizationBlobContainer.DeleteAsync(blobName);
}
}
}

28
src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs

@ -0,0 +1,28 @@
using System.Threading.Tasks;
using EventHub.Admin.Organizations.Memberships;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Admin.Controllers.Organizations.Memberships
{
[Controller]
[Area("eventhub-admin")]
[ControllerName("OrganizationMembership")]
[Route("/api/eventhub/organization-memberships")]
public class OrganizationMembershipController : AbpController, IOrganizationMembershipAppService
{
private readonly IOrganizationMembershipAppService _organizationMembershipAppService;
public OrganizationMembershipController(IOrganizationMembershipAppService organizationMembershipAppService)
{
_organizationMembershipAppService = organizationMembershipAppService;
}
[HttpGet]
public async Task<PagedResultDto<OrganizationMemberDto>> GetListAsync(OrganizationMemberListFilterDto input)
{
return await _organizationMembershipAppService.GetListAsync(input);
}
}
}

56
src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/OrganizationController.cs

@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using EventHub.Admin.Organizations;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Admin.Controllers.Organizations
{
[Controller]
[Area("eventhub-admin")]
[ControllerName("Organization")]
[Route("/api/eventhub/organizations")]
public class OrganizationController : AbpController, IOrganizationAppService
{
private readonly IOrganizationAppService _organizationAppService;
public OrganizationController(IOrganizationAppService organizationAppService)
{
_organizationAppService = organizationAppService;
}
[HttpGet]
public Task<PagedResultDto<OrganizationInListDto>> GetListAsync(OrganizationListFilterDto input)
{
return _organizationAppService.GetListAsync(input);
}
[HttpGet]
[Route("{id}")]
public Task<OrganizationProfileDto> GetAsync(Guid id)
{
return _organizationAppService.GetAsync(id);
}
[HttpGet]
[Route("by-name/{name}")]
public Task<OrganizationProfileDto> GetByNameAsync(string name)
{
return _organizationAppService.GetByNameAsync(name);
}
[HttpPut]
public Task<OrganizationProfileDto> UpdateAsync(Guid id, UpdateOrganizationDto input)
{
return _organizationAppService.UpdateAsync(id, input);
}
[HttpDelete]
[Route("{id}")]
public Task DeleteAsync(Guid id)
{
return _organizationAppService.DeleteAsync(id);
}
}
}

4
src/EventHub.Admin.Web/EventHub.Admin.Web.csproj

@ -11,8 +11,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Blazorise.Bootstrap" Version="0.9.3-preview6" /> <PackageReference Include="Blazorise.Bootstrap" Version="0.9.3.5" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="0.9.3-preview6" /> <PackageReference Include="Blazorise.Icons.FontAwesome" Version="0.9.3.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.*" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.*" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.*" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.*" />
</ItemGroup> </ItemGroup>

5
src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs

@ -1,4 +1,5 @@
using AutoMapper; using AutoMapper;
using EventHub.Admin.Organizations;
namespace EventHub.Admin.Web namespace EventHub.Admin.Web
{ {
@ -6,7 +7,7 @@ namespace EventHub.Admin.Web
{ {
public EventHubBlazorAutoMapperProfile() public EventHubBlazorAutoMapperProfile()
{ {
//Define your AutoMapper configuration here for the Blazor project. CreateMap<OrganizationProfileDto, UpdateOrganizationDto>();
} }
} }
} }

26
src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs

@ -1,9 +1,12 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Admin.Permissions;
using EventHub.Localization; using EventHub.Localization;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Volo.Abp.Account.Localization; using Volo.Abp.Account.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.UI.Navigation; using Volo.Abp.UI.Navigation;
using Volo.Abp.Users; using Volo.Abp.Users;
@ -30,7 +33,7 @@ namespace EventHub.Admin.Web.Menus
} }
} }
private Task ConfigureMainMenuAsync(MenuConfigurationContext context) private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{ {
var l = context.GetLocalizer<EventHubResource>(); var l = context.GetLocalizer<EventHubResource>();
@ -44,7 +47,7 @@ namespace EventHub.Admin.Web.Menus
) )
); );
return Task.CompletedTask; await AddOrganizationMenu(context, l);
} }
private Task ConfigureUserMenuAsync(MenuConfigurationContext context) private Task ConfigureUserMenuAsync(MenuConfigurationContext context)
@ -67,5 +70,22 @@ namespace EventHub.Admin.Web.Menus
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task AddOrganizationMenu(MenuConfigurationContext context, IStringLocalizer l)
{
var organizationMenu = new ApplicationMenuItem(EventHubMenus.OrganizationManagement.GroupName,
l["Menu:OrganizationManagement"], icon: "fa fa-sitemap");
context.Menu.Items.Insert(2, organizationMenu);
organizationMenu.AddItem(
new ApplicationMenuItem(EventHubMenus.OrganizationManagement.Organizations, l["Menu:Organizations"],
url: "/organizations").RequirePermissions(EventHubPermissions.Organizations.Default));
organizationMenu.AddItem(
new ApplicationMenuItem(EventHubMenus.OrganizationManagement.OrganizationMemberships,
l["Menu:OrganizationMemberships"], url: "/organization-memberships")
.RequirePermissions(EventHubPermissions.Organizations.Memberships.Default));
return Task.CompletedTask;
}
} }
} }

8
src/EventHub.Admin.Web/Menus/EventHubMenus.cs

@ -5,7 +5,11 @@
private const string Prefix = "EventHub"; private const string Prefix = "EventHub";
public const string Home = Prefix + ".Home"; public const string Home = Prefix + ".Home";
//Add your menu items here... public static class OrganizationManagement
{
public const string GroupName = Prefix + ".OrganizationManagement";
public const string Organizations = GroupName + ".Organizations";
public const string OrganizationMemberships = GroupName + ".OrganizationMemberships";
}
} }
} }

268
src/EventHub.Admin.Web/Pages/OrganizationManagement.razor

@ -0,0 +1,268 @@
@page "/organizations"
@using Volo.Abp.BlazoriseUI.Components
@using EventHub.Admin.Permissions
@using Microsoft.AspNetCore.Authorization
@using EventHub.Admin.Organizations
@using EventHub.Organizations
@using Volo.Abp.AspNetCore.Components.Notifications
@inherits EventHubComponentBase
@inject NavigationManager NavigationManager
@attribute [Authorize(EventHubPermissions.Organizations.Default)]
@inject IOrganizationAppService OrganizationAppService
@inject IUiNotificationService UiNotificationService
<Card>
<CardHeader>
@* ************************* PAGE HEADER ************************* *@
<h2>@L["Organizations"]</h2>
</CardHeader>
@* ************************* SEARCH ************************* *@
<CardBody>
<Form>
<div id="FilterSection" class="row mt-3">
<Column ColumnSize="ColumnSize.Is3">
<Field>
<FieldLabel>@L["Name"]</FieldLabel>
<TextEdit TValue="string" KeyPress="OnKeyPress" @bind-Text="@Filter.Name"/>
</Field>
</Column>
<Column ColumnSize="ColumnSize.Is3">
<Field>
<FieldLabel>@L["DisplayName"]</FieldLabel>
<TextEdit TValue="string" KeyPress="OnKeyPress" @bind-Text="@Filter.DisplayName"/>
</Field>
</Column>
<Column ColumnSize="ColumnSize.Is3">
<Field>
<FieldLabel>@L["MinMemberCount"]</FieldLabel>
<NumericEdit TValue="int?" KeyPress="OnKeyPress" @bind-Value="@Filter.MinMemberCount"/>
</Field>
</Column>
<Column ColumnSize="ColumnSize.Is3">
<Field>
<FieldLabel>@L["MaxMemberCount"]</FieldLabel>
<NumericEdit TValue="int?" KeyPress="OnKeyPress" @bind-Value="@Filter.MaxMemberCount"/>
</Field>
</Column>
</div>
</Form>
@* ************************* DATA GRID ************************* *@
<DataGrid TItem="OrganizationInListDto"
Data="OrganizationList"
ReadData="OnDataGridReadAsync"
TotalItems="TotalCount"
ShowPager="true"
PageSize="PageSize"
Responsive="true">
<DataGridColumns>
<DataGridEntityActionsColumn TItem="OrganizationInListDto">
<DisplayTemplate>
<EntityActions TItem="OrganizationInListDto">
<EntityAction TItem="OrganizationInListDto"
RequiredPolicy="@EventHubPermissions.Organizations.Memberships.Default"
Clicked="@(() => NavigationManager.NavigateTo($"/organization-memberships/{context.Id}"))"
Text="@L["Members"]">
</EntityAction>
<EntityAction TItem="OrganizationInListDto"
RequiredPolicy="@EventHubPermissions.Organizations.Update"
Clicked="() => OpenEditOrganizationModal(context)"
Text="@L["Edit"]">
</EntityAction>
<EntityAction TItem="OrganizationInListDto"
RequiredPolicy="@EventHubPermissions.Organizations.Delete"
Clicked="() => DeleteOrganizationAsync(context)"
ConfirmationMessage="@(() => L["DeleteConfirmationMessage"])"
Text="@L["Delete"]">
</EntityAction>
</EntityActions>
</DisplayTemplate>
</DataGridEntityActionsColumn>
<DataGridColumn TItem="OrganizationInListDto"
Field="Name"
Caption="@L["Name"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationInListDto"
Field="DisplayName"
Caption="@L["DisplayName"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationInListDto"
Field="MemberCount"
Caption="@L["MemberCount"]">
</DataGridColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
@* ************************* EDIT MODAL ************************* *@
<Modal @ref="EditOrganizationModal" Closing="@OnEditModalClosing">
<ModalContent Centered="true" Size="ModalSize.Large">
<Form id="EditOrganizationForm">
<ModalHeader>
<ModalTitle>@L["UpdateOrganization"]</ModalTitle>
<CloseButton Clicked="() => EditOrganizationModal.Hide()"/>
</ModalHeader>
<ModalBody>
<Tabs SelectedTab="@SelectedTabInEditModal" SelectedTabChanged="@OnSelectedTabChangedInEditModal">
<Items>
<Tab Name="@TabContentInEditModal.OrganizationProfile.ToString()">@L["OrganizationInfo"]</Tab>
<Tab Name="@TabContentInEditModal.ProfileImage.ToString()">@L["ProfileImage"]</Tab>
</Items>
<Content>
<TabPanel Name="@TabContentInEditModal.OrganizationProfile.ToString()">
<Validations Mode="ValidationMode.Auto" Model="@EditOrganizationModal" ValidateOnLoad="false">
<Validation>
<Field>
<FieldLabel>@L["DisplayName"] *</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.DisplayName">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["Description"] *</FieldLabel>
<MemoEdit Rows="4" TValue="string" @bind-Text="@EditingOrganization.Description">
<Feedback>
<ValidationError/>
</Feedback>
</MemoEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["Website"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.Website">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["TwitterUsername"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.TwitterUsername">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["GitHubUsername"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.GitHubUsername">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["FacebookUsername"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.FacebookUsername">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["InstagramUsername"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.InstagramUsername">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["MediumUsername"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingOrganization.MediumUsername">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
</Validations>
</TabPanel>
<TabPanel Name="@TabContentInEditModal.ProfileImage.ToString()">
@if (!IsLoadingProfileImage)
{
@if (!ProfileImageUrl.IsNullOrEmpty())
{
<div class="text-center mt-4">
<img src="@ProfileImageUrl" class="organization-profile-img" width="600" height="340" alt="profile-image"/>
</div>
}
}
else
{
<div class="d-flex justify-content-center mt-4">
<div class="spinner-border" role="status">
<span class="sr-only">@L["Loading"]...</span>
</div>
</div>
}
<div class="mt-3 mb-2">
<Field Horizontal="true">
<FieldLabel ColumnSize="ColumnSize.Is3">@L["ChooseProfileImage"]</FieldLabel>
<FieldBody ColumnSize="ColumnSize.Is6">
<FileEdit MaxMessageSize="@OrganizationConsts.MaxProfilePictureFileSize" Filter=".jpg, .png" Changed="@OnProfileImageFileChanged" Progressed="@OnProgressedForProfileImage" AutoReset="true"/>
</FieldBody>
<Column ColumnSize="ColumnSize.Is3">
@if (!ProfileImageUrl.IsNullOrWhiteSpace())
{
<Button Color="Color.Warning" Clicked="@OnDeleteCoverImageButtonClicked">@L["DeleteCoverImage"]</Button>
}
else
{
<Button disabled Color="Color.Secondary" Clicked="@OnDeleteCoverImageButtonClicked">@L["DeleteCoverImage"]</Button>
}
</Column>
</Field>
</div>
</TabPanel>
</Content>
</Tabs>
</ModalBody>
<ModalFooter>
<Button Color="Color.Secondary"
Clicked="() => EditOrganizationModal.Hide()">
@L["Cancel"]
</Button>
<Button Type="ButtonType.Submit" PreventDefaultOnSubmit="true" Color="Color.Primary"
Clicked="UpdateOrganizationAsync">
<i class="fa-check fa"></i> @L["Save"]
</Button>
</ModalFooter>
</Form>
</ModalContent>
</Modal>

167
src/EventHub.Admin.Web/Pages/OrganizationManagement.razor.cs

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Blazorise;
using Blazorise.DataGrid;
using EventHub.Admin.Organizations;
using Microsoft.AspNetCore.Components.Web;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Web.Pages
{
public partial class OrganizationManagement
{
private IReadOnlyList<OrganizationInListDto> OrganizationList { get; set; }
private OrganizationListFilterDto Filter { get; set; }
private int CurrentPage { get; set; }
private string CurrentSorting { get; set; }
private int TotalCount { get; set; }
private int PageSize { get; }
private OrganizationProfileDto Organization { get; set; }
private string ProfileImageUrl { get; set; }
private Guid EditingOrganizationId { get; set; }
private UpdateOrganizationDto EditingOrganization { get; set; }
private Modal EditOrganizationModal { get; set; }
private IFileEntry FileEntry { get; set; }
private bool IsLoadingProfileImage { get; set; }
private string SelectedTabInEditModal { get; set; }
public OrganizationManagement()
{
Filter = new OrganizationListFilterDto();
PageSize = LimitedResultRequestDto.DefaultMaxResultCount;
OrganizationList = new List<OrganizationInListDto>();
EditingOrganization = new UpdateOrganizationDto();
IsLoadingProfileImage = false;
SelectedTabInEditModal = TabContentInEditModal.OrganizationProfile.ToString();
}
protected override async Task OnInitializedAsync()
{
await GetOrganizationsAsync();
}
private async Task OnDataGridReadAsync(DataGridReadDataEventArgs<OrganizationInListDto> e)
{
CurrentSorting = e.Columns
.Where(c => c.Direction != SortDirection.None)
.Select(c => c.Field + (c.Direction == SortDirection.Descending ? " DESC" : ""))
.JoinAsString(",");
CurrentPage = e.Page - 1;
await GetOrganizationsAsync();
await InvokeAsync(StateHasChanged);
}
private async Task GetOrganizationsAsync()
{
Filter.MaxResultCount = PageSize;
Filter.SkipCount = CurrentPage * PageSize;
Filter.Sorting = CurrentSorting;
var result = await OrganizationAppService.GetListAsync(Filter);
OrganizationList = result.Items;
TotalCount = (int) result.TotalCount;
}
private async Task OpenEditOrganizationModal(OrganizationInListDto input)
{
EditingOrganizationId = input.Id;
Organization = await OrganizationAppService.GetAsync(EditingOrganizationId);
FillProfileImageUrl(Organization.ProfilePictureContent);
EditingOrganization = ObjectMapper.Map<OrganizationProfileDto, UpdateOrganizationDto>(Organization);
EditOrganizationModal.Show();
}
private async Task UpdateOrganizationAsync()
{
await OrganizationAppService.UpdateAsync(EditingOrganizationId, EditingOrganization);
await GetOrganizationsAsync();
EditOrganizationModal.Hide();
}
private void OnDeleteCoverImageButtonClicked()
{
EditingOrganization.ProfilePictureContent = null;
FileEntry = new FileEntry();
ProfileImageUrl = null;
IsLoadingProfileImage = false;
}
private async Task DeleteOrganizationAsync(OrganizationInListDto organization)
{
await OrganizationAppService.DeleteAsync(organization.Id);
await GetOrganizationsAsync();
}
private async Task OnProfileImageFileChanged(FileChangedEventArgs e)
{
FileEntry = e.Files.FirstOrDefault();
if (FileEntry is null)
{
return;
}
IsLoadingProfileImage = true;
using (var stream = new MemoryStream())
{
await FileEntry.WriteToStreamAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
EditingOrganization.ProfilePictureContent = stream.ToArray();
FillProfileImageUrl(EditingOrganization.ProfilePictureContent);
await InvokeAsync(StateHasChanged);
}
}
private void FillProfileImageUrl(byte[] content)
{
if (content != null)
{
var imageBase64Data = Convert.ToBase64String(content);
var imageDataUrl = $"data:image/png;base64,{imageBase64Data}";
ProfileImageUrl = imageDataUrl;
}
}
private void OnProgressedForProfileImage(FileProgressedEventArgs e)
{
if (e.Percentage == 100D)
{
IsLoadingProfileImage = false;
}
}
private void OnSelectedTabChangedInEditModal(string name)
{
SelectedTabInEditModal = name;
}
private void OnEditModalClosing(CancelEventArgs e)
{
IsLoadingProfileImage = false;
SelectedTabInEditModal = TabContentInEditModal.OrganizationProfile.ToString();
}
private async Task OnKeyPress(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
await GetOrganizationsAsync();
}
}
}
public enum TabContentInEditModal : byte
{
OrganizationProfile = 0,
ProfileImage
}
}

78
src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor

@ -0,0 +1,78 @@
@page "/organization-memberships/{OrganizationId:guid?}"
@using Volo.Abp.BlazoriseUI.Components
@using EventHub.Admin.Organizations.Memberships
@using EventHub.Admin.Permissions
@using Microsoft.AspNetCore.Authorization
@inherits EventHubComponentBase
@attribute [Authorize(EventHubPermissions.Organizations.Memberships.Default)]
@inject IOrganizationMembershipAppService OrganizationMembershipAppService
<Card>
<CardHeader>
@* ************************* HEADER ************************* *@
<h2>
@L["OrganizationMemberships"]
@if (OrganizationId.HasValue && !OrganizationName.IsNullOrWhiteSpace())
{
<span> (@OrganizationName)</span>
}
</h2>
</CardHeader>
@* ************************* SEARCH ************************* *@
<CardBody>
<div id="FilterSection" class="mt-3 ml-2">
<Row>
<Column ColumnSize="ColumnSize.Is6">
<Field>
<FieldLabel>@L["OrganizationName"]</FieldLabel>
<TextEdit TValue="string" KeyPress="OnKeyPress" @bind-Text="@OrganizationName"/>
</Field>
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Field>
<FieldLabel>@L["UserName"]</FieldLabel>
<TextEdit TValue="string" KeyPress="OnKeyPress" @bind-Text="@Filter.UserName"/>
</Field>
</Column>
</Row>
</div>
@* ************************* DATA GRID ************************* *@
<DataGrid TItem="OrganizationMemberDto"
Data="OrganizationMemberList"
ReadData="OnDataGridReadAsync"
TotalItems="TotalCount"
ShowPager="true"
PageSize="PageSize"
Responsive="true"
Sortable="false">
<DataGridColumns>
<DataGridColumn TItem="OrganizationMemberDto"
Field="OrganizationName"
Caption="@L["OrganizationName"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationMemberDto"
Field="UserName"
Caption="@L["UserName"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationMemberDto"
Field="Email"
Caption="@L["Email"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationMemberDto"
Field="Name"
Caption="@L["Name"]">
</DataGridColumn>
<DataGridColumn TItem="OrganizationMemberDto"
Field="Surname"
Caption="@L["Surname"]">
</DataGridColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>

98
src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor.cs

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Blazorise.DataGrid;
using EventHub.Admin.Organizations;
using EventHub.Admin.Organizations.Memberships;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Web.Pages
{
public partial class OrganizationMembershipManagement
{
[Parameter]
public Guid? OrganizationId { get; set; }
private string OrganizationName { get; set; }
[Inject]
protected IOrganizationAppService OrganizationAppService { get; set; }
private IReadOnlyList<OrganizationMemberDto> OrganizationMemberList { get; set; }
private OrganizationMemberListFilterDto Filter { get; set; }
private int CurrentPage { get; set; }
private int TotalCount { get; set; }
private int PageSize { get; }
public OrganizationMembershipManagement()
{
Filter = new OrganizationMemberListFilterDto();
PageSize = LimitedResultRequestDto.DefaultMaxResultCount;
OrganizationMemberList = new List<OrganizationMemberDto>();
}
protected override async Task OnInitializedAsync()
{
await GetOrganizationMembershipsAsync();
if (OrganizationId.HasValue)
{
OrganizationName = (await OrganizationAppService.GetAsync(OrganizationId.Value)).Name;
}
}
private async Task OnDataGridReadAsync(DataGridReadDataEventArgs<OrganizationMemberDto> e)
{
CurrentPage = e.Page - 1;
await GetOrganizationMembershipsAsync();
await InvokeAsync(StateHasChanged);
}
private async Task GetOrganizationMembershipsAsync()
{
Filter.MaxResultCount = PageSize;
Filter.SkipCount = CurrentPage * PageSize;
if (OrganizationId.HasValue)
{
Filter.OrganizationId = OrganizationId!.Value;
}
else
{
Filter.OrganizationId = null;
}
if (!Filter.UserName.IsNullOrWhiteSpace())
{
Filter.UserName = Filter.UserName.Trim();
}
var result = await OrganizationMembershipAppService.GetListAsync(Filter);
OrganizationMemberList = result.Items;
TotalCount = (int) result.TotalCount;
}
private async Task OnKeyPress(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
if (OrganizationName.IsNullOrWhiteSpace())
{
OrganizationId = null;
OrganizationName = null;
}
else
{
var organization = await OrganizationAppService.GetByNameAsync(OrganizationName.Trim());
OrganizationId = organization.Id;
OrganizationName = organization.Name;
}
await GetOrganizationMembershipsAsync();
}
}
}
}

1
src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Users;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;

2
src/EventHub.Domain.Shared/EventHubErrorCodes.cs

@ -11,5 +11,7 @@
public const string CapacityCantBeLowerThanRegisteredUserCount = "EventHub:CapacityCantBeLowerThanRegisteredUserCount"; public const string CapacityCantBeLowerThanRegisteredUserCount = "EventHub:CapacityCantBeLowerThanRegisteredUserCount";
public const string NotAuthorizedToUpdateEvent = "EventHub:NotAuthorizedToUpdateEvent"; public const string NotAuthorizedToUpdateEvent = "EventHub:NotAuthorizedToUpdateEvent";
public const string CantChangeEventTiming = "EventHub:CantChangeEventTiming"; public const string CantChangeEventTiming = "EventHub:CantChangeEventTiming";
public const string UserNotFound = "EventHub:UserNotFound";
public const string OrganizationNotFound = "EventHub:OrganizationNotFound";
} }
} }

35
src/EventHub.Domain.Shared/Localization/EventHub/en.json

@ -82,6 +82,39 @@
"EventTimingPreview": "Event Timing Preview", "EventTimingPreview": "Event Timing Preview",
"CoverImagePreview": "Cover Image Preview", "CoverImagePreview": "Cover Image Preview",
"EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!", "EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!",
"PastEvent": "Past Event" "PastEvent": "Past Event",
"Menu:OrganizationManagement": "Organization Management",
"Menu:OrganizationMemberships": "Memberships",
"Permission:OrganizationManagement": "Organization Management",
"Permission:MembershipManagement": "Membership Management",
"Permission:Create": "Create",
"Permission:Edit": "Edit",
"Permission:Delete": "Delete",
"UserName": "User name",
"Email": "Email",
"Name": "Name",
"Surname": "Surname",
"DisplayName": "Display Name",
"MinMemberCount": "Min Member Count",
"MaxMemberCount": "Max Member Count",
"MemberCount": "Member Count",
"UpdateOrganization": "Update Organization",
"Description": "Description",
"Website": "Website",
"TwitterUsername": "Twitter Username",
"GitHubUsername": "GitHub Username",
"FacebookUsername": "Facebook Username",
"InstagramUsername": "Instagram Username",
"MediumUsername": "Medium Username",
"ChooseProfileImage": "Choose profile image:",
"OrganizationMemberships": "Organization Memberships",
"EventHub:UserNotFound": "There is no user named \"{UserName}\"",
"EventHub:OrganizationNotFound": "There is no organization named \"{OrganizationName}\"",
"DeleteCoverImage": "Delete Cover Image",
"DeleteConfirmationMessage": "Are you sure you want to delete this item?",
"Loading": "Loading",
"OrganizationInfo": "Organization Info",
"ProfileImage": "Profile Image",
"OrganizationName": "Organization Name"
} }
} }

3
src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs

@ -2,6 +2,8 @@
{ {
public static class OrganizationConsts public static class OrganizationConsts
{ {
public const string DefaultSorting = "CreationTime desc";
public const int MinNameLength = 2; public const int MinNameLength = 2;
public const int MaxNameLength = 32; public const int MaxNameLength = 32;
@ -24,6 +26,5 @@
public const int MaxInstagramUsernameLength = 24; public const int MaxInstagramUsernameLength = 24;
public const int MaxMediumUsernameLength = 24; public const int MaxMediumUsernameLength = 24;
} }
} }

24
src/EventHub.Domain/Organizations/Memberships/IOrganizationMembershipRepository.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
namespace EventHub.Organizations.Memberships
{
public interface IOrganizationMembershipRepository : IRepository<OrganizationMembership, Guid>
{
Task<List<OrganizationMemberWithDetails>> GetMemberListAsync(
Guid? organizationId,
Guid? userId,
int skipCount,
int maxResultCount,
CancellationToken cancellationToken = default);
Task<int> GetCountAsync(
Guid? organizationId,
Guid? userId,
CancellationToken cancellationToken = default);
}
}

18
src/EventHub.Domain/Organizations/Memberships/OrganizationMemberWithDetails.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.Domain.Entities;
namespace EventHub.Organizations.Memberships
{
public class OrganizationMemberWithDetails : Entity<Guid>
{
public string OrganizationName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}

40
src/EventHub.Domain/Organizations/Memberships/OrganizationMembershipChangedEventHandler.cs

@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
using EventHub.Organizations;
using EventHub.Organizations.Memberships;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;
namespace EventHub.Domain.Organizations.Memberships
{
public class OrganizationMembershipChangedEventHandler : ILocalEventHandler<EntityCreatedEventData<OrganizationMembership>>,
ILocalEventHandler<EntityDeletedEventData<OrganizationMembership>>, ITransientDependency
{
private readonly IRepository<Organization, Guid> _organizationRepository;
public OrganizationMembershipChangedEventHandler(IRepository<Organization, Guid> organizationRepository)
{
_organizationRepository = organizationRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<OrganizationMembership> eventData)
{
var organization = await _organizationRepository.GetAsync(eventData.Entity.OrganizationId);
organization.MemberCount++;
await _organizationRepository.UpdateAsync(organization);
}
public async Task HandleEventAsync(EntityDeletedEventData<OrganizationMembership> eventData)
{
var organization = await _organizationRepository.GetAsync(eventData.Entity.OrganizationId);
organization.MemberCount--;
await _organizationRepository.UpdateAsync(organization);
}
}
}

2
src/EventHub.Domain/Organizations/Organization.cs

@ -26,6 +26,8 @@ namespace EventHub.Organizations
public string MediumUsername { get; set; } public string MediumUsername { get; set; }
public int MemberCount { get; internal set; }
private Organization() private Organization()
{ {
} }

77
src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Organizations/Memberships/OrganizationMembershipRepository.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EventHub.Organizations;
using EventHub.Organizations.Memberships;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Identity;
namespace EventHub.EntityFrameworkCore.Organizations.Memberships
{
public class OrganizationMembershipRepository : EfCoreRepository<EventHubDbContext, OrganizationMembership, Guid>,
IOrganizationMembershipRepository
{
public OrganizationMembershipRepository(IDbContextProvider<EventHubDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<List<OrganizationMemberWithDetails>> GetMemberListAsync(
Guid? organizationId,
Guid? userId,
int skipCount,
int maxResultCount,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var organizationMembershipsDbSet = dbContext.Set<OrganizationMembership>();
var identityUserDbSet = dbContext.Set<IdentityUser>();
var organizationDbSet = dbContext.Set<Organization>();
var query = organizationMembershipsDbSet
.Join(organizationDbSet, t => t.OrganizationId, organization => organization.Id,
(organizationMember, organization) => new {organizationMember, organization})
.Join(identityUserDbSet, organizationWithMember => organizationWithMember.organizationMember.UserId,
user => user.Id, (organizationWithMember, user) => new {organizationWithMember, user})
.WhereIf(userId.HasValue, t => t.user.Id == userId)
.WhereIf(organizationId.HasValue,
t => t.organizationWithMember.organizationMember.OrganizationId == organizationId)
.OrderByDescending(o => o.organizationWithMember.organizationMember.CreationTime)
.Select(t => new OrganizationMemberWithDetails
{
OrganizationName = t.organizationWithMember.organization.Name,
UserName = t.user.UserName,
Email = t.user.Email,
Name = t.user.Name,
Surname = t.user.Surname
});
return await query
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<int> GetCountAsync(
Guid? organizationId,
Guid? userId,
CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
var organizationMembershipsDbSet = dbContext.Set<OrganizationMembership>();
var identityUserDbSet = dbContext.Set<IdentityUser>();
return await organizationMembershipsDbSet
.Join(identityUserDbSet, organizationMember => organizationMember.UserId, user => user.Id,
(organizationMember, user) => new {organizationMember, user})
.WhereIf(userId.HasValue, t => t.user.Id == userId)
.WhereIf(organizationId.HasValue, t => t.organizationMember.OrganizationId == organizationId)
.CountAsync(GetCancellationToken(cancellationToken));
}
}
}

2628
src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.Designer.cs

File diff suppressed because it is too large

24
src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace EventHub.Migrations
{
public partial class Added_MemberCount_To_Organization : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "MemberCount",
table: "EhOrganizations",
type: "integer",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "MemberCount",
table: "EhOrganizations");
}
}
}

5
src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs

@ -18,7 +18,7 @@ namespace EventHub.Migrations
modelBuilder modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("Relational:MaxIdentifierLength", 63) .HasAnnotation("Relational:MaxIdentifierLength", 63)
.HasAnnotation("ProductVersion", "5.0.7") .HasAnnotation("ProductVersion", "5.0.8")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
modelBuilder.Entity("EventHub.Countries.Country", b => modelBuilder.Entity("EventHub.Countries.Country", b =>
@ -310,6 +310,9 @@ namespace EventHub.Migrations
b.Property<string>("MediumUsername") b.Property<string>("MediumUsername")
.HasColumnType("text"); .HasColumnType("text");
b.Property<int>("MemberCount")
.HasColumnType("integer");
b.Property<string>("Name") b.Property<string>("Name")
.IsRequired() .IsRequired()
.HasMaxLength(32) .HasMaxLength(32)

Loading…
Cancel
Save