diff --git a/api/src/Sozsoft.Platform.Application.Contracts/FileManagement/FileItemDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/FileManagement/FileItemDto.cs index 858dc6c..0b6486b 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/FileManagement/FileItemDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/FileManagement/FileItemDto.cs @@ -16,6 +16,7 @@ public class FileItemDto public string Path { get; set; } = string.Empty; public string ParentId { get; set; } = string.Empty; public bool IsReadOnly { get; set; } + public bool IsProtectedFolder { get; set; } public int? ChildCount { get; set; } public string TenantId { get; set; } = string.Empty; } diff --git a/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs b/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs index 5349985..e327b24 100644 --- a/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs +++ b/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs @@ -28,14 +28,14 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe private const string FolderMarkerSuffix = ".folder"; private const string IndexFileName = "index.json"; - // Protected system folders that cannot be deleted, renamed, or moved private static readonly HashSet ProtectedFolders = new(StringComparer.OrdinalIgnoreCase) { - // BlobContainerNames.Avatar, - // BlobContainerNames.Import, - // BlobContainerNames.Note, - // BlobContainerNames.Intranet - // BlobContainerNames.Backup + BlobContainerNames.Intranet, + BlobContainerNames.Avatar, + BlobContainerNames.Import, + BlobContainerNames.Messenger, + BlobContainerNames.Note, + BlobContainerNames.Backup }; public FileManagementAppService( @@ -122,6 +122,39 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe return isProtected; } + private bool IsProtectedRootFolder(string path) + { + var pathParts = path.Split('/', StringSplitOptions.RemoveEmptyEntries); + return pathParts.Length == 1 && ProtectedFolders.Contains(pathParts[0]); + } + + private FileItemDto ToFileItemDto( + FileMetadata metadata, + string? parentId = null, + string? tenantId = null) + { + var isProtectedFolder = metadata.Type == "folder" && IsProtectedRootFolder(metadata.Path); + var isReadOnly = metadata.IsReadOnly || isProtectedFolder; + + return new FileItemDto + { + Id = metadata.Id, + Name = metadata.Name, + Type = metadata.Type, + Size = metadata.Size, + Extension = metadata.Extension, + MimeType = metadata.MimeType, + CreatedAt = metadata.CreatedAt, + ModifiedAt = metadata.ModifiedAt, + Path = metadata.Path, + ParentId = parentId ?? metadata.ParentId, + IsReadOnly = isReadOnly, + IsProtectedFolder = isProtectedFolder, + ChildCount = metadata.ChildCount, + TenantId = tenantId == "host" ? null : (tenantId ?? metadata.TenantId) + }; + } + private void ValidateNotProtectedFolder(string id, string operation) { var decodedPath = DecodeIdAsPath(id); @@ -267,29 +300,11 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe { var items = await GetFolderIndexAsync(parentId, tenantId); - var result = items.Select(metadata => - { - var isRootLevel = string.IsNullOrEmpty(parentId); - var isProtected = IsProtectedFolder(metadata.Path); - var finalIsReadOnly = metadata.IsReadOnly || (isRootLevel && isProtected); - - return new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - Size = metadata.Size, - Extension = metadata.Extension, - MimeType = metadata.MimeType, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = parentId ?? string.Empty, - IsReadOnly = finalIsReadOnly, - ChildCount = metadata.ChildCount, - TenantId = metadata.TenantId - }; - }).OrderBy(x => x.Type == "folder" ? 0 : 1).ThenBy(x => x.Name).ToList(); + var result = items + .Select(metadata => ToFileItemDto(metadata, parentId ?? string.Empty)) + .OrderBy(x => x.Type == "folder" ? 0 : 1) + .ThenBy(x => x.Name) + .ToList(); return new GetFilesDto { Items = result }; } @@ -347,18 +362,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe TenantId = tenantId == "host" ? null : tenantId }; - return new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = input.ParentId ?? string.Empty, - IsReadOnly = metadata.IsReadOnly, - TenantId = tenantId == "host" ? null : tenantId - }; + return ToFileItemDto(metadata, input.ParentId ?? string.Empty, tenantId); } [HttpPost("api/app/file-management/upload-file")] @@ -440,21 +444,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe }; // File system'e kaydedildi, index güncellemeye gerek yok - return new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - Size = metadata.Size, - Extension = metadata.Extension, - MimeType = metadata.MimeType, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = input.ParentId ?? string.Empty, - IsReadOnly = metadata.IsReadOnly, - TenantId = tenantId == "host" ? null : tenantId - }; + return ToFileItemDto(metadata, input.ParentId ?? string.Empty, tenantId); } [HttpPost("api/app/file-management/{id}/rename-item")] @@ -511,21 +501,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe await SaveFolderIndexAsync(parentItems, tenantId, metadata.ParentId == string.Empty ? null : metadata.ParentId); - return new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - Size = metadata.Size, - Extension = metadata.Extension, - MimeType = metadata.MimeType, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = metadata.ParentId, - IsReadOnly = metadata.IsReadOnly, - TenantId = tenantId == "host" ? null : tenantId - }; + return ToFileItemDto(metadata, metadata.ParentId, tenantId); } [HttpPost("api/app/file-management/{id}/move-item")] @@ -577,21 +553,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe targetItems.Add(metadata); await SaveFolderIndexAsync(targetItems, tenantId, input.TargetFolderId); - return new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - Size = metadata.Size, - Extension = metadata.Extension, - MimeType = metadata.MimeType, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = metadata.ParentId, - IsReadOnly = metadata.IsReadOnly, - TenantId = tenantId == "host" ? null : tenantId - }; + return ToFileItemDto(metadata, metadata.ParentId, tenantId); } [HttpDelete("api/app/file-management/{id}/item/{tenantId}")] @@ -739,6 +701,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe Path = finalTargetPath, ParentId = input.TargetFolderId ?? string.Empty, IsReadOnly = false, + IsProtectedFolder = false, TenantId = tenantId == "host" ? null : tenantId }); } @@ -769,6 +732,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe Path = finalTargetPath, ParentId = input.TargetFolderId ?? string.Empty, IsReadOnly = false, + IsProtectedFolder = false, TenantId = tenantId == "host" ? null : tenantId }); } @@ -867,6 +831,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe Path = finalTargetPath, ParentId = input.TargetFolderId ?? string.Empty, IsReadOnly = false, + IsProtectedFolder = false, TenantId = tenantId == "host" ? null : tenantId }); } @@ -897,6 +862,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe Path = finalTargetPath, ParentId = input.TargetFolderId ?? string.Empty, IsReadOnly = false, + IsProtectedFolder = false, TenantId = tenantId == "host" ? null : tenantId }); } @@ -955,21 +921,7 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe var filteredItems = allItems .Where(x => x.Name.ToLowerInvariant().Contains(query)) - .Select(metadata => new FileItemDto - { - Id = metadata.Id, - Name = metadata.Name, - Type = metadata.Type, - Size = metadata.Size, - Extension = metadata.Extension, - MimeType = metadata.MimeType, - CreatedAt = metadata.CreatedAt, - ModifiedAt = metadata.ModifiedAt, - Path = metadata.Path, - ParentId = metadata.ParentId, - IsReadOnly = metadata.IsReadOnly, - TenantId = tenantId == "host" ? null : tenantId - }) + .Select(metadata => ToFileItemDto(metadata, metadata.ParentId, tenantId)) .OrderBy(x => x.Type == "folder" ? 0 : 1) .ThenBy(x => x.Name) .ToList(); diff --git a/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs b/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs index d60fd9b..671f388 100644 --- a/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs +++ b/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs @@ -571,6 +571,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService Path = relativePath, ParentId = string.Empty, IsReadOnly = false, + IsProtectedFolder = false, ChildCount = 0, TenantId = _currentTenant.Id?.ToString() }); diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs index 2a13713..37d4582 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormDynamicApiAppService.cs @@ -15,6 +15,7 @@ using IdentityUser = Volo.Abp.Identity.IdentityUser; using Sozsoft.Platform.BlobStoring; using Sozsoft.Platform.Identity; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; namespace Sozsoft.Platform.ListForms.DynamicApi; @@ -32,6 +33,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic private readonly IdentityUserManager userManager; private readonly BlobManager blobCdnManager; private readonly IConfiguration configuration; + private readonly IHostEnvironment hostEnvironment; private readonly IOptions identityOptions; public ListFormDynamicApiAppService( @@ -42,6 +44,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic IdentityUserManager userManager, BlobManager blobCdnManager, IConfiguration configuration, + IHostEnvironment hostEnvironment, IOptions identityOptions) { this.tenantRepository = tenantRepository; @@ -51,6 +54,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic this.userManager = userManager; this.blobCdnManager = blobCdnManager; this.configuration = configuration; + this.hostEnvironment = hostEnvironment; this.identityOptions = identityOptions; } @@ -59,10 +63,15 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic return Guid.TryParse(value, out var id) ? id : Guid.Empty; } - private async Task SaveAvatarAsync(IdentityUser user, string avatar) + private async Task SaveAvatarAsync(IdentityUser user, string avatar, bool saveDefaultWhenMissing = false) { if (avatar.IsNullOrWhiteSpace()) { + if (saveDefaultWhenMissing) + { + await SaveDefaultAvatarAsync(user); + } + return; } @@ -94,6 +103,38 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic user.SetAvatar(AvatarProvider.GetAvatar(configuration, user.TenantId?.ToString(), user.Id.ToString())); } + private async Task SaveDefaultAvatarAsync(IdentityUser user) + { + var defaultAvatarPath = FindDefaultAvatarPath(); + if (defaultAvatarPath == null) + { + return; + } + + var fileName = $"{user.Id}.jpg"; + await using var stream = File.OpenRead(defaultAvatarPath); + await blobCdnManager.SaveAsync(BlobContainerNames.Avatar, fileName, stream); + user.SetAvatar(AvatarProvider.GetAvatar(configuration, user.TenantId?.ToString(), user.Id.ToString())); + } + + private string? FindDefaultAvatarPath() + { + var directory = new DirectoryInfo(hostEnvironment.ContentRootPath); + + while (directory != null) + { + var candidate = Path.Combine(directory.FullName, "ui", "public", "img", "others", "default-profile.png"); + if (File.Exists(candidate)) + { + return candidate; + } + + directory = directory.Parent; + } + + return null; + } + [Authorize(IdentityPermissions.Users.Create)] public async Task PostUserInsertAsync(DynamicApiBaseInput input) { @@ -119,7 +160,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic user.SetJobPositionId(ParseGuid(input.Data.JobPositionId)); user.SetIsVerified(verify); - await SaveAvatarAsync(user, input.Data.Avatar); + await SaveAvatarAsync(user, input.Data.Avatar, saveDefaultWhenMissing: true); (await userManager.CreateAsync(user, input.Data.Password)).CheckErrors(); await userManager.SetLockoutEnabledAsync(user, true); diff --git a/ui/public/version.json b/ui/public/version.json index 6348d9a..46c0ed2 100644 --- a/ui/public/version.json +++ b/ui/public/version.json @@ -1,5 +1,5 @@ { - "commit": "7632c9e", + "commit": "771ea95", "releases": [ { "version": "1.1.04", diff --git a/ui/src/proxy/intranet/models.ts b/ui/src/proxy/intranet/models.ts index f1f65bc..228eed2 100644 --- a/ui/src/proxy/intranet/models.ts +++ b/ui/src/proxy/intranet/models.ts @@ -49,6 +49,7 @@ export interface DocumentDto { path: string parentId: string isReadOnly: boolean + isProtectedFolder: boolean childCount: number tenantId?: string } diff --git a/ui/src/types/fileManagement.ts b/ui/src/types/fileManagement.ts index cfa9f00..543c1fc 100644 --- a/ui/src/types/fileManagement.ts +++ b/ui/src/types/fileManagement.ts @@ -10,6 +10,7 @@ export interface FileItem { path: string extension: string isReadOnly: boolean + isProtectedFolder: boolean childCount?: number // Klasörler için öğe sayısı tenantId?: string } @@ -78,4 +79,4 @@ export interface FileManagerFilters { fileType?: string sortBy: SortBy sortOrder: SortOrder -} \ No newline at end of file +} diff --git a/ui/src/views/admin/files/FileManager.tsx b/ui/src/views/admin/files/FileManager.tsx index 3330d34..09a3813 100644 --- a/ui/src/views/admin/files/FileManager.tsx +++ b/ui/src/views/admin/files/FileManager.tsx @@ -136,18 +136,7 @@ const FileManager = () => { const response = await fileManagementService.getItems(folderId, selectedTenant?.id) // Backend returns GetFilesDto which has Items property const items = response.data.items || [] - // Manual protection for system folders - const protectedItems = items.map((item) => { - const isSystemFolder = ['intranet', 'avatar', 'import', 'note', 'backup'].includes( - item.name.toLowerCase(), - ) - return { - ...item, - isReadOnly: item.isReadOnly || isSystemFolder, - } - }) - - setItems(protectedItems) + setItems(items) } catch (error) { console.error('Failed to fetch items:', error) toast.push(Failed to load files and folders)