Browse Source

Merge pull request #19 from volosoft/EngincanV/organization-profile-picture

Add profile picture section for Organizations
pull/21/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
54c4c6a5f0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs
  2. 2
      src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs
  3. 2
      src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs
  4. 56
      src/EventHub.Application/Organizations/OrganizationAppService.cs
  5. 1
      src/EventHub.Domain.Shared/EventHub.Domain.Shared.csproj
  6. 6
      src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs
  7. 8
      src/EventHub.Domain.Shared/Localization/EventHub/en.json
  8. 2
      src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs
  9. 1
      src/EventHub.Domain/EventHub.Domain.csproj
  10. 6
      src/EventHub.Domain/EventHubDomainModule.cs
  11. 10
      src/EventHub.Domain/Organizations/OrganizationProfilePictureContainer.cs
  12. 6
      src/EventHub.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/EventHubMigrationsDbContext.cs
  13. 2624
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210209143744_Added_Blob_Database.Designer.cs
  14. 73
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210209143744_Added_Blob_Database.cs
  15. 5162
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs
  16. 6
      src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubEntityFrameworkCoreModule.cs
  17. 1
      src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj
  18. 40
      src/EventHub.Web/Controllers/OrganizationController.cs
  19. 38
      src/EventHub.Web/Helpers/AllowedExtensionsAttribute.cs
  20. 36
      src/EventHub.Web/Helpers/MaxFileSizeAttribute.cs
  21. 54
      src/EventHub.Web/Pages/Organizations/Edit.cshtml
  22. 13
      src/EventHub.Web/Pages/Organizations/Edit.cshtml.cs
  23. 118
      src/EventHub.Web/Pages/Organizations/Edit.js
  24. 9
      src/EventHub.Web/Pages/Organizations/Index.cshtml
  25. 15
      src/EventHub.Web/Pages/Organizations/Profile.cshtml
  26. 2
      test/EventHub.Application.Tests/Organizations/OrganizationAppServiceTests.cs

2
src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs

@ -18,5 +18,7 @@ namespace EventHub.Organizations
Task<bool> IsOrganizationOwnerAsync(Guid organizationId);
Task UpdateAsync(Guid id, UpdateOrganizationDto input);
Task SaveProfilePictureAsync(Guid id, byte[] bytes);
}
}

2
src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs

@ -10,5 +10,7 @@ namespace EventHub.Organizations
public string DisplayName { get; set; }
public string Description { get; set; }
public byte[] ProfilePictureContent { get; set; }
}
}

2
src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs

@ -22,5 +22,7 @@ namespace EventHub.Organizations
public string InstagramUsername { get; set; }
public string MediumUsername { get; set; }
public byte[] ProfilePictureContent { get; set; }
}
}

56
src/EventHub.Application/Organizations/OrganizationAppService.cs

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.BlobStoring;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users;
@ -14,13 +15,17 @@ namespace EventHub.Organizations
{
private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly OrganizationManager _organizationManager;
private readonly IBlobContainer<OrganizationProfilePictureContainer> _organizationBlobContainer;
public OrganizationAppService(
IRepository<Organization, Guid> organizationRepository,
OrganizationManager organizationManager)
OrganizationManager organizationManager,
IBlobContainer<OrganizationProfilePictureContainer> organizationBlobContainer
)
{
_organizationRepository = organizationRepository;
_organizationManager = organizationManager;
_organizationBlobContainer = organizationBlobContainer;
}
[Authorize]
@ -45,16 +50,27 @@ namespace EventHub.Organizations
nameof(Organization.DisplayName)
);
var organizationDto = ObjectMapper.Map<List<Organization>, List<OrganizationInListDto>>(organizations);
foreach (var organization in organizationDto)
{
organization.ProfilePictureContent = await GetProfilePictureAsync(organization.Id);
}
return new PagedResultDto<OrganizationInListDto>(
organizationCount,
ObjectMapper.Map<List<Organization>, List<OrganizationInListDto>>(organizations)
organizationDto
);
}
public async Task<OrganizationProfileDto> GetProfileAsync(string name)
{
var organization = await _organizationRepository.GetAsync(o => o.Name == name);
return ObjectMapper.Map<Organization, OrganizationProfileDto>(organization);
var organizationProfileDto = ObjectMapper.Map<Organization, OrganizationProfileDto>(organization);
organizationProfileDto.ProfilePictureContent = await GetProfilePictureAsync(organizationProfileDto.Id);
return organizationProfileDto;
}
[Authorize]
@ -64,9 +80,14 @@ namespace EventHub.Organizations
var query = (await _organizationRepository.GetQueryableAsync()).Where(o => o.OwnerUserId == currentUserId);
var organizations = await AsyncExecuter.ToListAsync(query);
return new ListResultDto<OrganizationInListDto>(
ObjectMapper.Map<List<Organization>, List<OrganizationInListDto>>(organizations)
);
var organizationDto = ObjectMapper.Map<List<Organization>, List<OrganizationInListDto>>(organizations);
foreach (var organization in organizationDto)
{
organization.ProfilePictureContent = await GetProfilePictureAsync(organization.Id);
}
return new ListResultDto<OrganizationInListDto>(organizationDto);
}
public async Task<bool> IsOrganizationOwnerAsync(Guid organizationId)
@ -96,5 +117,28 @@ namespace EventHub.Organizations
await _organizationRepository.UpdateAsync(organization);
}
[Authorize]
public async Task SaveProfilePictureAsync(Guid id, byte[] bytes)
{
var organization = await _organizationRepository.GetAsync(x => x.Id == id);
if (organization.OwnerUserId != CurrentUser.GetId())
{
throw new BusinessException(EventHubErrorCodes.NotAuthorizedToUpdateOrganizationProfile)
.WithData("Name", organization.DisplayName);
}
var blobName = id.ToString();
await _organizationBlobContainer.SaveAsync(blobName, bytes, overrideExisting: true);
}
private async Task<byte[]> GetProfilePictureAsync(Guid id)
{
var blobName = id.ToString();
return await _organizationBlobContainer.GetAllBytesOrNullAsync(blobName);
}
}
}

1
src/EventHub.Domain.Shared/EventHub.Domain.Shared.csproj

@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring.Database.Domain.Shared" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.Identity.Domain.Shared" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.IdentityServer.Domain.Shared" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Domain.Shared" Version="4.2.0-rc.1" />

6
src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs

@ -1,4 +1,4 @@
using EventHub.Localization;
using EventHub.Localization;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Identity;
@ -10,6 +10,7 @@ using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.BlobStoring.Database;
namespace EventHub
{
@ -19,7 +20,8 @@ namespace EventHub
typeof(AbpIdentityDomainSharedModule),
typeof(AbpIdentityServerDomainSharedModule),
typeof(AbpPermissionManagementDomainSharedModule),
typeof(AbpSettingManagementDomainSharedModule)
typeof(AbpSettingManagementDomainSharedModule),
typeof(BlobStoringDatabaseDomainSharedModule)
)]
public class EventHubDomainSharedModule : AbpModule
{

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

@ -48,6 +48,14 @@
"PersonalWebsite": "Personal Website",
"SocialMedia": "Social Media",
"EventHub:NotAuthorizedToUpdateOrganizationProfile": "You are not authorized to update the \"{OrganizationName}\" organization.",
"OrganizationProfilePictureEditSuccessMessage": "The organization's profile picture has been changed successfully.",
"ChooseOrganizationProfilePicture": "Choose organization profile picture...",
"OrganizationProfilePictureSizeExceedMessage": "The file is too large to upload. Max file size: 1MB.",
"OrganizationProfilePictureExtensionNotAllowed": "This file extension is not allowed. Allowed extensions: png, jpg, jpeg",
"ProfileInfo": "Profile Info",
"ProfilePicture": "Profile Picture",
"OrganizationProfilePictureInfo": "Upload 16:9 aspect ratio pictures for the best view. Maximum file size: 1MB.",
"Close": "Close",
"EventHub:CapacityOfEventFull": "\"{EventTitle}\" event capacity is full!",
"GoToEventAddress": "Go to Event Address",
"AttendeesCanSeeEventLocation": "Only attendees can see the event's {0}.",

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

@ -10,5 +10,7 @@
public const int MinDescriptionNameLength = 50;
public const int MaxDescriptionNameLength = 1000;
public const int MaxProfilePictureFileSize = 1024 * 1024; //1MB
}
}

1
src/EventHub.Domain/EventHub.Domain.csproj

@ -12,6 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring.Database.Domain" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.Emailing" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.Identity.Domain" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" Version="4.2.0-rc.1" />

6
src/EventHub.Domain/EventHubDomainModule.cs

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
@ -9,6 +9,7 @@ using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.IdentityServer;
using Volo.Abp.SettingManagement;
using Volo.Abp.BlobStoring.Database;
namespace EventHub
{
@ -21,7 +22,8 @@ namespace EventHub
typeof(AbpIdentityServerDomainModule),
typeof(AbpPermissionManagementDomainIdentityServerModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpEmailingModule)
typeof(AbpEmailingModule),
typeof(BlobStoringDatabaseDomainModule)
)]
public class EventHubDomainModule : AbpModule
{

10
src/EventHub.Domain/Organizations/OrganizationProfilePictureContainer.cs

@ -0,0 +1,10 @@
using Volo.Abp.BlobStoring;
namespace EventHub.Organizations
{
[BlobContainerName("organization-profile-pictures")]
public class OrganizationProfilePictureContainer
{
}
}

6
src/EventHub.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/EventHubMigrationsDbContext.cs

@ -1,4 +1,4 @@
using EventHub.Events.Registrations;
using EventHub.Events.Registrations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
@ -7,7 +7,8 @@ using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
namespace EventHub.EntityFrameworkCore
{
@ -40,6 +41,7 @@ namespace EventHub.EntityFrameworkCore
/* Configure your own tables/entities inside the ConfigureEventHub method */
builder.ConfigureEventHub(true);
builder.ConfigureBlobStoring();
}
}
}

2624
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210209143744_Added_Blob_Database.Designer.cs

File diff suppressed because it is too large

73
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210209143744_Added_Blob_Database.cs

@ -0,0 +1,73 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EventHub.Migrations
{
public partial class Added_Blob_Database : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpBlobContainers",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobContainers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpBlobs",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ContainerId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Content = table.Column<byte[]>(type: "varbinary(max)", maxLength: 2147483647, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobs", x => x.Id);
table.ForeignKey(
name: "FK_AbpBlobs_AbpBlobContainers_ContainerId",
column: x => x.ContainerId,
principalTable: "AbpBlobContainers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AbpBlobContainers_TenantId_Name",
table: "AbpBlobContainers",
columns: new[] { "TenantId", "Name" });
migrationBuilder.CreateIndex(
name: "IX_AbpBlobs_ContainerId",
table: "AbpBlobs",
column: "ContainerId");
migrationBuilder.CreateIndex(
name: "IX_AbpBlobs_TenantId_ContainerId_Name",
table: "AbpBlobs",
columns: new[] { "TenantId", "ContainerId", "Name" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpBlobs");
migrationBuilder.DropTable(
name: "AbpBlobContainers");
}
}
}

5162
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs

File diff suppressed because it is too large

6
src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubEntityFrameworkCoreModule.cs

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
@ -8,6 +8,7 @@ using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
namespace EventHub.EntityFrameworkCore
{
@ -19,7 +20,8 @@ namespace EventHub.EntityFrameworkCore
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpBackgroundJobsEntityFrameworkCoreModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule)
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(BlobStoringDatabaseEntityFrameworkCoreModule)
)]
public class EventHubEntityFrameworkCoreModule : AbpModule
{

1
src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring.Database.EntityFrameworkCore" Version="4.2.0-rc.1" />
<ProjectReference Include="..\EventHub.Domain\EventHub.Domain.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="4.2.0-rc.1" />

40
src/EventHub.Web/Controllers/OrganizationController.cs

@ -0,0 +1,40 @@
using System.IO;
using System.Threading.Tasks;
using EventHub.Organizations;
using EventHub.Web.Pages.Organizations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Web.Controllers
{
[Route("api/organization")]
public class OrganizationController : AbpController
{
private readonly IOrganizationAppService _organizationAppService;
public OrganizationController(IOrganizationAppService organizationAppService)
{
_organizationAppService = organizationAppService;
}
[HttpPost]
[Authorize]
[Route("save-profile-picture")]
public async Task SaveProfilePicture([FromForm] OrganizationProfilePictureInput input)
{
byte[] profilePictureContent = new byte[] {};
if (input.ProfilePictureFile != null && input.ProfilePictureFile.Length > 0)
{
using (var memoryStream = new MemoryStream())
{
await input.ProfilePictureFile.CopyToAsync(memoryStream);
profilePictureContent = memoryStream.ToArray();
}
}
await _organizationAppService.SaveProfilePictureAsync(input.OrganizationId, profilePictureContent);
}
}
}

38
src/EventHub.Web/Helpers/AllowedExtensionsAttribute.cs

@ -0,0 +1,38 @@
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Http;
namespace EventHub.Web.Helpers
{
public class AllowedExtensionsAttribute : ValidationAttribute
{
private readonly string[] _allowedExtensions;
public AllowedExtensionsAttribute(string[] allowedExtensions)
{
_allowedExtensions = allowedExtensions;
}
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value == null)
{
return ValidationResult.Success;
}
var file = value as IFormFile;
var extension = Path.GetExtension(file.FileName);
if (file.Length > 0 && file != null)
{
if (!_allowedExtensions.Contains(extension.ToLower()))
{
return new ValidationResult("This file extension is not allowed. Allowed extensions: " + string.Join(",", _allowedExtensions));
}
}
return ValidationResult.Success;
}
}
}

36
src/EventHub.Web/Helpers/MaxFileSizeAttribute.cs

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http;
namespace EventHub.Web.Helpers
{
public class MaxFileSizeAttribute : ValidationAttribute
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
if (value == null)
{
return ValidationResult.Success;
}
var file = value as IFormFile;
if (file.Length > 0)
{
if (file.Length > _maxFileSize)
{
return new ValidationResult("Maximum file size: " + _maxFileSize);
}
}
return ValidationResult.Success;
}
}
}

54
src/EventHub.Web/Pages/Organizations/Edit.cshtml

@ -4,7 +4,59 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model EventHub.Web.Pages.Organizations.EditPageModel
@section scripts {
<abp-script-bundle>
<abp-script src="/Pages/Organizations/Edit.js"/>
</abp-script-bundle>
}
<div class="container mb-4">
<h1>@L["EditOrganization"]</h1>
<abp-dynamic-form abp-model="Organization" submit-button="true"/>
<ul class="nav nav-pills mt-3 mb-3" id="pills-tab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="profile-info-tab" data-toggle="pill" href="#profile-info" role="tab" aria-controls="profile-info" aria-selected="true">@L["ProfileInfo"]</a>
</li>
<li class="nav-item">
<a class="nav-link" id="profile-picture-tab" data-toggle="pill" href="#profile-picture" role="tab" aria-controls="profile-picture" aria-selected="false">@L["ProfilePicture"]</a>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="profile-info" role="tabpanel" aria-labelledby="profile-info-tab">
<abp-dynamic-form abp-model="Organization" submit-button="true"/>
</div>
<div class="tab-pane fade" id="profile-picture" role="tabpanel" aria-labelledby="profile-picture-tab">
<form method="post" id="EditOrganizationProfileForm" enctype="multipart/form-data">
<input type="hidden" id="organization-id" value="@Model.Organization.Id"/>
<input type="hidden" id="organization-name" value="@Model.Name" />
<abp-row>
<abp-column size="_12" v-align="Center">
<div class="form-group">
<label>@L["ProfilePicture"]</label>
<div class="custom-file">
<input id="organization-profile-picture" type="file" class="custom-file-input form-control">
<label id="choose-cover-image" class="custom-file-label" for="customFile">@L["ChooseOrganizationProfilePicture"]</label>
<small id="Organization_ProfilePictureInfoText" class="form-text text-muted mt-2">@L["OrganizationProfilePictureInfo"]</small>
</div>
</div>
</abp-column>
</abp-row>
<button id="btnSubmit" type="submit" class="btn btn-primary btn-block">
@L["Submit"]
</button>
</form>
<div class="modal fade" id="picturePreviewModal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<img class="img-fluid" id="img-upload" alt=""/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-block" data-dismiss="modal">@L["Close"]</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

13
src/EventHub.Web/Pages/Organizations/Edit.cshtml.cs

@ -2,7 +2,9 @@ using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using EventHub.Organizations;
using EventHub.Web.Helpers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace EventHub.Web.Pages.Organizations
@ -78,4 +80,15 @@ namespace EventHub.Web.Pages.Organizations
public string MediumUsername { get; set; }
}
public class OrganizationProfilePictureInput
{
[Required]
public Guid OrganizationId { get; set; }
[DataType(DataType.Upload)]
[MaxFileSize(OrganizationConsts.MaxProfilePictureFileSize)]
[AllowedExtensions(new string[] { ".jpg", ".png", ".jpeg" })]
public IFormFile ProfilePictureFile { get; set; }
}
}

118
src/EventHub.Web/Pages/Organizations/Edit.js

@ -0,0 +1,118 @@
$(function () {
var l = abp.localization.getResource('EventHub');
var fileInput = document.getElementById("organization-profile-picture");
var file;
fileInput.addEventListener('change', function () {
var showModal = true;
file = fileInput.files[0];
if(file == undefined) {
$("#choose-cover-image").html(l("ChooseOrganizationProfilePicture"));
return;
}
else {
$("#choose-cover-image").html(file.name);
}
var permittedExtensions = ["jpg", "jpeg", "png"]
var fileExtension = $(this).val().split('.').pop();
if (permittedExtensions.indexOf(fileExtension) === -1) {
showModal = false;
abp.message.error(l("OrganizationProfilePictureExtensionNotAllowed"))
.then(() => {
$("#choose-cover-image").html(l("ChooseOrganizationProfilePicture"));
file = null;
});
}
//1mb
else if(file.size > 1024 * 1024) {
showModal = false;
abp.message.error(l("OrganizationProfilePictureSizeExceedMessage"))
.then(() => {
$("#choose-cover-image").html(l("ChooseOrganizationProfilePicture"));
file = null;
});
}
var img = new Image();
img.onload = function () {
var imageError = true;
var sizes = {
width: this.width,
height: this.height
};
URL.revokeObjectURL(this.src);
if(showModal && imageError) {
readURL(file);
$("#picturePreviewModal").modal('show');
}
}
var objectURL = URL.createObjectURL(file);
img.src = objectURL;
});
function readURL(input) {
if (input) {
var reader = new FileReader();
reader.onload = function (e) {
$('#img-upload').attr('src', e.target.result);
}
reader.readAsDataURL(input);
}
}
$("form#EditOrganizationProfileForm").submit(function (e) {
e.preventDefault();
var organizationId = document.getElementById("organization-id").value;
var organizationName = document.getElementById("organization-name").value;
if(file === undefined) {
file = null;
}
var formData = new FormData();
formData.append("OrganizationId", organizationId);
formData.append("ProfilePictureFile", file);
$.ajax({
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentage = evt.loaded / evt.total;
percentage = parseInt(percentage * 100);
if(percentage !== 100){
$('#btnSubmit').prop("disabled", true);
}
}
}, false);
return xhr;
},
url: `/api/organization/save-profile-picture`,
data: formData,
type: 'POST',
contentType: false,
processData: false,
success: function(data){
abp.message
.success(l("OrganizationProfilePictureEditSuccessMessage"))
.then(data => window.location.href = "/Organizations/" + organizationName);
},
error: function (data) {
abp.message.error(data.responseJSON.error.message);
}
});
});
});

9
src/EventHub.Web/Pages/Organizations/Index.cshtml

@ -9,7 +9,14 @@
{
<abp-column size-lg="_4" size-md="_6">
<abp-card class="mb-3">
<img abp-card-image="Top" src="https://picsum.photos/seed/@organization.Name/400/160?blur=8" alt="@organization.DisplayName"/>
@if (organization.ProfilePictureContent == null)
{
<img abp-card-image="Top" style="max-height: 200px; min-height: 150px;" src="https://picsum.photos/seed/@organization.Name/400/160?blur=8" alt="@organization.DisplayName"/>
}
else
{
<img abp-card-image="Top" style="max-height: 200px; min-height: 150px;" src="@($"data:image/png;base64,{Convert.ToBase64String(organization.ProfilePictureContent)}")" alt="@organization.DisplayName"/>
}
<abp-card-body>
<abp-card-title>@organization.DisplayName</abp-card-title>
<abp-card-text>

15
src/EventHub.Web/Pages/Organizations/Profile.cshtml

@ -11,8 +11,15 @@
<abp-column size-lg="_8" size-md="_6">
<abp-row>
<abp-column size-lg="_12">
<abp-card class="mb-3">
<img abp-card-image="Top" src="https://picsum.photos/seed/@Model.Name/1920/400?blur=8" alt="@Model.Organization.DisplayName"/>
<abp-card class="mb-3" >
@if (Model.Organization.ProfilePictureContent == null)
{
<img abp-card-image="Top" style="max-height: 300px; min-height: 200px;" src="https://picsum.photos/seed/@Model.Name/1920/400?blur=8" alt="@Model.Organization.DisplayName"/>
}
else
{
<img abp-card-image="Top" style="max-height: 300px; min-height: 200px;" src="@($"data:image/png;base64,{Convert.ToBase64String(Model.Organization.ProfilePictureContent)}")" alt="@Model.Organization.DisplayName"/>
}
<abp-card-body>
<abp-card-title>
@if (Model.IsOrganizationOwner)
@ -53,8 +60,8 @@
</h5>
</div>
}
@if (!Model.Organization.TwitterUsername.IsNullOrWhiteSpace() || !Model.Organization.GitHubUsername.IsNullOrWhiteSpace() ||
!Model.Organization.InstagramUsername.IsNullOrWhiteSpace() || !Model.Organization.FacebookUsername.IsNullOrWhiteSpace() ||
@if (!Model.Organization.TwitterUsername.IsNullOrWhiteSpace() || !Model.Organization.GitHubUsername.IsNullOrWhiteSpace() ||
!Model.Organization.InstagramUsername.IsNullOrWhiteSpace() || !Model.Organization.FacebookUsername.IsNullOrWhiteSpace() ||
!Model.Organization.MediumUsername.IsNullOrWhiteSpace())
{
<div class="profile-icons mt-4 text-center">

2
test/EventHub.Application.Tests/Organizations/OrganizationAppServiceTests.cs

@ -111,7 +111,7 @@ namespace EventHub.Organizations
{
Login(_testData.UserAdminId);
var exception = await Assert.ThrowsAsync<AbpAuthorizationException>(() =>
var exception = await Assert.ThrowsAsync<BusinessException>(() =>
_organizationAppService.UpdateAsync(
_testData.OrganizationDotnetEuropeId,
new UpdateOrganizationDto

Loading…
Cancel
Save