FileManager IsProtectedFolder düzenlemesi

This commit is contained in:
Sedat Öztürk 2026-06-13 22:34:42 +03:00
parent 771ea95e55
commit fc30539916
8 changed files with 103 additions and 117 deletions

View file

@ -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;
}

View file

@ -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<string> 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();

View file

@ -571,6 +571,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
Path = relativePath,
ParentId = string.Empty,
IsReadOnly = false,
IsProtectedFolder = false,
ChildCount = 0,
TenantId = _currentTenant.Id?.ToString()
});

View file

@ -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> identityOptions;
public ListFormDynamicApiAppService(
@ -42,6 +44,7 @@ public class ListFormDynamicApiAppService : PlatformAppService, IListFormDynamic
IdentityUserManager userManager,
BlobManager blobCdnManager,
IConfiguration configuration,
IHostEnvironment hostEnvironment,
IOptions<IdentityOptions> 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<CreateUpdateUserInput> 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);

View file

@ -1,5 +1,5 @@
{
"commit": "7632c9e",
"commit": "771ea95",
"releases": [
{
"version": "1.1.04",

View file

@ -49,6 +49,7 @@ export interface DocumentDto {
path: string
parentId: string
isReadOnly: boolean
isProtectedFolder: boolean
childCount: number
tenantId?: string
}

View file

@ -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
}

View file

@ -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(<Notification type="danger">Failed to load files and folders</Notification>)