User Identity Avatar Image listesine eklendi

This commit is contained in:
Sedat Öztürk 2026-06-11 00:38:20 +03:00
parent 6098758f34
commit bf73a70e2b
24 changed files with 183 additions and 26 deletions

View file

@ -34,6 +34,7 @@ public class UserInfoViewModel : ExtensibleObject
public bool PhoneNumberConfirmed { get; set; } public bool PhoneNumberConfirmed { get; set; }
public int AccessFailedCount { get; set; } public int AccessFailedCount { get; set; }
public bool ShouldChangePasswordOnNextLogin { get; set; } public bool ShouldChangePasswordOnNextLogin { get; set; }
public string Avatar { get; set; }
public string RocketUsername { get; set; } public string RocketUsername { get; set; }
public DateTimeOffset? CreationTime { get; set; } public DateTimeOffset? CreationTime { get; set; }
public DateTimeOffset? LastModificationTime { get; set; } public DateTimeOffset? LastModificationTime { get; set; }

View file

@ -0,0 +1,20 @@
using Microsoft.Extensions.Configuration;
namespace Sozsoft.Platform.Identity;
public static class AvatarProvider
{
public static string GetAvatar(
IConfiguration configuration,
string? tenantId,
string? userId)
{
var tenantPart = !string.IsNullOrEmpty(tenantId)
? $"tenants/{tenantId}"
: "host";
var baseUrl = configuration["App:CdnUrl"]?.TrimEnd('/');
return $"{baseUrl}/{tenantPart}/avatar/{userId ?? "default"}.jpg";
}
}

View file

@ -18,6 +18,7 @@ public partial class IdentityUserToUserInfoViewModelMapper : MapperBase<Identity
[MapperIgnoreTarget(nameof(UserInfoViewModel.Departments))] [MapperIgnoreTarget(nameof(UserInfoViewModel.Departments))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.JobPositions))] [MapperIgnoreTarget(nameof(UserInfoViewModel.JobPositions))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.userRoleNames))] [MapperIgnoreTarget(nameof(UserInfoViewModel.userRoleNames))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.Avatar))]
public override partial UserInfoViewModel Map(IdentityUser source); public override partial UserInfoViewModel Map(IdentityUser source);
[MapperIgnoreTarget(nameof(UserInfoViewModel.Roles))] [MapperIgnoreTarget(nameof(UserInfoViewModel.Roles))]
@ -27,6 +28,7 @@ public partial class IdentityUserToUserInfoViewModelMapper : MapperBase<Identity
[MapperIgnoreTarget(nameof(UserInfoViewModel.Departments))] [MapperIgnoreTarget(nameof(UserInfoViewModel.Departments))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.JobPositions))] [MapperIgnoreTarget(nameof(UserInfoViewModel.JobPositions))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.userRoleNames))] [MapperIgnoreTarget(nameof(UserInfoViewModel.userRoleNames))]
[MapperIgnoreTarget(nameof(UserInfoViewModel.Avatar))]
public override partial void Map(IdentityUser source, UserInfoViewModel destination); public override partial void Map(IdentityUser source, UserInfoViewModel destination);
public override void BeforeMap(IdentityUser source) public override void BeforeMap(IdentityUser source)

View file

@ -9,6 +9,7 @@ using Sozsoft.Platform.Extensions;
using Sozsoft.Platform.Identity.Dto; using Sozsoft.Platform.Identity.Dto;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
@ -37,6 +38,7 @@ public class PlatformIdentityAppService : ApplicationService
public IRepository<Department, Guid> departmentRepository { get; } public IRepository<Department, Guid> departmentRepository { get; }
public IRepository<JobPosition, Guid> jobPositionRepository { get; } public IRepository<JobPosition, Guid> jobPositionRepository { get; }
public BlobManager BlobCdnManager { get; } public BlobManager BlobCdnManager { get; }
private readonly IConfiguration configuration;
public PlatformIdentityAppService( public PlatformIdentityAppService(
IIdentityUserAppService identityUserAppService, IIdentityUserAppService identityUserAppService,
@ -53,6 +55,7 @@ public class PlatformIdentityAppService : ApplicationService
IRepository<Department, Guid> departmentRepository, IRepository<Department, Guid> departmentRepository,
IRepository<JobPosition, Guid> jobPositionRepository, IRepository<JobPosition, Guid> jobPositionRepository,
BlobManager blobCdnManager, BlobManager blobCdnManager,
IConfiguration configuration,
IGuidGenerator guidGenerator IGuidGenerator guidGenerator
) )
{ {
@ -66,6 +69,7 @@ public class PlatformIdentityAppService : ApplicationService
this.departmentRepository = departmentRepository; this.departmentRepository = departmentRepository;
this.jobPositionRepository = jobPositionRepository; this.jobPositionRepository = jobPositionRepository;
this.BlobCdnManager = blobCdnManager; this.BlobCdnManager = blobCdnManager;
this.configuration = configuration;
this.permissionRepository = permissionRepository; this.permissionRepository = permissionRepository;
this.branchRepository = branchRepository; this.branchRepository = branchRepository;
this.branchUsersRepository = branchUsersRepository; this.branchUsersRepository = branchUsersRepository;
@ -280,6 +284,7 @@ public class PlatformIdentityAppService : ApplicationService
user.SetEducationLevel(UserInfo.EducationLevel); user.SetEducationLevel(UserInfo.EducationLevel);
user.SetGraduationSchool(UserInfo.GraduationSchool); user.SetGraduationSchool(UserInfo.GraduationSchool);
user.SetBloodType(UserInfo.BloodType); user.SetBloodType(UserInfo.BloodType);
user.SetAvatar(UserInfo.Avatar);
await UserManager.UpdateAsync(user); await UserManager.UpdateAsync(user);
} }
@ -293,11 +298,15 @@ public class PlatformIdentityAppService : ApplicationService
if (input.Avatar is null || input.Avatar.ContentLength == 0) if (input.Avatar is null || input.Avatar.ContentLength == 0)
{ {
await BlobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName); await BlobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName);
user.SetAvatar(null);
} }
else else
{ {
await BlobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream()); await BlobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream());
user.SetAvatar(AvatarProvider.GetAvatar(configuration, user.TenantId?.ToString(), user.Id.ToString()));
} }
await UserManager.UpdateAsync(user);
} }
public async Task<List<PermissionDefinitionRecord>> GetPermissionList() public async Task<List<PermissionDefinitionRecord>> GetPermissionList()

View file

@ -4,7 +4,9 @@ using Sozsoft.Platform.BlobStoring;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Sozsoft.Platform.Extensions;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Account; using Volo.Abp.Account;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -21,14 +23,17 @@ namespace Sozsoft.Platform.Identity;
public class PlatformProfileAppService : ProfileAppService, IProfileAppService public class PlatformProfileAppService : ProfileAppService, IProfileAppService
{ {
public BlobManager _blobCdnManager { get; } public BlobManager _blobCdnManager { get; }
private readonly IConfiguration _configuration;
public PlatformProfileAppService( public PlatformProfileAppService(
IdentityUserManager userManager, IdentityUserManager userManager,
IOptions<IdentityOptions> identityOptions, IOptions<IdentityOptions> identityOptions,
BlobManager blobCdnManager BlobManager blobCdnManager,
IConfiguration configuration
) : base(userManager, identityOptions) ) : base(userManager, identityOptions)
{ {
_blobCdnManager = blobCdnManager; _blobCdnManager = blobCdnManager;
_configuration = configuration;
} }
[RemoteService(false)] [RemoteService(false)]
@ -63,10 +68,12 @@ public class PlatformProfileAppService : ProfileAppService, IProfileAppService
if (input.Avatar is null || input.Avatar.ContentLength == 0) if (input.Avatar is null || input.Avatar.ContentLength == 0)
{ {
await _blobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName); await _blobCdnManager.DeleteAsync(BlobContainerNames.Avatar, fileName);
user.SetAvatar(null);
} }
else else
{ {
await _blobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream()); await _blobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, input.Avatar.GetStream());
user.SetAvatar(AvatarProvider.GetAvatar(_configuration, user.TenantId?.ToString(), user.Id.ToString()));
} }
user.Name = input.Name; user.Name = input.Name;

View file

@ -13,6 +13,7 @@ public static class UserInfoViewModelMappingExtensions
{ {
userInfoViewModel.IsVerified = user.GetIsVerified(); userInfoViewModel.IsVerified = user.GetIsVerified();
userInfoViewModel.LoginEndDate = user.GetLoginEndDate(); userInfoViewModel.LoginEndDate = user.GetLoginEndDate();
userInfoViewModel.Avatar = user.GetAvatar();
userInfoViewModel.RocketUsername = user.GetRocketUsername(); userInfoViewModel.RocketUsername = user.GetRocketUsername();
userInfoViewModel.WorkHour = user.GetWorkHour(); userInfoViewModel.WorkHour = user.GetWorkHour();
userInfoViewModel.DepartmentId = user.GetDepartmentId(); userInfoViewModel.DepartmentId = user.GetDepartmentId();

View file

@ -4122,6 +4122,12 @@
"en": "Camera Off", "en": "Camera Off",
"tr": "Kamera Kapalı" "tr": "Kamera Kapalı"
}, },
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Avatar",
"en": "Avatar",
"tr": "Avatar"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Listform.ListformField.RoomName", "key": "App.Listform.ListformField.RoomName",

View file

@ -809,15 +809,16 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 710, true, true, true, true, false), EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 710, true, true, true, true, false),
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() { EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[ new () { Order=1,ColCount=1,ColSpan=1,ItemType="group",Items=[
new EditingFormItemDto { Order=1, DataField="Email", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=1, DataField="Avatar", ColSpan=1, EditorType2=EditorTypes.dxImageViewer },
new EditingFormItemDto { Order=2, DataField="Name", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=2, DataField="Email", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=3, DataField="Surname", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=3, DataField="Name", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=4, DataField="PhoneNumber", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=4, DataField="Surname", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=5, DataField="WorkHour", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order=5, DataField="PhoneNumber", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=6, DataField="DepartmentId", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order=6, DataField="WorkHour", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=7, DataField="JobPositionId", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order=7, DataField="DepartmentId", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=8, DataField="Password", ColSpan=1, EditorType2=EditorTypes.dxTextBox }, new EditingFormItemDto { Order=8, DataField="JobPositionId", ColSpan=1, EditorType2=EditorTypes.dxSelectBox, EditorOptions = EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order=9, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox }, new EditingFormItemDto { Order=9, DataField="Password", ColSpan=1, EditorType2=EditorTypes.dxTextBox },
new EditingFormItemDto { Order=10, DataField="IsActive", ColSpan=1, EditorType2=EditorTypes.dxCheckBox },
]} ]}
}), }),
InsertServiceAddress = "list-form-dynamic-api/user-insert", InsertServiceAddress = "list-form-dynamic-api/user-insert",
@ -906,7 +907,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "Id", FieldName = "Id",
CaptionName = "App.Listform.ListformField.Id", CaptionName = "App.Listform.ListformField.Id",
Width = 0, Width = 0,
ListOrderNo = 1, ListOrderNo = 0,
Visible = false, Visible = false,
IsActive = true, IsActive = true,
@ -915,6 +916,25 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
PivotSettingsJson = DefaultPivotSettingsJson PivotSettingsJson = DefaultPivotSettingsJson
}, },
new ListFormField new ListFormField
{
ListFormCode = listForm.ListFormCode,
RoleId = null,
UserId = null,
CultureName = LanguageCodes.En,
SourceDbType = DbType.String,
FieldName = "Avatar",
CaptionName = "App.Listform.ListformField.Avatar",
Width = 0,
ListOrderNo = 1,
Visible = true,
IsActive = true,
AllowSearch = true,
ColumnCustomizationJson = DefaultColumnCustomizationJson,
PermissionJson = DefaultFieldPermissionJson(PlatformConsts.IdentityPermissions.Users.Create, PlatformConsts.IdentityPermissions.Users.Default, PlatformConsts.IdentityPermissions.Users.Update, true, true, false),
PivotSettingsJson = DefaultPivotSettingsJson
},
new ListFormField
{ {
ListFormCode = listForm.ListFormCode, ListFormCode = listForm.ListFormCode,
RoleId = null, RoleId = null,
@ -1005,7 +1025,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "WorkHour", FieldName = "WorkHour",
CaptionName = "App.Listform.ListformField.WorkHour", CaptionName = "App.Listform.ListformField.WorkHour",
Width = 0, Width = 0,
ListOrderNo = 6, ListOrderNo = 7,
LookupJson = LookupQueryValues.DefaultLookupQueryJson(nameof(TableNameEnum.WorkHour), "Name", "Name"), LookupJson = LookupQueryValues.DefaultLookupQueryJson(nameof(TableNameEnum.WorkHour), "Name", "Name"),
Visible = true, Visible = true,
IsActive = true, IsActive = true,
@ -1027,7 +1047,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "DepartmentId", FieldName = "DepartmentId",
CaptionName = "App.Listform.ListformField.DepartmentId", CaptionName = "App.Listform.ListformField.DepartmentId",
Width = 0, Width = 0,
ListOrderNo = 7, ListOrderNo = 8,
LookupJson = JsonSerializer.Serialize(new LookupDto { LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query, DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name", DisplayExpr = "Name",
@ -1055,7 +1075,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "JobPositionId", FieldName = "JobPositionId",
CaptionName = "App.Listform.ListformField.JobPositionId", CaptionName = "App.Listform.ListformField.JobPositionId",
Width = 0, Width = 0,
ListOrderNo = 8, ListOrderNo = 9,
LookupJson = JsonSerializer.Serialize(new LookupDto { LookupJson = JsonSerializer.Serialize(new LookupDto {
DataSourceType = UiLookupDataSourceTypeEnum.Query, DataSourceType = UiLookupDataSourceTypeEnum.Query,
DisplayExpr = "Name", DisplayExpr = "Name",
@ -1085,7 +1105,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "IsActive", FieldName = "IsActive",
CaptionName = "App.Listform.ListformField.IsActive", CaptionName = "App.Listform.ListformField.IsActive",
Width = 0, Width = 0,
ListOrderNo = 9, ListOrderNo = 10,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
@ -1103,7 +1123,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "IsVerified", FieldName = "IsVerified",
CaptionName = "App.Listform.ListformField.IsVerified", CaptionName = "App.Listform.ListformField.IsVerified",
Width = 0, Width = 0,
ListOrderNo = 10, ListOrderNo = 11,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
@ -1121,7 +1141,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "EmailConfirmed", FieldName = "EmailConfirmed",
CaptionName = "App.Listform.ListformField.EmailConfirmed", CaptionName = "App.Listform.ListformField.EmailConfirmed",
Width = 0, Width = 0,
ListOrderNo = 11, ListOrderNo = 12,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
@ -1139,7 +1159,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "TwoFactorEnabled", FieldName = "TwoFactorEnabled",
CaptionName = "App.Listform.ListformField.TwoFactorEnabled", CaptionName = "App.Listform.ListformField.TwoFactorEnabled",
Width = 0, Width = 0,
ListOrderNo = 12, ListOrderNo = 13,
Visible = true, Visible = true,
IsActive = true, IsActive = true,
@ -1157,7 +1177,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
FieldName = "Password", FieldName = "Password",
CaptionName = "App.Listform.ListformField.Password", CaptionName = "App.Listform.ListformField.Password",
Width = 0, Width = 0,
ListOrderNo = 13, ListOrderNo = 14,
Visible = false, Visible = false,
IsActive = false, IsActive = false,

View file

@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Sozsoft.Platform.Extensions; using Sozsoft.Platform.Extensions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -24,6 +25,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
private readonly ITenantRepository _tenantRepository; private readonly ITenantRepository _tenantRepository;
private readonly IRepository<Branch, Guid> _branchRepository; private readonly IRepository<Branch, Guid> _branchRepository;
private readonly IRepository<BranchUsers, Guid> _branchUsersRepository; private readonly IRepository<BranchUsers, Guid> _branchUsersRepository;
private readonly IConfiguration _configuration;
public PlatformIdentityDataSeeder( public PlatformIdentityDataSeeder(
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
@ -37,6 +39,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
IPermissionGrantRepository permissionGrantRepository, IPermissionGrantRepository permissionGrantRepository,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
ITenantRepository tenantRepository, ITenantRepository tenantRepository,
IConfiguration configuration,
IOptions<IdentityOptions> identityOptions IOptions<IdentityOptions> identityOptions
) : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions) ) : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions)
{ {
@ -44,6 +47,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
this._tenantRepository = tenantRepository; this._tenantRepository = tenantRepository;
this._branchRepository = branchRepository; this._branchRepository = branchRepository;
this._branchUsersRepository = branchUsersRepository; this._branchUsersRepository = branchUsersRepository;
this._configuration = configuration;
} }
public override async Task<IdentityDataSeedResult> SeedAsync(string adminEmail, string adminPassword, Guid? tenantId = null, string adminUserName = null) public override async Task<IdentityDataSeedResult> SeedAsync(string adminEmail, string adminPassword, Guid? tenantId = null, string adminUserName = null)
@ -142,6 +146,7 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue); adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue); adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue); adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);
// adminUser.SetAvatar(GetAvatar(tenantId?.ToString(), adminUser.Id.ToString()));
adminUser.SetEducationLevel(PlatformConsts.AbpIdentity.User.AdminEducationLevelDefaultValue); adminUser.SetEducationLevel(PlatformConsts.AbpIdentity.User.AdminEducationLevelDefaultValue);
adminUser.SetGraduationSchool(PlatformConsts.AbpIdentity.User.AdminGraduationSchoolDefaultValue); adminUser.SetGraduationSchool(PlatformConsts.AbpIdentity.User.AdminGraduationSchoolDefaultValue);
adminUser.SetHomeAddress(PlatformConsts.AbpIdentity.User.AdminHomeAddressDefaultValue); adminUser.SetHomeAddress(PlatformConsts.AbpIdentity.User.AdminHomeAddressDefaultValue);
@ -195,5 +200,16 @@ public class PlatformIdentityDataSeeder : IdentityDataSeeder
return result; return result;
} }
} }
// private string GetAvatar(string? tenantId, string? userId)
// {
// var tenantPart = !string.IsNullOrEmpty(tenantId)
// ? $"tenants/{tenantId}"
// : "host";
// var baseUrl = _configuration["App:CdnUrl"]?.TrimEnd('/');
// return $"{baseUrl}/{tenantPart}/avatar/{userId ?? "default"}.jpg";
// }
} }

View file

@ -120,6 +120,7 @@ public static class PlatformConsts
public const string LoginEndDate = "LoginEndDate"; public const string LoginEndDate = "LoginEndDate";
public const string RoleNames = "RoleNames"; public const string RoleNames = "RoleNames";
public const string IsVerified = "IsVerified"; public const string IsVerified = "IsVerified";
public const string Avatar = "Avatar";
public const string RocketUsername = "RocketUsername"; public const string RocketUsername = "RocketUsername";
public const string HomeAddress = "HomeAddress"; public const string HomeAddress = "HomeAddress";
public const string EducationLevel = "EducationLevel"; public const string EducationLevel = "EducationLevel";

View file

@ -69,6 +69,15 @@ public static class PlatformModuleExtensionConfigurator
property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = false; property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = false;
}); });
user.AddOrUpdateProperty<string>(
PlatformConsts.AbpIdentity.User.Avatar,
property =>
{
property.DisplayName = new LocalizableString(typeof(PlatformResource), PlatformConsts.AbpIdentity.User.Avatar);
property.IsAvailableToClients = true;
property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = false;
});
user.AddOrUpdateProperty<string>( user.AddOrUpdateProperty<string>(
PlatformConsts.AbpIdentity.User.RocketUsername, PlatformConsts.AbpIdentity.User.RocketUsername,
property => property =>

View file

@ -31,6 +31,16 @@ public static class AbpIdentityUserExtensions
return user.GetProperty<DateTime?>(PlatformConsts.AbpIdentity.User.LoginEndDate); return user.GetProperty<DateTime?>(PlatformConsts.AbpIdentity.User.LoginEndDate);
} }
//Avatar
public static void SetAvatar(this IdentityUser user, string value)
{
user.SetProperty(PlatformConsts.AbpIdentity.User.Avatar, value);
}
public static string GetAvatar(this IdentityUser user)
{
return user.GetProperty<string>(PlatformConsts.AbpIdentity.User.Avatar);
}
//Rocket Username //Rocket Username
public static void SetRocketUsername(this IdentityUser user, string rocketUsername) public static void SetRocketUsername(this IdentityUser user, string rocketUsername)
{ {

View file

@ -37,6 +37,15 @@ public static class PlatformEfCoreEntityExtensionMappings
} }
); );
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
PlatformConsts.AbpIdentity.User.Avatar,
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(512).HasDefaultValue(null);
}
);
ObjectExtensionManager.Instance ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>( .MapEfCoreProperty<IdentityUser, string>(
PlatformConsts.AbpIdentity.User.RocketUsername, PlatformConsts.AbpIdentity.User.RocketUsername,

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations namespace Sozsoft.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20260606212623_Initial")] [Migration("20260610203148_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -6870,6 +6870,10 @@ namespace Sozsoft.Platform.Migrations
.HasDefaultValue(0) .HasDefaultValue(0)
.HasColumnName("AccessFailedCount"); .HasColumnName("AccessFailedCount");
b.Property<string>("Avatar")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<DateTime?>("BirthDate") b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime2"); .HasColumnType("datetime2");

View file

@ -435,6 +435,7 @@ namespace Sozsoft.Platform.Migrations
ShouldChangePasswordOnNextLogin = table.Column<bool>(type: "bit", nullable: false), ShouldChangePasswordOnNextLogin = table.Column<bool>(type: "bit", nullable: false),
EntityVersion = table.Column<int>(type: "int", nullable: false), EntityVersion = table.Column<int>(type: "int", nullable: false),
LastPasswordChangeTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true), LastPasswordChangeTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
Avatar = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
BirthDate = table.Column<DateTime>(type: "datetime2", nullable: true), BirthDate = table.Column<DateTime>(type: "datetime2", nullable: true),
BirthPlace = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true), BirthPlace = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
BloodType = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: true), BloodType = table.Column<string>(type: "nvarchar(16)", maxLength: 16, nullable: true),

View file

@ -6867,6 +6867,10 @@ namespace Sozsoft.Platform.Migrations
.HasDefaultValue(0) .HasDefaultValue(0)
.HasColumnName("AccessFailedCount"); .HasColumnName("AccessFailedCount");
b.Property<string>("Avatar")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<DateTime?>("BirthDate") b.Property<DateTime?>("BirthDate")
.HasColumnType("datetime2"); .HasColumnType("datetime2");

View file

@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Sozsoft.Platform.Extensions; using Sozsoft.Platform.Extensions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -24,6 +25,7 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
private readonly IRepository<BranchUsers, Guid> _branchUsersRepository; private readonly IRepository<BranchUsers, Guid> _branchUsersRepository;
private readonly IPermissionGrantRepository _permissionGrantRepository; private readonly IPermissionGrantRepository _permissionGrantRepository;
private readonly ITenantRepository _tenantRepository; private readonly ITenantRepository _tenantRepository;
private readonly IConfiguration _configuration;
public TenantIdentityDataSeeder( public TenantIdentityDataSeeder(
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
@ -37,6 +39,7 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
IPermissionGrantRepository permissionGrantRepository, IPermissionGrantRepository permissionGrantRepository,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
ITenantRepository tenantRepository, ITenantRepository tenantRepository,
IConfiguration configuration,
IOptions<IdentityOptions> identityOptions IOptions<IdentityOptions> identityOptions
) : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions) ) : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions)
{ {
@ -44,6 +47,7 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
this._branchUsersRepository = branchUsersRepository; this._branchUsersRepository = branchUsersRepository;
this._permissionGrantRepository = permissionGrantRepository; this._permissionGrantRepository = permissionGrantRepository;
this._tenantRepository = tenantRepository; this._tenantRepository = tenantRepository;
this._configuration = configuration;
} }
public override async Task<IdentityDataSeedResult> SeedAsync(string adminEmail, string adminPassword, Guid? tenantId = null, string adminUserName = null) public override async Task<IdentityDataSeedResult> SeedAsync(string adminEmail, string adminPassword, Guid? tenantId = null, string adminUserName = null)
@ -140,6 +144,7 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue); adminUser.SetWorkHour(PlatformConsts.AbpIdentity.User.AdminWorkHourDefaultValue);
adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue); adminUser.SetNationality(PlatformConsts.AbpIdentity.User.AdminNationalityDefaultValue);
adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue); adminUser.SetBloodType(PlatformConsts.AbpIdentity.User.AdminBloodTypeDefaultValue);
// adminUser.SetAvatar(GetAvatar(tenantId?.ToString(), adminUser.Id.ToString()));
adminUser.SetEducationLevel(PlatformConsts.AbpIdentity.User.AdminEducationLevelDefaultValue); adminUser.SetEducationLevel(PlatformConsts.AbpIdentity.User.AdminEducationLevelDefaultValue);
adminUser.SetGraduationSchool(PlatformConsts.AbpIdentity.User.AdminGraduationSchoolDefaultValue); adminUser.SetGraduationSchool(PlatformConsts.AbpIdentity.User.AdminGraduationSchoolDefaultValue);
adminUser.SetHomeAddress(PlatformConsts.AbpIdentity.User.AdminHomeAddressDefaultValue); adminUser.SetHomeAddress(PlatformConsts.AbpIdentity.User.AdminHomeAddressDefaultValue);
@ -178,5 +183,16 @@ public class TenantIdentityDataSeeder : IdentityDataSeeder
return result; return result;
} }
} }
// private string GetAvatar(string? tenantId, string? userId)
// {
// var tenantPart = !string.IsNullOrEmpty(tenantId)
// ? $"tenants/{tenantId}"
// : "host";
// var baseUrl = _configuration["App:CdnUrl"]?.TrimEnd('/');
// return $"{baseUrl}/{tenantPart}/avatar/{userId ?? "default"}.jpg";
// }
} }

View file

@ -5,6 +5,7 @@
"CorsOrigins": "https://dev.sozsoft.com", "CorsOrigins": "https://dev.sozsoft.com",
"RedirectAllowedUrls": "https://dev.sozsoft.com,https://dev.sozsoft.com/authentication/callback", "RedirectAllowedUrls": "https://dev.sozsoft.com,https://dev.sozsoft.com/authentication/callback",
"AttachmentsPath": "/etc/api/mail-queue/attachments", "AttachmentsPath": "/etc/api/mail-queue/attachments",
"CdnUrl": "https://dev-cdn.sozsoft.com",
"CdnPath": "/etc/api/cdn", "CdnPath": "/etc/api/cdn",
"BaseDomain": "sozsoft.com", "BaseDomain": "sozsoft.com",
"BackupPath": "/var/opt/mssql/backup" "BackupPath": "/var/opt/mssql/backup"

View file

@ -5,6 +5,7 @@
"CorsOrigins": "https://sozsoft.com", "CorsOrigins": "https://sozsoft.com",
"RedirectAllowedUrls": "https://sozsoft.com,https://sozsoft.com/authentication/callback", "RedirectAllowedUrls": "https://sozsoft.com,https://sozsoft.com/authentication/callback",
"AttachmentsPath": "/etc/api/mail-queue/attachments", "AttachmentsPath": "/etc/api/mail-queue/attachments",
"CdnUrl": "https://cdn.sozsoft.com",
"CdnPath": "/etc/api/cdn", "CdnPath": "/etc/api/cdn",
"BaseDomain": "sozsoft.com", "BaseDomain": "sozsoft.com",
"BackupPath": "/var/opt/mssql/backup" "BackupPath": "/var/opt/mssql/backup"

View file

@ -5,6 +5,7 @@
"CorsOrigins": "http://localhost,http://localhost:3000,http://localhost:4200", "CorsOrigins": "http://localhost,http://localhost:3000,http://localhost:4200",
"RedirectAllowedUrls": "http://localhost:4200,http://localhost:4200/authentication/callback", "RedirectAllowedUrls": "http://localhost:4200,http://localhost:4200/authentication/callback",
"AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments", "AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments",
"CdnUrl": "https://localhost:44344",
"CdnPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\docker\\cdn", "CdnPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\docker\\cdn",
"Version": "1.0.5", "Version": "1.0.5",
"BackupPath": "/var/opt/mssql/backup" "BackupPath": "/var/opt/mssql/backup"

View file

@ -1,5 +1,5 @@
{ {
"commit": "c1e7f8c", "commit": "6098758",
"releases": [ "releases": [
{ {
"version": "1.1.04", "version": "1.1.04",

View file

@ -141,9 +141,12 @@ const ImageViewerEditorComponent = ({
height: thumbH, height: thumbH,
objectFit: 'cover', objectFit: 'cover',
borderRadius: 4, borderRadius: 4,
border: '1px solid #ddd',
display: 'block', display: 'block',
}} }}
onError={({ currentTarget }) => {
currentTarget.onerror = null
currentTarget.src = '/img/others/default-profile.png'
}}
/> />
</button> </button>
))} ))}

View file

@ -185,9 +185,12 @@ const ImageViewerEditorComponent = (templateData: any): ReactElement => {
height: thumbH, height: thumbH,
objectFit: 'cover', objectFit: 'cover',
borderRadius: 4, borderRadius: 4,
border: '1px solid #ddd',
display: 'block', display: 'block',
}} }}
onError={({ currentTarget }) => {
currentTarget.onerror = null
currentTarget.src = '/img/others/default-profile.png'
}}
/> />
</button> </button>
))} ))}

View file

@ -20,6 +20,8 @@ import {
} from '@/proxy/form/models' } from '@/proxy/form/models'
import { addCss } from './Utils' import { addCss } from './Utils'
const DEFAULT_PROFILE_IMAGE = '/img/others/default-profile.png'
const cellTemplateMultiValue = ( const cellTemplateMultiValue = (
cellElement: HTMLElement, cellElement: HTMLElement,
cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>, cellInfo: DataGridTypes.ColumnCellTemplateData<any, any>,
@ -89,6 +91,8 @@ function getImgPreview(): HTMLDivElement {
'opacity:0', 'opacity:0',
].join(';') ].join(';')
const img = document.createElement('img') const img = document.createElement('img')
img.onerror = null
img.src = DEFAULT_PROFILE_IMAGE
img.style.cssText = img.style.cssText =
'display:block;max-width:312px;max-height:312px;object-fit:contain;border-radius:4px;' 'display:block;max-width:312px;max-height:312px;object-fit:contain;border-radius:4px;'
el.appendChild(img) el.appendChild(img)
@ -101,6 +105,10 @@ function getImgPreview(): HTMLDivElement {
function showImgPreview(src: string, e: MouseEvent) { function showImgPreview(src: string, e: MouseEvent) {
const el = getImgPreview() const el = getImgPreview()
const imgEl = el.querySelector('img') as HTMLImageElement const imgEl = el.querySelector('img') as HTMLImageElement
imgEl.onerror = () => {
imgEl.onerror = null
imgEl.src = DEFAULT_PROFILE_IMAGE
}
if (imgEl.src !== src) imgEl.src = src if (imgEl.src !== src) imgEl.src = src
const GAP = 12 const GAP = 12
@ -148,9 +156,13 @@ const cellTemplateImage = (
urls.forEach((url) => { urls.forEach((url) => {
const img = document.createElement('img') const img = document.createElement('img')
img.onerror = () => {
img.onerror = null
img.src = DEFAULT_PROFILE_IMAGE
}
img.src = url img.src = url
img.alt = '' img.alt = ''
img.style.cssText = `width:${w}px;height:${h}px;object-fit:cover;border-radius:4px;border:1px solid #ddd;margin:2px;vertical-align:middle;display:inline-block;cursor:zoom-in;` img.style.cssText = `width:${w}px;height:${h}px;object-fit:cover;border-radius:4px;margin:2px;vertical-align:middle;display:inline-block;cursor:zoom-in;`
img.addEventListener('mouseenter', (e) => showImgPreview(url, e as MouseEvent)) img.addEventListener('mouseenter', (e) => showImgPreview(url, e as MouseEvent))
img.addEventListener('mousemove', (e) => showImgPreview(url, e as MouseEvent)) img.addEventListener('mousemove', (e) => showImgPreview(url, e as MouseEvent))