Wizard Delete eksiklikleri
This commit is contained in:
parent
9a14be4ee4
commit
9816fb91ce
7 changed files with 238 additions and 29 deletions
15
.github/instructions/lowcode.instructions.md
vendored
15
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,6 +357,7 @@ public class ListFormSeedDataSynchronizer(
|
|||
listForm.SeedFilePath.Replace('\\', '/').Trim('/').Split('/'));
|
||||
|
||||
// Klasor ilk kayitta olusur; ekran, hedefi diskte hazir bulmak zorunda degildir.
|
||||
if (createTarget)
|
||||
Directory.CreateDirectory(isFolder ? fullPath : Path.GetDirectoryName(fullPath)!);
|
||||
|
||||
await action(listForm, new SeedTarget(fullPath, isFolder));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
if (reportKept)
|
||||
Add(report, WizardRecordTypeEnum.PermissionGroup, name, WizardDeleteStatusEnum.NotFound);
|
||||
continue;
|
||||
}
|
||||
|
||||
var permissions = await repoPerm.GetListAsync(a => a.GroupName == name);
|
||||
if (permissions.Count > 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/// <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,6 +451,7 @@ public class WizardRecordCleaner(
|
|||
var module = await repoModule.FindAsync(id);
|
||||
if (module == null)
|
||||
{
|
||||
if (reportKept)
|
||||
Add(report, WizardRecordTypeEnum.Module, id, WizardDeleteStatusEnum.NotFound);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -387,6 +472,7 @@ public class WizardRecordCleaner(
|
|||
|
||||
if (usedBy.Count > 0)
|
||||
{
|
||||
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,6 +534,7 @@ public class WizardRecordCleaner(
|
|||
var languageKey = await repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == resourceName && a.Key == key);
|
||||
if (languageKey == null)
|
||||
{
|
||||
if (reportKept)
|
||||
Add(report, WizardRecordTypeEnum.LanguageKey, key, WizardDeleteStatusEnum.NotFound);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -466,6 +553,7 @@ public class WizardRecordCleaner(
|
|||
|
||||
if (usedBy.Count > 0)
|
||||
{
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue