Wizard Delete eksiklikleri

This commit is contained in:
Sedat ÖZTÜRK 2026-09-24 17:24:37 +03:00
parent 9a14be4ee4
commit 9816fb91ce
7 changed files with 238 additions and 29 deletions

View file

@ -1469,6 +1469,21 @@ kullanan, sonra kullanılan — ve her kontrol o ana kadar kalan kayıtlara kar
| Veri kaynağı | ekran, Custom Endpoint, zamanlanmış iş |
| Dil anahtarı | menü/yetki/yetki grubu adı, ekran başlığı/açıklaması, alan başlığı |
Silme yolunda wizard'ın **bağlandığı** kaplar da boşaldıysa gider, `InsertedRecords`'ta olmasalar
bile: kap başka bir wizard'la oluşmuş ve o wizard silinirken bu wizard yüzünden bırakılmış
olabilir. Üst menü zinciri yukarı doğru yürünür — alt menüsü kalmayan ve kendisi adres açmayan
(`Url` boş) menü silinir, adresi olan ya da altında menü kalan kayıtta zincir durur. Wizard'ın
`PermissionGroupName`'i içinde yetki kalmamışsa, silinen menülerin ve `MenuParentModuleId`'nin
modülü yukarıdaki tabloda kullananı yoksa silinir; adlarının dil anahtarları da aynı kontrolden
geçer. Bu kaplar rapora yalnızca silindiklerinde girer. Düzenleme (redeploy) yolunda kaplara
dokunulmaz.
Silinen menü, modül, yetki ve yetki grubu satırları ekranların seed dosyalarından da çıkarılır
(`App.Menus.{Menu,Module}.json`, `Abp.Identity.Permission{s,Groups}.json`;
`IListFormSeedDataSynchronizer.RemoveRowsByFieldAsync`, iş anahtarıyla, `SeedSyncDelete`'e
bağlı). Wizard bu dosyalara yazmaz, ama kayıt menü/yetki ekranlarından düzenlendiğinde satır
senkronizasyonla girer; dosyada kalırsa migrate kaydı geri getirir.
Bağlı wizard'ı olan (alt formu ya da component bağımlılığı wizard olarak duran) bir wizard
silinemez; önce bağlı olanlar silinir. Kural listedeki gruplamayla aynı kaynaktandır
(`WizardFileInfoDto.Dependencies`), sunucu `DeleteFile`'da reddeder, istemci butonu kapatır.

View file

@ -911,6 +911,9 @@ Sıra **kardeşler arasındadır**: her üst yetkinin altında 1'den başlar, gr
kendi aralarında 1'den. `PlatformPermissionAppService` sıralama anahtarını kökten yaprağa üst yetki
zincirinden kurar, böylece çocuk her zaman ebeveyninin altında kalır. Değerler seed dosyalarındaki
`Order` alanında tutulur ve Permissions / Permission Groups ekranlarından düzenlenebilir.
Wizard (ve migrate'te `WizardDataSeeder`) yeni grubu ve grubun yeni kök yetkisini kardeşlerin en
büyük sırasının bir fazlasıyla, alt yetkileri Create/Update/Delete/Export/Import/Note dizilimiyle
1'den yazar (`NextPermissionOrder`, `AbpPermissionsExtensions`).
Aksiyon yetkisi olan alt gruplar (UI tarafındaki karşılıkları `constants/permission.constant.ts`
içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppService'lerdedir):

View file

@ -31,6 +31,15 @@ public interface IListFormSeedDataSynchronizer
/// <summary>Silinen satirlari dosyadan cikarir; <c>SeedSyncDelete</c> kapaliysa hicbir sey yapmaz.</summary>
Task RemoveRowsAsync(string listFormCode, object[] keys);
/// <summary>
/// <paramref name="fieldName"/> alaninin degeri <paramref name="values"/> icinde olan satirlari
/// dosyadan cikarir; <c>SeedSyncDelete</c> kapaliysa ya da hedef diskte yoksa hicbir sey yapmaz.
/// Kaydi veritabani disinda silinmis satirlar icindir: eslesme is anahtari uzerinden yapilir,
/// cunku dosyanin anahtari ekraninkinden farkli olabilir (yetki dosyalari <c>Name</c> ile
/// yazilir, ekran <c>Id</c> kullanir) ve dosyanin kendi basligina dokunulmaz.
/// </summary>
Task RemoveRowsByFieldAsync(string listFormCode, string fieldName, IReadOnlyCollection<string> values);
/// <summary>
/// Dosyayi listenin tamamiyla yeniden yazar; toplu islemler (import) icin kullanilir.
/// Toplu yukleme kayit ekledigi icin <c>SeedSyncInsert</c> bayragina baglidir.
@ -149,6 +158,44 @@ public class ListFormSeedDataSynchronizer(
});
}
public async Task RemoveRowsByFieldAsync(string listFormCode, string fieldName, IReadOnlyCollection<string> values)
{
var wanted = (values ?? [])
.Where(v => !string.IsNullOrWhiteSpace(v))
.ToHashSet(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(fieldName) || wanted.Count == 0)
return;
bool Matches(Dictionary<string, object> row) =>
row.Any(kv => string.Equals(kv.Key, fieldName, StringComparison.OrdinalIgnoreCase) &&
wanted.Contains(ToComparableText(kv.Value)));
await ExecuteAsync(listFormCode, listForm => listForm.SeedSyncDelete, async (_, target) =>
{
List<string> files = target.IsFolder
? Directory.Exists(target.Path) ? [.. Directory.EnumerateFiles(target.Path, "*.json")] : []
: File.Exists(target.Path) ? [target.Path] : [];
foreach (var filePath in files)
{
var seedFile = JsonSerializer.Deserialize<ListFormSeedDataFileDto>(
await File.ReadAllTextAsync(filePath),
ListFormSeedDataFileDto.JsonOptions);
if (seedFile?.Rows == null || seedFile.Rows.RemoveAll(Matches) == 0)
continue;
// Klasor modunda satir dosyasi tek satirliktir; bosalan dosya klasorde kalirsa
// applier onu satirsiz bir ekran dosyasi olarak okur.
if (target.IsFolder && seedFile.Rows.Count == 0)
File.Delete(filePath);
else
await WriteSeedFileAsync(filePath, seedFile);
}
}, createTarget: false);
}
public async Task SyncAllAsync(string listFormCode)
{
await ExecuteAsync(listFormCode, listForm => listForm.SeedSyncInsert, WriteAllRowsAsync);
@ -290,7 +337,8 @@ public class ListFormSeedDataSynchronizer(
private async Task ExecuteAsync(
string listFormCode,
Func<ListForm, bool> isEnabled,
Func<ListForm, SeedTarget, Task> action)
Func<ListForm, SeedTarget, Task> action,
bool createTarget = true)
{
if (string.IsNullOrWhiteSpace(listFormCode))
return;
@ -309,7 +357,8 @@ public class ListFormSeedDataSynchronizer(
listForm.SeedFilePath.Replace('\\', '/').Trim('/').Split('/'));
// Klasor ilk kayitta olusur; ekran, hedefi diskte hazir bulmak zorunda degildir.
Directory.CreateDirectory(isFolder ? fullPath : Path.GetDirectoryName(fullPath)!);
if (createTarget)
Directory.CreateDirectory(isFolder ? fullPath : Path.GetDirectoryName(fullPath)!);
await action(listForm, new SeedTarget(fullPath, isFolder));
}

View file

@ -188,6 +188,7 @@ public class ListFormWizardAppService(
{
var permReadRecord = new PermissionDefinitionRecord(GuidGenerator.Create(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both);
permReadRecord.SetModules(permissionModules);
permReadRecord.SetPermissionOrder(existingPerms.Where(p => string.IsNullOrWhiteSpace(p.ParentName)).NextPermissionOrder());
permRead = await repoPerm.InsertAsync(permReadRecord, autoSave: false);
inserted.PermissionNames.Add(permRead.Name);
@ -217,16 +218,20 @@ public class ListFormWizardAppService(
: [.. crudPermissions, .. listOnlyPermissions];
var permissionNames = new List<string> { permRead.Name };
// Alt yetkilerin sirasi kardesler arasindadir ve listedeki dizilimi izler (Create = 1, ...).
var childOrder = 0;
foreach (var (suffix, langKey) in childPermissions)
{
var permissionName = $"{code}.{suffix}";
permissionNames.Add(permissionName);
childOrder++;
if (existingPerms.Any(a => a.Name == permissionName))
continue;
var childPermission = new PermissionDefinitionRecord(GuidGenerator.Create(), groupName, permissionName, permRead.Name, langKey, true, MultiTenancySides.Both);
childPermission.SetModules(permissionModules);
childPermission.SetPermissionOrder(childOrder);
await repoPerm.InsertAsync(childPermission, autoSave: false);
inserted.PermissionNames.Add(permissionName);
@ -2306,7 +2311,9 @@ public class ListFormWizardAppService(
if (!await repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await repoPermGroup.InsertAsync(new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName), autoSave: false);
var group = new PermissionGroupDefinitionRecord(GuidGenerator.Create(), groupName, groupName);
group.SetPermissionOrder((await repoPermGroup.GetListAsync()).NextPermissionOrder());
await repoPermGroup.InsertAsync(group, autoSave: false);
// Grup adi ust menu koduyla ayniysa anahtar menu adiyla ortaktir; metin varsa uzerine
// yazmak menu adini bozar, ust menu daha once baska bir wizard'la olustuysa metin hic

View file

@ -33,6 +33,13 @@ namespace Sozsoft.Platform.ListForms;
/// kaynagi) ve diger rollerin yetki atamalari korunur; silinip yeniden uretilseler menu sirasi ve
/// rol atamalari kaybolurdu.
/// </para>
/// <para>
/// Silme yolunda wizard'in <b>baglandigi</b> kaplar da (ust menu zinciri, modul, izin grubu) bosaldiysa
/// silinir: kap bir baska wizard'la olusmus ve o wizard silinirken bu wizard'in menusu yuzunden
/// birakilmis olabilir; sahibi kalmayan kap yeniden deploy'da artik olarak kalirdi. Bu kaplar
/// yalnizca silindiklerinde rapora girer. Silinen menu, modul, yetki ve izin grubu satirlari ekranlarin
/// seed dosyalarindan da cikarilir; aksi halde migrate onlari geri getirirdi.
/// </para>
/// </summary>
public class WizardRecordCleaner(
IRepository<ListForm, Guid> repoListForm,
@ -50,6 +57,7 @@ public class WizardRecordCleaner(
IRepository<BackgroundWorker, Guid> repoBackgroundWorker,
IRepository<IntranetWidget, Guid> repoIntranetWidget,
ITenantRepository tenantRepository,
IListFormSeedDataSynchronizer seedDataSynchronizer,
IConfiguration configuration,
ICurrentTenant currentTenant,
ILogger<WizardRecordCleaner> logger) : ITransientDependency
@ -67,12 +75,38 @@ public class WizardRecordCleaner(
if (wizard?.ComponentKind != WizardComponentKindEnum.Custom && !string.IsNullOrWhiteSpace(wizard?.ListFormCode))
await CleanListFormAsync(wizard.ListFormCode, mode, report);
await CleanMenusAsync(ins.MenuCodes, wizard?.MenuParentCode, mode, report);
var deletedMenus = await CleanMenusAsync(ins.MenuCodes, wizard?.MenuParentCode, mode, report);
var adoptedLanguageKeys = new List<string>();
if (mode == WizardDeleteMode.Delete && wizard?.CreateMenu == true)
{
var parents = deletedMenus.Select(m => m.ParentCode).Append(wizard.MenuParentCode);
var emptyParents = await CleanEmptyParentMenusAsync(parents, deletedMenus, report);
deletedMenus.AddRange(emptyParents);
adoptedLanguageKeys.AddRange(emptyParents.Select(m => m.DisplayName));
}
await CleanPermissionsAsync(ins.PermissionNames, mode, report);
await CleanPermissionGroupsAsync(ins.PermissionGroupNames, mode, report);
await CleanModulesAsync(ins.ModuleIds, mode, report);
await CleanPermissionGroupsAsync(ins.PermissionGroupNames, mode, report, reportKept: true);
await CleanModulesAsync(ins.ModuleIds, mode, report, reportKept: true);
if (mode == WizardDeleteMode.Delete)
{
var groups = await CleanPermissionGroupsAsync(
Adopted([wizard?.PermissionGroupName], ins.PermissionGroupNames), mode, report, reportKept: false);
adoptedLanguageKeys.AddRange(groups);
await CleanModulesAsync(
Adopted([.. deletedMenus.Select(m => m.ModuleId), wizard?.MenuParentModuleId], ins.ModuleIds),
mode, report, reportKept: false);
}
await CleanDataSourcesAsync(ins.DataSourceCodes, mode, report);
await CleanLanguageKeysAsync(ins.LanguageKeys, report);
await CleanLanguageKeysAsync(ins.LanguageKeys, report, reportKept: true);
await CleanLanguageKeysAsync(Adopted(adoptedLanguageKeys, ins.LanguageKeys), report, reportKept: false);
await RemoveSeedRowsAsync(report);
foreach (var item in report)
{
@ -188,8 +222,9 @@ public class WizardRecordCleaner(
/// Altinda baska menu kalan menu silinmez. Wizard'in kendi menusu ust menuden once silinir;
/// ust menunun bos kalip kalmadigi ancak o zaman bilinir.
/// </summary>
private async Task CleanMenusAsync(List<string> codes, string? menuParentCode, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report)
private async Task<List<Menu>> CleanMenusAsync(List<string> codes, string? menuParentCode, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report)
{
var deleted = new List<Menu>();
var pending = new List<string>();
foreach (var code in codes.Distinct(StringComparer.OrdinalIgnoreCase))
{
@ -220,6 +255,7 @@ public class WizardRecordCleaner(
await repoMenu.DeleteAsync(menu, autoSave: true);
Add(report, WizardRecordTypeEnum.Menu, code, WizardDeleteStatusEnum.Deleted);
deleted.Add(menu);
pending.Remove(code);
progressed = true;
}
@ -231,6 +267,39 @@ public class WizardRecordCleaner(
AddKept(report, WizardRecordTypeEnum.Menu, code,
[.. children.Select(c => Ref(WizardRecordTypeEnum.Menu, c.Code))]);
}
return deleted;
}
/// <summary>
/// Ust menu zincirini yukari dogru temizler: alt menusu kalmayan ve kendisi bir adres acmayan
/// (yalnizca kap olan) menu silinir, ardindan onun ustune bakilir. Adresi olan ya da altinda
/// baska menu kalan kayitta zincir durur; bu kayitlar wizard'in olmadigi icin rapora yazilmaz.
/// </summary>
private async Task<List<Menu>> CleanEmptyParentMenusAsync(IEnumerable<string?> parentCodes, List<Menu> alreadyDeleted, List<WizardDeleteLogItemDto> report)
{
var deleted = new List<Menu>();
var visited = new HashSet<string>(alreadyDeleted.Select(m => m.Code), StringComparer.OrdinalIgnoreCase);
var pending = new Queue<string>(parentCodes.Where(c => !string.IsNullOrWhiteSpace(c)).Select(c => c!.Trim()));
while (pending.TryDequeue(out var code))
{
if (!visited.Add(code))
continue;
var menu = await repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (menu == null || !string.IsNullOrWhiteSpace(menu.Url) || await repoMenu.AnyAsync(a => a.ParentCode == code))
continue;
await repoMenu.DeleteAsync(menu, autoSave: true);
Add(report, WizardRecordTypeEnum.Menu, code, WizardDeleteStatusEnum.Deleted);
deleted.Add(menu);
if (!string.IsNullOrWhiteSpace(menu.ParentCode))
pending.Enqueue(menu.ParentCode);
}
return deleted;
}
// ── Yetki ──────────────────────────────────────────────────────────────────────
@ -317,8 +386,13 @@ public class WizardRecordCleaner(
// ── Yetki grubu, modul, veri kaynagi ───────────────────────────────────────────
private async Task CleanPermissionGroupsAsync(List<string> names, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report)
/// <summary>
/// <paramref name="reportKept"/> kapaliyken kayit wizard'in degil, yalnizca baglandigi kaptir:
/// bulunamamasi ya da kullanimda kalmasi rapora yazilmaz. Silinen grup adlarini dondurur.
/// </summary>
private async Task<List<string>> CleanPermissionGroupsAsync(List<string> names, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report, bool reportKept)
{
var deleted = new List<string>();
foreach (var name in names.Distinct(StringComparer.OrdinalIgnoreCase))
{
if (mode == WizardDeleteMode.Redeploy)
@ -330,25 +404,35 @@ public class WizardRecordCleaner(
var group = await repoPermGroup.FirstOrDefaultAsync(a => a.Name == name);
if (group == null)
{
Add(report, WizardRecordTypeEnum.PermissionGroup, name, WizardDeleteStatusEnum.NotFound);
if (reportKept)
Add(report, WizardRecordTypeEnum.PermissionGroup, name, WizardDeleteStatusEnum.NotFound);
continue;
}
var permissions = await repoPerm.GetListAsync(a => a.GroupName == name);
if (permissions.Count > 0)
{
AddKept(report, WizardRecordTypeEnum.PermissionGroup, name,
[.. permissions.Select(p => Ref(WizardRecordTypeEnum.Permission, p.Name))]);
if (reportKept)
{
AddKept(report, WizardRecordTypeEnum.PermissionGroup, name,
[.. permissions.Select(p => Ref(WizardRecordTypeEnum.Permission, p.Name))]);
}
continue;
}
await repoPermGroup.DeleteAsync(group, autoSave: true);
Add(report, WizardRecordTypeEnum.PermissionGroup, name, WizardDeleteStatusEnum.Deleted);
deleted.Add(name);
}
return deleted;
}
/// <summary>Modul; menuler, yetkilerin modul listesi ve kiracilarin modul listesi tarafindan kullanilir.</summary>
private async Task CleanModulesAsync(List<string> ids, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report)
/// <summary>
/// Modul; menuler, yetkilerin modul listesi ve kiracilarin modul listesi tarafindan kullanilir.
/// <paramref name="reportKept"/> izin gruplarindakiyle aynidir.
/// </summary>
private async Task CleanModulesAsync(List<string> ids, WizardDeleteMode mode, List<WizardDeleteLogItemDto> report, bool reportKept)
{
if (ids.Count == 0)
return;
@ -367,7 +451,8 @@ public class WizardRecordCleaner(
var module = await repoModule.FindAsync(id);
if (module == null)
{
Add(report, WizardRecordTypeEnum.Module, id, WizardDeleteStatusEnum.NotFound);
if (reportKept)
Add(report, WizardRecordTypeEnum.Module, id, WizardDeleteStatusEnum.NotFound);
continue;
}
@ -387,7 +472,8 @@ public class WizardRecordCleaner(
if (usedBy.Count > 0)
{
AddKept(report, WizardRecordTypeEnum.Module, id, usedBy);
if (reportKept)
AddKept(report, WizardRecordTypeEnum.Module, id, usedBy);
continue;
}
@ -439,7 +525,7 @@ public class WizardRecordCleaner(
/// basligi onu gosteriyorsa birakilir. Alan basliklari (<c>App.Listform.ListformField.*</c>)
/// ekranlar arasinda ortak oldugu icin bu kontrol olmadan baska ekranlarin basliklari kaybolurdu.
/// </summary>
private async Task CleanLanguageKeysAsync(List<string> keys, List<WizardDeleteLogItemDto> report)
private async Task CleanLanguageKeysAsync(List<string> keys, List<WizardDeleteLogItemDto> report, bool reportKept)
{
var resourceName = PlatformConsts.AppName;
@ -448,7 +534,8 @@ public class WizardRecordCleaner(
var languageKey = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == resourceName && a.Key == key);
if (languageKey == null)
{
Add(report, WizardRecordTypeEnum.LanguageKey, key, WizardDeleteStatusEnum.NotFound);
if (reportKept)
Add(report, WizardRecordTypeEnum.LanguageKey, key, WizardDeleteStatusEnum.NotFound);
continue;
}
@ -466,7 +553,8 @@ public class WizardRecordCleaner(
if (usedBy.Count > 0)
{
AddKept(report, WizardRecordTypeEnum.LanguageKey, key, usedBy);
if (reportKept)
AddKept(report, WizardRecordTypeEnum.LanguageKey, key, usedBy);
continue;
}
@ -479,6 +567,32 @@ public class WizardRecordCleaner(
}
}
// ── Seed dosyalari ─────────────────────────────────────────────────────────────
/// <summary>
/// Silinen kayitlarin ekran seed dosyalarindaki satirlari. Wizard bu dosyalara yazmaz; satir,
/// kayit menu/modul/yetki ekranlarindan duzenlendiginde senkronizasyonla girer ve kayit silindikten
/// sonra dosyada kalirsa migrate onu geri getirir.
/// </summary>
private async Task RemoveSeedRowsAsync(List<WizardDeleteLogItemDto> report)
{
List<string> Deleted(WizardRecordTypeEnum type) =>
[.. report.Where(i => i.RecordType == type && i.Status == WizardDeleteStatusEnum.Deleted).Select(i => i.Name)];
await seedDataSynchronizer.RemoveRowsByFieldAsync(PlatformConsts.AppCodes.Menus.Menu, nameof(Menu.Code), Deleted(WizardRecordTypeEnum.Menu));
await seedDataSynchronizer.RemoveRowsByFieldAsync(PlatformConsts.AppCodes.Menus.Module, nameof(Module.Id), Deleted(WizardRecordTypeEnum.Module));
await seedDataSynchronizer.RemoveRowsByFieldAsync(SeedConsts.AbpIdentity.Permissions.Default, nameof(PermissionDefinitionRecord.Name), Deleted(WizardRecordTypeEnum.Permission));
await seedDataSynchronizer.RemoveRowsByFieldAsync(SeedConsts.AbpIdentity.PermissionGroups.Default, nameof(PermissionGroupDefinitionRecord.Name), Deleted(WizardRecordTypeEnum.PermissionGroup));
}
/// <summary>Wizard'in kendi urettikleri disinda kalan, bos olmayan adaylar.</summary>
private static List<string> Adopted(IEnumerable<string?> candidates, List<string> owned) =>
[.. candidates
.Where(c => !string.IsNullOrWhiteSpace(c))
.Select(c => c!.Trim())
.Where(c => !owned.Contains(c, StringComparer.OrdinalIgnoreCase))
.Distinct(StringComparer.OrdinalIgnoreCase)];
// ── Rapor ──────────────────────────────────────────────────────────────────────
/// <summary>JSON icinde tirnakli deger olarak arar; kod bir baska kodun parcasiysa eslesmez.</summary>

View file

@ -197,8 +197,9 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
input.PermissionGroupName = groupName;
if (!await _repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await _repoPermGroup.InsertAsync(
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
var group = new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName);
group.SetPermissionOrder((await _repoPermGroup.GetListAsync()).NextPermissionOrder());
await _repoPermGroup.InsertAsync(group, autoSave: true);
}
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
@ -210,30 +211,32 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
var permissionModules = input.MenuParentModuleId?.Trim();
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
permRead ??= await InsertPermissionAsync(groupName, code, null, nameLangKey, permissionModules);
permRead ??= await InsertPermissionAsync(groupName, code, null, nameLangKey, permissionModules,
existingPerms.Where(p => string.IsNullOrWhiteSpace(p.ParentName)).NextPermissionOrder());
// CRUD üçlüsü her iki yolda da üretilir: Custom component de Form
// üzerinden kayıt ekler, günceller ve siler (AppService.Create ile aynı davranış).
// Alt yetkilerin sirasi kardesler arasindadir ve bu dizilimi izler (Create = 1, ...).
if (!existingPerms.Any(a => a.Name == permCreateName))
await InsertPermissionAsync(groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, permissionModules);
await InsertPermissionAsync(groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, permissionModules, 1);
if (!existingPerms.Any(a => a.Name == permUpdateName))
await InsertPermissionAsync(groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, permissionModules);
await InsertPermissionAsync(groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, permissionModules, 2);
if (!existingPerms.Any(a => a.Name == permDeleteName))
await InsertPermissionAsync(groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, permissionModules);
await InsertPermissionAsync(groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, permissionModules, 3);
// Export/Import/Note yalnızca ListForm ekosisteminde karşılığı olan işlemlerdir.
if (!isCustomComponent)
{
if (!existingPerms.Any(a => a.Name == permExportName))
await InsertPermissionAsync(groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, permissionModules);
await InsertPermissionAsync(groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, permissionModules, 4);
if (!existingPerms.Any(a => a.Name == permImportName))
await InsertPermissionAsync(groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, permissionModules);
await InsertPermissionAsync(groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, permissionModules, 5);
if (!existingPerms.Any(a => a.Name == permNoteName))
await InsertPermissionAsync(groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, permissionModules);
await InsertPermissionAsync(groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, permissionModules, 6);
}
// // Permission Grants - Admin role için, sadece eksik olanları ekle
@ -439,11 +442,13 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
string name,
string parentName,
string displayName,
string modules)
string modules,
int order)
{
var permission = new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, name, parentName, displayName, true, MultiTenancySides.Both);
permission.SetModules(modules);
permission.SetPermissionOrder(order);
return await _repoPerm.InsertAsync(permission, autoSave: true);
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Data;
using Volo.Abp.PermissionManagement;
@ -48,5 +49,20 @@ public static class AbpPermissionsExtensions
.Distinct()
.ToArray();
}
/// <summary>
/// Yetki ekranindaki sira (<see cref="PlatformConsts.Permissions.Order"/>) kardesler arasindadir;
/// yeni kayit en buyuk kardes sirasinin bir fazlasini alir, kardes yoksa 1. Bos birakilan sira
/// 0 olur ve kayit listenin en ustune cikar.
/// </summary>
public static int NextPermissionOrder(this IEnumerable<IHasExtraProperties> siblings)
{
return siblings.Select(s => s.GetProperty<int>(PlatformConsts.Permissions.Order)).DefaultIfEmpty(0).Max() + 1;
}
public static void SetPermissionOrder(this IHasExtraProperties record, int order)
{
record.SetProperty(PlatformConsts.Permissions.Order, order);
}
}