application-configuration içerisine Menus alt kısım eklendi
This commit is contained in:
parent
679f8fcb89
commit
df3294e02a
13 changed files with 216 additions and 163 deletions
|
|
@ -1250,7 +1250,7 @@ ekranın kendi yetki hatasını gösterir; ayrıca gizlemek istiyorsan `designer
|
|||
Gömülen görünüm, hedef ekranın `LayoutJson`'ında açık olmalıdır.
|
||||
|
||||
**Public site düğümleri** yalnızca public bileşenlerde (`PublicHeader`, `PublicFooter`, public
|
||||
sayfalar) anlamlıdır; veriyi uygulama konfigürasyonundan (`publicMenus`, tema, dil) okur:
|
||||
sayfalar) anlamlıdır; veriyi uygulama konfigürasyonundan (`menus.public`, tema, dil) okur:
|
||||
|
||||
| Düğüm | Prop'lar (varsayılan) |
|
||||
| --- | --- |
|
||||
|
|
@ -1582,7 +1582,7 @@ Intranet de bu moduldedir: menü `Hr.Intranet`, ekran/yetki kodları `Hr.Intrane
|
|||
(Developer Kit › Intranet Widgets) Saas'ta kalır; kayıtlarının `PermissionName` değerleri `Hr.Intranet.*.Widget`'tır.
|
||||
|
||||
İK yetkilerinin `Modules` değeri `Sas|Hr`'dir: host (`Sas`) her zaman görür, kiracı yalnızca modül
|
||||
listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `MenuAppService`,
|
||||
listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `MenuNavigationProvider`,
|
||||
`RolesPermission.tsx`). Yetki ağacı: `Hr.Definitions` → `Hr.SkillType` (1),
|
||||
`Hr.SkillLevel` (2), `Hr.Skill` (3), `Hr.Department` (4), `Hr.JobPosition` (5);
|
||||
`Hr.OrgChart` grubun kökünde (2). ListForm ekranlarının alt yetkileri `.Create`, `.Update`,
|
||||
|
|
@ -1685,7 +1685,7 @@ yazma; tasarımcıda yeni bir Custom Component + `Pub` menü kaydı (`Pub.Header
|
|||
| Route | Bileşen | Veri |
|
||||
| --- | --- | --- |
|
||||
| `/home` · `/about` · `/contact` | Custom Component (`PublicHomePage` · `PublicAboutPage` · `PublicContactPage`) | `data/Sas.DeveloperKit.CustomComponents/{Id}.json` · SEO bileşen kaydında |
|
||||
| (rotasız) | Custom Component (`PublicHeader` · `PublicFooter`) | Üst menü + alt bilgi hızlı bağlantıları: `Pub` menüsü `Pub.Header` (uygulama konfigürasyonunun `publicMenus` alanı) |
|
||||
| (rotasız) | Custom Component (`PublicHeader` · `PublicFooter`) | Üst menü + alt bilgi hızlı bağlantıları: `Pub` menüsü `Pub.Header` (uygulama konfigürasyonunun `menus.public` alanı) |
|
||||
| `/products` · `/checkout` · `/payment` · `/success` | `views/public/{Products,Checkout,Payment,Success}` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` · `Sas_H_Order` |
|
||||
| `/blog` · `/blog/:id` | `views/public/{Blog,BlogDetail}` | `Sas_H_BlogPost` · `Sas_H_BlogCategory` |
|
||||
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |
|
||||
|
|
|
|||
|
|
@ -666,7 +666,7 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
|||
silmek ya da yolunu değiştirmek çerçeve dışıdır.
|
||||
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
||||
`Pub.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
||||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `menus.public` alanıyla taşınır
|
||||
(menü ucu `[Authorize]`). Sayfa ise Custom Component'tir: `/admin/` ile
|
||||
başlamayan `RoutePath` otomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler;
|
||||
`RoutePath` boş bırakılabilir (rotasız bileşen). Yeni public sayfa =
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -98,7 +98,9 @@ React/C# kodu yazmadan, yalnızca konfigürasyon ile mümkündür.
|
|||
```
|
||||
|
||||
**Akışın özeti:** Kullanıcı giriş yapar → ABP `application-configuration` yanıtına
|
||||
`PlatformApplicationConfigurationContributor` menü, route ve yetkileri ekler → `DynamicRouter`
|
||||
`PlatformApplicationConfigurationContributor` menüleri (`extraProperties.menus`: `public` ziyaretçi
|
||||
menüsü, `protected` kullanıcının yetkili yönetim navigasyonu — ayrı bir `/api/app/menu` isteği yoktur,
|
||||
süzgeç `MenuNavigationProvider`), route ve yetkileri ekler → `DynamicRouter`
|
||||
route kayıtlarını React bileşenlerine bağlar → `/admin/list/{ListFormCode}` gibi bir ekran
|
||||
açıldığında `ListFormsAppService` metadata'yı, `ListFormDataAppService` ise veriyi döner →
|
||||
DevExtreme bileşenleri bu metadata'ya göre kendini kurar.
|
||||
|
|
@ -988,7 +990,8 @@ Route.Authority ──┘ └► Permission
|
|||
`data/Sas.Menus.Module.json`): `Sas` → `Sas` (Saas), `Adm` → `Adm`,
|
||||
`Hr` → `Hr` (İK), `Pub` → `Pub.Header` (public site). Tablo ön eki aynı modülden gelir
|
||||
(`MenuPrefix`: `Sas_`, `Adm_`, `Hr_`; `TableNameResolver`). Her yetki satırı `Modules`
|
||||
(pipe ile ayrılmış) değeri taşır; kiracının modül listesi doluysa menü (`MenuAppService`) ve rol
|
||||
(pipe ile ayrılmış) değeri taşır; kiracının modül listesi doluysa menü (`MenuNavigationProvider`;
|
||||
hem `menus.protected` hem Menü Yönetimi listesi bu süzgeçten geçer) ve rol
|
||||
yetki ekranı (`RolesPermission.tsx`) yalnızca kesişen modüllerin yetkilerini gösterir. Sözleşme:
|
||||
`Sas` host'ta görünürlüğü, `Adm` ve `Hr` kiracıdaki görünürlüğü belirler — Saas/Administration
|
||||
yetkileri `Sas|Adm`, İK yetkileri `Sas|Hr` taşır.
|
||||
|
|
@ -1133,9 +1136,9 @@ menü bir kez eklenir iki yerde birden görünür; alt kaydı olan bir menü üs
|
|||
Alt bilgide `cta` kayıtları (Giriş) yer almaz: oradaki düğüm `source="navigation"` ile çizilir. `Url` sayfa yolu, `Icon`
|
||||
react-icons adı, `Order` sıra, `IsDisabled` yayın anahtarı, `DisplayName` dil anahtarıdır;
|
||||
`CssClass` içinde `cta` geçen kayıt düz bağlantı yerine vurgulu buton olarak çizilir. Menü ucu
|
||||
`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||||
`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `menus.public` alanıyla taşınır
|
||||
(`PlatformApplicationConfigurationContributor`), istemcide `usePublicMenu` okur. `Pub` modülü
|
||||
yönetim navigasyonuna girmez (`abpConfig.model.ts` menüyü kurarken bu modülü eler; Menü Yönetimi
|
||||
yönetim navigasyonuna (`menus.protected`) girmez (`MenuNavigationProvider` bu modülü eler; Menü Yönetimi
|
||||
ekranı elemez, menüler oradan düzenlenir). Seed: `configs/seeds/data/Sas.Menus.Module.json` ve
|
||||
`Sas.Menus.Menu.json`.
|
||||
|
||||
|
|
@ -1954,6 +1957,9 @@ konfigürasyonu ile üretilir.
|
|||
|
||||
- **easy-peasy** global store: `auth`, `theme`, `locale`, `abpConfig`, `admin`, `client`, `base`.
|
||||
Oturum ve tercih verileri `localStorage` yerine store üzerinden yönetilir (`stateSync.ts`).
|
||||
Kenar çubuğu (`abpConfig.menu.mainMenu`) ayrı bir menü isteğiyle değil, `getConfig` içinde
|
||||
application-configuration'ın `extraProperties.menus.protected` listesi + yüklü dil metinlerinden
|
||||
kurulur; dil, yetki ya da menü kaydından sonra `getConfig` çağrısı menüyü de tazeler.
|
||||
- **React Query**: sunucu verisi önbellekleme.
|
||||
- **Context'ler**: `ComponentContext` (runtime bileşen kaydı), `DemoContext` (demo talebi diyaloğu).
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
namespace Sozsoft.Platform.Menus;
|
||||
|
||||
/// <summary>
|
||||
/// Yönetim navigasyonunun application-configuration ile taşınan menü satırı. Yalnızca
|
||||
/// istemcinin ağacı kurmak için okuduğu alanları taşır; düzenleme ekranı <see cref="MenuDto"/> kullanır.
|
||||
/// </summary>
|
||||
public class MenuNavigationDto
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public int Order { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public string ParentCode { get; set; }
|
||||
public string RequiredPermissionName { get; set; }
|
||||
public string ModuleId { get; set; }
|
||||
}
|
||||
|
|
@ -4,18 +4,14 @@ using System.Linq;
|
|||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sozsoft.Languages.Caching;
|
||||
using Sozsoft.Languages.Entities;
|
||||
using Sozsoft.Languages.Languages;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
using Volo.Abp.Application.Services;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.PermissionManagement;
|
||||
using Volo.Abp.TenantManagement;
|
||||
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
|
||||
using Volo.Abp;
|
||||
|
||||
|
|
@ -31,8 +27,7 @@ public class MenuAppService : CrudAppService<
|
|||
private readonly IRepository<Menu, Guid> _menuRepository;
|
||||
private readonly IRepository<LanguageKey, Guid> _repositoryKey;
|
||||
private readonly IRepository<LanguageText, Guid> _repositoryText;
|
||||
private readonly ITenantRepository _tenantRepository;
|
||||
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
|
||||
private readonly MenuNavigationProvider _navigationProvider;
|
||||
private readonly DistributedCacheCleaner _cacheCleaner;
|
||||
private readonly IRepository<Module, string> _moduleRepository;
|
||||
private readonly IListFormSeedDataSynchronizer _seedDataSynchronizer;
|
||||
|
|
@ -41,8 +36,7 @@ public class MenuAppService : CrudAppService<
|
|||
IRepository<Menu, Guid> menuRepository,
|
||||
IRepository<LanguageKey, Guid> languageKeyRepository,
|
||||
IRepository<LanguageText, Guid> languageTextRepository,
|
||||
ITenantRepository tenantRepository,
|
||||
IPermissionDefinitionRecordRepository permissionRepository,
|
||||
MenuNavigationProvider navigationProvider,
|
||||
DistributedCacheCleaner cacheCleaner,
|
||||
IRepository<Module, string> moduleRepository,
|
||||
IListFormSeedDataSynchronizer seedDataSynchronizer
|
||||
|
|
@ -51,8 +45,7 @@ public class MenuAppService : CrudAppService<
|
|||
_menuRepository = menuRepository;
|
||||
_repositoryKey = languageKeyRepository;
|
||||
_repositoryText = languageTextRepository;
|
||||
_tenantRepository = tenantRepository;
|
||||
_permissionRepository = permissionRepository;
|
||||
_navigationProvider = navigationProvider;
|
||||
_cacheCleaner = cacheCleaner;
|
||||
_moduleRepository = moduleRepository;
|
||||
_seedDataSynchronizer = seedDataSynchronizer;
|
||||
|
|
@ -68,34 +61,10 @@ public class MenuAppService : CrudAppService<
|
|||
|
||||
var query = await CreateFilteredQueryAsync(input);
|
||||
query = query.Where(a => !a.IsDisabled);
|
||||
|
||||
//Tenant üzerinden modül bilgisi alınıp yalnızca o modüllerin menüleri listelenecek
|
||||
if (CurrentTenant.Id is Guid tenantId)
|
||||
{
|
||||
var tenant = await _tenantRepository.FindAsync(tenantId);
|
||||
if (tenant != null)
|
||||
{
|
||||
var tenantModules = tenant.GetModules();
|
||||
if (tenantModules.Length > 0)
|
||||
{
|
||||
var permissionRecords = await _permissionRepository.GetListAsync();
|
||||
var allowedPermissionNames = permissionRecords
|
||||
.Where(p => p.GetModules().Intersect(tenantModules).Any())
|
||||
.Select(p => p.Name)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
query = query.Where(menu =>
|
||||
string.IsNullOrEmpty(menu.RequiredPermissionName) ||
|
||||
allowedPermissionNames.Contains(menu.RequiredPermissionName));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
query = await _navigationProvider.ApplyTenantModuleFilterAsync(query);
|
||||
|
||||
var totalCount = await AsyncExecuter.CountAsync(query);
|
||||
|
||||
var entities = new List<Menu>();
|
||||
var entityDtos = new List<MenuDto>();
|
||||
|
||||
if (totalCount > 0)
|
||||
|
|
@ -103,38 +72,7 @@ public class MenuAppService : CrudAppService<
|
|||
query = ApplySorting(query, input);
|
||||
query = ApplyPaging(query, input);
|
||||
|
||||
var items = await AsyncExecuter.ToListAsync(query);
|
||||
|
||||
// Tüm unique permission'ları topla
|
||||
var uniquePermissions = items
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName))
|
||||
.Select(x => x.RequiredPermissionName)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// Tüm permission'ları bir kerede kontrol et (N+1 query problemini önler)
|
||||
var grantedPermissions = new HashSet<string>();
|
||||
foreach (var permission in uniquePermissions)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await AuthorizationService.IsGrantedAsync(permission))
|
||||
{
|
||||
grantedPermissions.Add(permission);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, $"Permission error for {permission}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Sadece yetkili menüleri filtrele
|
||||
entities = items
|
||||
.Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) ||
|
||||
grantedPermissions.Contains(item.RequiredPermissionName))
|
||||
.ToList();
|
||||
|
||||
var entities = await _navigationProvider.FilterGrantedAsync(await AsyncExecuter.ToListAsync(query));
|
||||
entityDtos = await base.MapToGetListOutputDtosAsync(entities);
|
||||
|
||||
var languageKeys = entityDtos
|
||||
|
|
@ -291,22 +229,6 @@ public class MenuAppService : CrudAppService<
|
|||
await _seedDataSynchronizer.RemoveRowsAsync(AppCodes.Menus.Menu, [menu.Code]);
|
||||
}
|
||||
|
||||
public async Task<List<MenuDto>> GetListMainMenuAsync()
|
||||
{
|
||||
await CheckGetPolicyAsync();
|
||||
|
||||
var query = await _menuRepository.GetQueryableAsync();
|
||||
// Public site menusu yonetim kenar cubugunda yer almaz; o agac ziyaretci
|
||||
// duzenine aittir ve uygulama konfigurasyonu uzerinden tasinir.
|
||||
query = query.Where(a => !a.IsDisabled && a.ModuleId != null &&
|
||||
a.ModuleId != PlatformConsts.PublicSite.ModuleId);
|
||||
|
||||
var entities = await AsyncExecuter.ToListAsync(query);
|
||||
var entityDtos = await MapToGetListOutputDtosAsync(entities);
|
||||
|
||||
return entityDtos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menüyü tüm alanlarıyla günceller ve görünen adın dil metinlerini de yazar.
|
||||
/// Kod (Code) dil anahtarı ve yetki eşleşmesi olduğu için değiştirilemez; gönderilen
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Volo.Abp.Authorization;
|
||||
using Volo.Abp.DependencyInjection;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Linq;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.PermissionManagement;
|
||||
using Volo.Abp.TenantManagement;
|
||||
|
||||
namespace Sozsoft.Platform.Menus;
|
||||
|
||||
/// <summary>
|
||||
/// Oturum açık kullanıcının görebileceği menüleri süzer: kiracının modül listesi dışındaki
|
||||
/// yetkiye bağlı menü ve kullanıcıya verilmemiş yetkili menü elenir. Yönetim navigasyonu
|
||||
/// (application-configuration `menus`) ve menü düzenleme listesi aynı süzgeçten geçer; iki
|
||||
/// kopya olsaydı kenar çubuğu ile düzenleme ekranı farklı menü gösterirdi.
|
||||
/// </summary>
|
||||
public class MenuNavigationProvider(
|
||||
IRepository<Menu, Guid> menuRepository,
|
||||
ITenantRepository tenantRepository,
|
||||
IPermissionDefinitionRecordRepository permissionRepository,
|
||||
ICurrentTenant currentTenant,
|
||||
IAuthorizationService authorizationService,
|
||||
IAsyncQueryableExecuter asyncExecuter,
|
||||
ILogger<MenuNavigationProvider> logger) : ITransientDependency
|
||||
{
|
||||
/// <summary>Yönetim kenar çubuğunun menüleri: pasifler ve public site ağacı dışarıda, sıra `Order`.</summary>
|
||||
public async Task<List<MenuNavigationDto>> GetNavigationAsync()
|
||||
{
|
||||
var query = (await menuRepository.GetQueryableAsync())
|
||||
.Where(menu => !menu.IsDisabled && menu.ModuleId != null &&
|
||||
menu.ModuleId != PlatformConsts.PublicSite.ModuleId);
|
||||
query = await ApplyTenantModuleFilterAsync(query);
|
||||
|
||||
var menus = await FilterGrantedAsync(await asyncExecuter.ToListAsync(query.OrderBy(menu => menu.Order)));
|
||||
|
||||
return menus.Select(menu => new MenuNavigationDto
|
||||
{
|
||||
Code = menu.Code,
|
||||
DisplayName = menu.DisplayName,
|
||||
Order = menu.Order,
|
||||
Url = menu.Url,
|
||||
Icon = menu.Icon,
|
||||
ParentCode = menu.ParentCode,
|
||||
RequiredPermissionName = menu.RequiredPermissionName,
|
||||
ModuleId = menu.ModuleId
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kiracının modül listesi doluysa yalnızca o modüllerin yetkisine bağlı (ya da yetkisiz)
|
||||
/// menüler kalır; liste boş kiracıda süzme yoktur.
|
||||
/// </summary>
|
||||
public async Task<IQueryable<Menu>> ApplyTenantModuleFilterAsync(IQueryable<Menu> query)
|
||||
{
|
||||
if (currentTenant.Id is not Guid tenantId)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
var tenant = await tenantRepository.FindAsync(tenantId);
|
||||
var tenantModules = tenant?.GetModules() ?? [];
|
||||
if (tenantModules.Length == 0)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
var allowedPermissionNames = (await permissionRepository.GetListAsync())
|
||||
.Where(permission => permission.GetModules().Intersect(tenantModules).Any())
|
||||
.Select(permission => permission.Name)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
return query.Where(menu =>
|
||||
string.IsNullOrEmpty(menu.RequiredPermissionName) ||
|
||||
allowedPermissionNames.Contains(menu.RequiredPermissionName));
|
||||
}
|
||||
|
||||
/// <summary>Yetki gerektiren menülerden yalnızca kullanıcıya verilmiş olanları bırakır; her yetki bir kez sorulur.</summary>
|
||||
public async Task<List<Menu>> FilterGrantedAsync(IReadOnlyCollection<Menu> menus)
|
||||
{
|
||||
var grantedPermissions = new HashSet<string>();
|
||||
foreach (var permission in menus
|
||||
.Select(menu => menu.RequiredPermissionName)
|
||||
.Where(name => !string.IsNullOrWhiteSpace(name))
|
||||
.Distinct())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await authorizationService.IsGrantedAsync(permission))
|
||||
{
|
||||
grantedPermissions.Add(permission);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Tanımı silinmiş bir yetkiye bağlı menü navigasyonu düşürmemeli; menü gizlenir.
|
||||
logger.LogError(ex, "Permission check failed for menu permission {Permission}", permission);
|
||||
}
|
||||
}
|
||||
|
||||
return menus
|
||||
.Where(menu => string.IsNullOrWhiteSpace(menu.RequiredPermissionName) ||
|
||||
grantedPermissions.Contains(menu.RequiredPermissionName))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Identity;
|
||||
using Sozsoft.Platform.Menus;
|
||||
using Sozsoft.Platform.Public;
|
||||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
|
||||
using Volo.Abp.Data;
|
||||
|
|
@ -54,13 +55,20 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
|
|||
context.ApplicationConfiguration.SetProperty(
|
||||
"customComponents", await customComponentRepository.GetListAsync());
|
||||
|
||||
// Public site menusu: ziyaretci menu ucunu cagiramaz ([Authorize]), bu yuzden
|
||||
// rotalar ve custom component'ler gibi anonim erisilen konfigurasyonla tasinir.
|
||||
var menuRepository = context.ServiceProvider.GetRequiredService<IRepository<Menu, Guid>>();
|
||||
var publicMenus = await menuRepository.GetListAsync(
|
||||
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled);
|
||||
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
|
||||
// tarayici basligi ve paylasim karti bir kare gec dolar.
|
||||
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
|
||||
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
|
||||
|
||||
context.ApplicationConfiguration.SetProperty("publicMenus", publicMenus
|
||||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
|
||||
// Menüler tek başlıkta taşınır: `public` ziyaretçi menüsüdür (menü ucu [Authorize]
|
||||
// olduğu için ayrı istek atamaz), `protected` oturum açık kullanıcının yönetim navigasyonudur.
|
||||
// İstemci dil/yetki değişiminde konfigürasyonu zaten yeniden okur; ayrı bir menü isteği
|
||||
// aynı veriyi ikinci kez taşırdı.
|
||||
var menuRepository = context.ServiceProvider.GetRequiredService<IRepository<Menu, Guid>>();
|
||||
var publicMenus = (await menuRepository.GetListAsync(
|
||||
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled))
|
||||
.OrderBy(menu => menu.Order)
|
||||
.Select(menu => new
|
||||
{
|
||||
|
|
@ -74,14 +82,12 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
|
|||
target = menu.Target,
|
||||
requiredPermissionName = menu.RequiredPermissionName
|
||||
})
|
||||
.ToList());
|
||||
.ToList();
|
||||
var protectedMenus = currentUser.Id.HasValue
|
||||
? await context.ServiceProvider.GetRequiredService<MenuNavigationProvider>().GetNavigationAsync()
|
||||
: [];
|
||||
context.ApplicationConfiguration.SetProperty("menus", new { @public = publicMenus, @protected = protectedMenus });
|
||||
|
||||
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
|
||||
// tarayici basligi ve paylasim karti bir kare gec dolar.
|
||||
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
|
||||
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
|
||||
|
||||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||
if (currentUser.Id.HasValue)
|
||||
{
|
||||
var sessionRepository = context.ServiceProvider.GetRequiredService<IIdentitySessionRepository>();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { FullAuditedEntityDto } from '../abp'
|
||||
import type { PublicMenuItem } from '@/utils/hooks/usePublicMenu'
|
||||
|
||||
export interface MenuDto extends FullAuditedEntityDto<string> {
|
||||
code?: string
|
||||
|
|
@ -19,3 +20,15 @@ export interface MenuDto extends FullAuditedEntityDto<string> {
|
|||
menuTextTr?: string
|
||||
menuTextEn?: string
|
||||
}
|
||||
|
||||
/** application-configuration `extraProperties.menus.protected` satırı: yönetim navigasyonunu kuran alanlar. */
|
||||
export type MenuNavigationDto = Pick<
|
||||
MenuDto,
|
||||
'code' | 'displayName' | 'order' | 'url' | 'icon' | 'parentCode' | 'requiredPermissionName' | 'moduleId'
|
||||
>
|
||||
|
||||
/** application-configuration `extraProperties.menus`: ziyaretçi menüsü + oturum açık kullanıcının navigasyonu. */
|
||||
export interface ConfigMenus {
|
||||
public: PublicMenuItem[]
|
||||
protected: MenuNavigationDto[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,15 +94,6 @@ export class MenuService {
|
|||
{ apiName: this.apiName },
|
||||
)
|
||||
|
||||
getListMainMenu = () =>
|
||||
apiService.fetchData<MenuDto[]>(
|
||||
{
|
||||
method: 'GET',
|
||||
url: `/api/app/menu/main-menu`,
|
||||
},
|
||||
{ apiName: this.apiName },
|
||||
)
|
||||
|
||||
getModules = () =>
|
||||
apiService.fetchData<ModuleLookupDto[]>(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import type { Action, Thunk } from 'easy-peasy'
|
||||
import { action, thunk } from 'easy-peasy'
|
||||
import { Injections, StoreModel } from '.'
|
||||
import { Injections } from '.'
|
||||
import { getLocalization, setLocalization } from '../services/localization.service'
|
||||
import appConfig from '../proxy/configs/app.config'
|
||||
import { NavigationTree } from '../proxy/menus/navigation'
|
||||
import { MenuDto } from '../proxy/menus/models'
|
||||
import { ConfigMenus, MenuNavigationDto } from '../proxy/menus/models'
|
||||
import getChildren from '../utils/navigation'
|
||||
import { PUBLIC_MENU_MODULE_ID } from '../utils/hooks/usePublicMenu'
|
||||
import {
|
||||
ApplicationConfigurationDto,
|
||||
ApplicationLocalizationRequestDto,
|
||||
|
|
@ -30,6 +29,23 @@ const getWarmupRoutes = (routes: RouteDto[] | undefined, granted: Record<string,
|
|||
)
|
||||
.map((route) => route.componentPath.replace(/^@\//, ''))
|
||||
|
||||
/**
|
||||
* Yönetim kenar çubuğu, application-configuration'ın `extraProperties.menus.protected` alanından kurulur;
|
||||
* sunucu pasif, public ve yetkisiz menüleri zaten elemiştir. Görünen ad etkin dilin metnidir.
|
||||
*/
|
||||
const buildMainMenu = (
|
||||
menus: MenuNavigationDto[] | undefined,
|
||||
texts: Texts | undefined,
|
||||
defaultResourceName: string | undefined,
|
||||
): NavigationTree[] =>
|
||||
getChildren(
|
||||
(menus ?? []).map((menu) => ({
|
||||
...menu,
|
||||
displayName: getLocalization(texts ?? {}, defaultResourceName, '::' + menu.displayName),
|
||||
})),
|
||||
null,
|
||||
)
|
||||
|
||||
export interface GetConfigOptions {
|
||||
/** Localization kaynaklarını da zorla yeniden yükle. */
|
||||
reloadTexts?: boolean
|
||||
|
|
@ -51,7 +67,6 @@ export interface AbpConfigModel {
|
|||
mainMenu: NavigationTree[]
|
||||
}
|
||||
setMenu: Action<AbpConfigModel, NavigationTree[]>
|
||||
getMenu: Thunk<AbpConfigModel, never, Injections, StoreModel>
|
||||
}
|
||||
|
||||
export const abpConfigModel: AbpConfigModel = {
|
||||
|
|
@ -98,10 +113,15 @@ export const abpConfigModel: AbpConfigModel = {
|
|||
: [],
|
||||
)
|
||||
|
||||
//Eğer login değilse
|
||||
if (result.data.currentUser.isAuthenticated) {
|
||||
await actions.getMenu()
|
||||
}
|
||||
actions.setMenu(
|
||||
result.data.currentUser.isAuthenticated
|
||||
? buildMainMenu(
|
||||
(result.data.extraProperties?.['menus'] as ConfigMenus | undefined)?.protected,
|
||||
helpers.getState().texts,
|
||||
result.data.localization.defaultResourceName,
|
||||
)
|
||||
: [],
|
||||
)
|
||||
}),
|
||||
texts: undefined,
|
||||
setTexts: action((state, payload) => {
|
||||
|
|
@ -118,34 +138,4 @@ export const abpConfigModel: AbpConfigModel = {
|
|||
setMenu: action((state, payload) => {
|
||||
state.menu.mainMenu = [...payload]
|
||||
}),
|
||||
getMenu: thunk(async (actions, _, { injections, getState, getStoreState }) => {
|
||||
const { signedIn } = getStoreState().auth.session
|
||||
if (!signedIn) {
|
||||
return
|
||||
}
|
||||
|
||||
const result = await injections.menuService.getList(
|
||||
{
|
||||
sorting: 'order',
|
||||
skipCount: 0,
|
||||
maxResultCount: 1000,
|
||||
}
|
||||
)
|
||||
|
||||
const texts = getState().texts
|
||||
const defaultResourceName = getState().config?.localization.defaultResourceName
|
||||
|
||||
// Public site menusu ziyaretci duzenine aittir; yonetim navigasyonunda yer almaz.
|
||||
const items = (result.data.items ?? []).filter(
|
||||
(menu: MenuDto) => menu.moduleId !== PUBLIC_MENU_MODULE_ID,
|
||||
)
|
||||
|
||||
items.forEach((menu: MenuDto) => {
|
||||
const displayName = '::' + menu.displayName
|
||||
menu.displayName = getLocalization(texts || {}, defaultResourceName, displayName)
|
||||
})
|
||||
|
||||
const menu = getChildren(items, null)
|
||||
actions.setMenu(menu)
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { ClientModel, clientModel } from './client.model'
|
|||
import { LocaleModel, localeModel } from './locale.model'
|
||||
import { ThemeModel, themeModel } from './theme.model'
|
||||
import { refreshToken } from '../services/auth.service'
|
||||
import { MenuService } from '../services/menu.service'
|
||||
import { initStateSync, stateSyncMiddleware } from './stateSync'
|
||||
|
||||
export interface StoreModel {
|
||||
|
|
@ -22,11 +21,8 @@ export interface StoreModel {
|
|||
admin: AdminModel
|
||||
}
|
||||
|
||||
const menuService = new MenuService()
|
||||
|
||||
const injections = {
|
||||
abpConfigService,
|
||||
menuService,
|
||||
listFormService,
|
||||
refreshToken,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { useMemo } from 'react'
|
||||
import { useStoreState } from '@/store'
|
||||
import type { ConfigMenus } from '@/proxy/menus/models'
|
||||
|
||||
/**
|
||||
* Public site menu kaydi. Menuler `Pub` modulu altinda tutulur ve uygulama
|
||||
* konfigurasyonunun `publicMenus` alaniyla tasinir; menu ucu `[Authorize]`
|
||||
* konfigurasyonunun `menus.public` alaniyla tasinir; menu ucu `[Authorize]`
|
||||
* oldugu icin ziyaretci ayri bir istek atamaz.
|
||||
*/
|
||||
export interface PublicMenuItem {
|
||||
|
|
@ -25,9 +26,6 @@ export interface PublicMenuNode extends PublicMenuItem {
|
|||
/** Ust menude bu sinifi tasiyan kayit duz baglanti yerine vurgulu buton olarak cizilir. */
|
||||
export const PUBLIC_MENU_CTA_CLASS = 'cta'
|
||||
|
||||
/** Public site menulerinin modulu; yonetim navigasyonu bu modulu disarida birakir. */
|
||||
export const PUBLIC_MENU_MODULE_ID = 'Pub'
|
||||
|
||||
/** Ust ve alt bilgiyi cizen Custom Component'lerin adlari. */
|
||||
export const PUBLIC_HEADER_COMPONENT = 'PublicHeader'
|
||||
export const PUBLIC_FOOTER_COMPONENT = 'PublicFooter'
|
||||
|
|
@ -51,8 +49,8 @@ function buildChildren(
|
|||
*/
|
||||
export function usePublicMenu() {
|
||||
const publicMenus = useStoreState(
|
||||
(state) => state.abpConfig.config?.extraProperties?.['publicMenus'],
|
||||
) as PublicMenuItem[] | undefined
|
||||
(state) => (state.abpConfig.config?.extraProperties?.['menus'] as ConfigMenus | undefined)?.public,
|
||||
)
|
||||
const grantedPolicies = useStoreState((state) => state.abpConfig.config?.auth?.grantedPolicies)
|
||||
|
||||
return useMemo(() => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
NAV_ITEM_TYPE_ITEM,
|
||||
NAV_ITEM_TYPE_TITLE,
|
||||
} from '@/constants/navigation.constant'
|
||||
import { MenuDto } from '@/proxy/menus/models'
|
||||
import { MenuNavigationDto } from '@/proxy/menus/models'
|
||||
|
||||
export function navigationTreeToFlat(items: NavigationTree[]): NavigationTree[] {
|
||||
const result: NavigationTree[] = []
|
||||
|
|
@ -24,7 +24,7 @@ export function navigationTreeToFlat(items: NavigationTree[]): NavigationTree[]
|
|||
return result
|
||||
}
|
||||
|
||||
export default function getChildren(menu: MenuDto[], parentCode: string | null): NavigationTree[] {
|
||||
export default function getChildren(menu: MenuNavigationDto[], parentCode: string | null): NavigationTree[] {
|
||||
const menus: NavigationTree[] = []
|
||||
for (const child of menu.filter((a) => a.parentCode === parentCode)) {
|
||||
const item: NavigationTree = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue