diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 5455187086..25d49fb169 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -6,7 +6,7 @@ import { ABP } from '../models'; import { ReplaceableComponents } from '../models/replaceable-components'; import { RoutesService } from '../services/routes.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; -import { getRoutePath } from '../utils/route-utils'; +import { findRoute, getRoutePath } from '../utils/route-utils'; import { takeUntilDestroy } from '../utils/rxjs-utils'; import { TreeNode } from '../utils/tree-utils'; @@ -23,13 +23,10 @@ import { TreeNode } from '../utils/tree-utils'; export class DynamicLayoutComponent implements OnDestroy { layout: Type; - constructor( - injector: Injector, - private route: ActivatedRoute, - private routes: RoutesService, - private store: Store, - ) { + constructor(injector: Injector, private store: Store) { + const route = injector.get(ActivatedRoute); const router = injector.get(Router); + const routes = injector.get(RoutesService); const layouts = { application: this.getComponent('Theme.ApplicationLayoutComponent'), account: this.getComponent('Theme.AccountLayoutComponent'), @@ -38,11 +35,12 @@ export class DynamicLayoutComponent implements OnDestroy { router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { - let expectedLayout = (this.route.snapshot.data || {}).layout; - const path = getRoutePath(router); + let expectedLayout = (route.snapshot.data || {}).layout; if (!expectedLayout) { - let node = { parent: this.routes.search({ path }) } as TreeNode; + let node = findRoute(routes, getRoutePath(router)); + node = { parent: node } as TreeNode; + while (node.parent) { node = node.parent; diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 72aeacb29d..5023876a4d 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -21,6 +21,7 @@ import { PermissionDirective } from './directives/permission.directive'; import { ReplaceableTemplateDirective } from './directives/replaceable-template.directive'; import { StopPropagationDirective } from './directives/stop-propagation.directive'; import { VisibilityDirective } from './directives/visibility.directive'; +import { RoutesHandler } from './handlers/routes.handler'; import { ApiInterceptor } from './interceptors/api.interceptor'; import { LocalizationModule } from './localization.module'; import { ABP } from './models/common'; @@ -196,6 +197,12 @@ export class CoreModule { deps: [LocalizationService], useFactory: noop, }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [RoutesHandler], + useFactory: noop, + }, { provide: OAuthStorage, useFactory: storageFactory }, ], }; diff --git a/npm/ng-packs/packages/core/src/lib/handlers/index.ts b/npm/ng-packs/packages/core/src/lib/handlers/index.ts index e69de29bb2..b666218d93 100644 --- a/npm/ng-packs/packages/core/src/lib/handlers/index.ts +++ b/npm/ng-packs/packages/core/src/lib/handlers/index.ts @@ -0,0 +1 @@ +export * from './routes.handler'; diff --git a/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts new file mode 100644 index 0000000000..637f7b4165 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/handlers/routes.handler.ts @@ -0,0 +1,45 @@ +import { Injectable, Optional } from '@angular/core'; +import { Router } from '@angular/router'; +import { ABP } from '../models'; +import { RoutesService } from '../services/routes.service'; + +@Injectable({ + providedIn: 'root', +}) +export class RoutesHandler { + constructor(private routes: RoutesService, @Optional() private router: Router) { + this.addRoutes(); + } + + addRoutes() { + this.router?.config?.forEach(({ path = '', data }) => { + if (!data?.routes) return; + + if (Array.isArray(data.routes)) { + this.routes.add(data.routes); + return; + } + + const routes = flatRoutes([{ ...data.routes, path }], { path: '' }); + this.routes.add(routes); + }); + } +} + +function flatRoutes(routes: RouteDef[], parent: any) { + if (!routes) return []; + + return routes.reduce((acc, route) => { + const current = { + ...route, + parentName: parent.name, + path: parent.path + '/' + route.path, + }; + + acc.push(current, ...flatRoutes(current.children, current)); + + return acc; + }, []); +} + +type RouteDef = ABP.Route & { children: RouteDef[] }; diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 79e3d3f52b..930265020c 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -30,19 +30,16 @@ export namespace ABP { maxResultCount?: number; } - export interface Node { + export interface Nav { name: string; parentName?: string; + requiredPolicy?: string; order?: number; invisible?: boolean; } - export interface Nav extends Node { - path: string; - requiredPolicy?: string; - } - export interface Route extends Nav { + path: string; layout?: eLayoutType; iconClass?: string; } diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 4b9f3e7899..7257a50ff3 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,8 +1,10 @@ -import { Injectable } from '@angular/core'; -import { Store } from '@ngxs/store'; +import { Injectable, OnDestroy } from '@angular/core'; +import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { BehaviorSubject, Observable } from 'rxjs'; +import { GetAppConfiguration } from '../actions/config.actions'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; +import { takeUntilDestroy } from '../utils/rxjs-utils'; import { pushValueTo } from '../utils/array-utils'; import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; @@ -75,6 +77,13 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } + find(predicate: (item: TreeNode) => boolean, tree = this.tree): TreeNode | null { + return tree.reduce( + (acc, node) => (acc ? acc : predicate(node) ? node : this.find(predicate, node.children)), + null, + ); + } + patch(identifier: string, props: Partial): T[] | false { const flatItems = this._flat$.value; const index = flatItems.findIndex(item => item[this.id] === identifier); @@ -88,6 +97,10 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } + refresh(): T[] { + return this.add([]); + } + remove(identifiers: string[]): T[] { const set = new Set(); identifiers.forEach(id => set.add(id)); @@ -98,7 +111,7 @@ export abstract class AbstractTreeService { return this.publish(flatItems, visibleItems); } - search(params: Partial, tree = this.tree): TreeNode { + search(params: Partial, tree = this.tree): TreeNode | null { const searchKeys = Object.keys(params); return tree.reduce( @@ -107,38 +120,42 @@ export abstract class AbstractTreeService { ? acc : searchKeys.every(key => node[key] === params[key]) ? node - : node.children - ? this.search(params, node.children) - : acc, + : this.search(params, node.children), null, ); } } -@Injectable({ - providedIn: 'root', -}) -export class RoutesService extends AbstractTreeService { +export abstract class AbstractNavTreeService extends AbstractTreeService + implements OnDestroy { readonly id = 'name'; readonly parentId = 'parentName'; - readonly hide = (item: ABP.Route) => item.invisible; - readonly sort = (a: ABP.Route, b: ABP.Route) => a.order - b.order; -} + readonly hide = (item: T) => item.invisible || !this.isGranted(item); + readonly sort = (a: T, b: T) => a.order - b.order; -@Injectable({ - providedIn: 'root', -}) -export class SettingTabsService extends AbstractTreeService { - readonly id = 'name'; - readonly parentId = 'parentName'; - readonly hide = (setting: ABP.Tab) => setting.invisible || !this.isGranted(setting); - readonly sort = (a: ABP.Tab, b: ABP.Tab) => a.order - b.order; - - constructor(private store: Store) { + constructor(protected actions: Actions, protected store: Store) { super(); + + this.actions + .pipe(takeUntilDestroy(this), ofActionSuccessful(GetAppConfiguration)) + .subscribe(() => this.refresh()); + } + + protected isGranted({ requiredPolicy }: T): boolean { + return this.store.selectSnapshot(ConfigState.getGrantedPolicy(requiredPolicy)); } - private isGranted(setting: ABP.Tab): boolean { - return this.store.selectSnapshot(ConfigState.getGrantedPolicy(setting.requiredPolicy)); + hasInvisibleChild(identifier: string): boolean { + const node = this.find(item => item[this.id] === identifier); + return node?.children?.some(child => child.invisible); } + + /* istanbul ignore next */ + ngOnDestroy() {} } + +@Injectable({ providedIn: 'root' }) +export class RoutesService extends AbstractNavTreeService {} + +@Injectable({ providedIn: 'root' }) +export class SettingTabsService extends AbstractNavTreeService {} diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index d9b738b2aa..4bbf25ade2 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -93,23 +93,24 @@ export class ConfigState { static getGrantedPolicy(key: string) { const selector = createSelector([ConfigState], (state: Config.State): boolean => { if (!key) return true; - const getPolicy = k => snq(() => state.auth.grantedPolicies[k], false); + const getPolicy = (k: string) => snq(() => state.auth.grantedPolicies[k], false); const orRegexp = /\|\|/g; const andRegexp = /&&/g; + // TODO: Allow combination of ANDs & ORs if (orRegexp.test(key)) { - const keys = key.split('||').filter(k => !!k); + const keys = key.split('||').filter(Boolean); - if (keys.length !== 2) return false; + if (keys.length < 2) return false; - return getPolicy(keys[0].trim()) || getPolicy(keys[1].trim()); + return keys.some(k => getPolicy(k.trim())); } else if (andRegexp.test(key)) { - const keys = key.split('&&').filter(k => !!k); + const keys = key.split('&&').filter(Boolean); - if (keys.length !== 2) return false; + if (keys.length < 2) return false; - return getPolicy(keys[0].trim()) && getPolicy(keys[1].trim()); + return keys.every(k => getPolicy(k.trim())); } return getPolicy(key); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index 89a6a683b9..216df7b681 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -2,7 +2,8 @@ import { HttpClient } from '@angular/common/http'; import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; -import { NgxsModule, Store } from '@ngxs/store'; +import { Actions, NgxsModule, Store } from '@ngxs/store'; +import { NEVER } from 'rxjs'; import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; import { eLayoutType } from '../enums/common'; import { ABP } from '../models'; @@ -97,11 +98,24 @@ const storeData = { }; describe('DynamicLayoutComponent', () => { + const mockActions: Actions = NEVER; + const mockStore = ({ + selectSnapshot() { + return true; + }, + } as unknown) as Store; + const createComponent = createRoutingFactory({ component: RouterOutletComponent, stubsEnabled: false, declarations: [DummyComponent, DynamicLayoutComponent], mocks: [ApplicationConfigurationService, HttpClient], + providers: [ + { + provide: RoutesService, + useFactory: () => new RoutesService(mockActions, mockStore), + }, + ], imports: [RouterModule, DummyLayoutModule, NgxsModule.forRoot([ReplaceableComponentsState])], routes: [ { path: '', component: RouterOutletComponent }, diff --git a/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts index 41a761b84d..3945a6527d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/route-utils.spec.ts @@ -2,13 +2,35 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; import { RouterOutletComponent } from '../components'; -import { getRoutePath } from '../utils/route-utils'; +import { RoutesService } from '../services/routes.service'; +import { findRoute, getRoutePath } from '../utils/route-utils'; // tslint:disable-next-line @Component({ template: '' }) class DummyComponent {} describe('Route Utils', () => { + describe('#findRoute', () => { + const node = { path: '/foo' }; + + test.each` + path | expected | count + ${'/foo/bar/baz'} | ${node} | ${3} + ${'/foo/bar'} | ${node} | ${2} + ${'/foo'} | ${node} | ${1} + ${'/'} | ${null} | ${1} + `( + 'should find $expected in $count turns when path is $path', + async ({ path, expected, count }) => { + const find = jest.fn(cb => (cb(node) ? node : null)); + const routes = ({ find } as any) as RoutesService; + const route = findRoute(routes, path); + expect(route).toBe(expected); + expect(find).toHaveBeenCalledTimes(count); + }, + ); + }); + describe('#getRoutePath', () => { let spectator: SpectatorRouting; const createRouting = createRoutingFactory({ diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts new file mode 100644 index 0000000000..777df39c60 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.handler.spec.ts @@ -0,0 +1,40 @@ +import { Router } from '@angular/router'; +import { RoutesHandler } from '../handlers'; +import { RoutesService } from '../services'; + +describe('Routes Handler', () => { + describe('#add', () => { + it('should add routes from router config', () => { + const config = [ + { path: 'x' }, + { path: 'y', data: {} }, + { path: '', data: { routes: { name: 'Foo' } } }, + { path: 'bar', data: { routes: { name: 'Bar' } } }, + { data: { routes: [{ path: '/baz', name: 'Baz' }] } }, + ]; + const foo = [{ path: '/', name: 'Foo' }]; + const bar = [{ path: '/bar', name: 'Bar' }]; + const baz = [{ path: '/baz', name: 'Baz' }]; + + const routes = []; + const add = jest.fn(routes.push.bind(routes)); + const mockRoutesService = ({ add } as unknown) as RoutesService; + const mockRouter = ({ config } as unknown) as Router; + + const handler = new RoutesHandler(mockRoutesService, mockRouter); + + expect(add).toHaveBeenCalledTimes(3); + expect(routes).toEqual([foo, bar, baz]); + }); + + it('should not add routes when there is no router', () => { + const routes = []; + const add = jest.fn(routes.push.bind(routes)); + const mockRoutesService = ({ add } as unknown) as RoutesService; + + const handler = new RoutesHandler(mockRoutesService, null); + + expect(add).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts index 2392a86183..93214442ea 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/routes.service.spec.ts @@ -1,17 +1,31 @@ +import { Store } from '@ngxs/store'; +import { Subject } from 'rxjs'; import { take } from 'rxjs/operators'; +import { GetAppConfiguration } from '../actions'; import { RoutesService } from '../services'; -const routes = [ - { path: '/foo', name: 'foo' }, - { path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, - { path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, - { path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, -]; - describe('Routes Service', () => { + let service: RoutesService; + const routes = [ + { path: '/foo', name: 'foo' }, + { path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, + { path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, + { path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, + ]; + + const mockActions = new Subject(); + const mockStore = ({ + selectSnapshot() { + return true; + }, + } as unknown) as Store; + + beforeEach(() => { + service = new RoutesService(mockActions, mockStore); + }); + describe('#add', () => { it('should add given routes as flat$, tree$, and visible$', async () => { - const service = new RoutesService(); service.add(routes); const flat = await service.flat$.pipe(take(1)).toPromise(); @@ -38,9 +52,34 @@ describe('Routes Service', () => { }); }); + describe('#find', () => { + it('should return node found based on query', () => { + service.add(routes); + const result = service.find(route => route.invisible); + expect(result.name).toBe('bar'); + expect(result.children.length).toBe(1); + expect(result.children[0].name).toBe('baz'); + }); + + it('should return null when query is not found', () => { + service.add(routes); + const result = service.find(route => route.requiredPolicy === 'X'); + expect(result).toBe(null); + }); + }); + + describe('#hasInvisibleChild', () => { + it('should return if node has invisible child', () => { + service.add(routes); + + expect(service.hasInvisibleChild('foo')).toBe(true); + expect(service.hasInvisibleChild('bar')).toBe(false); + expect(service.hasInvisibleChild('baz')).toBe(false); + }); + }); + describe('#remove', () => { it('should remove routes based on given routeNames', () => { - const service = new RoutesService(); service.add(routes); service.remove(['bar']); @@ -66,9 +105,9 @@ describe('Routes Service', () => { describe('#patch', () => { it('should patch propeties of routes based on given routeNames', () => { - const service = new RoutesService(); + service['isGranted'] = jest.fn(route => route.requiredPolicy !== 'X'); service.add(routes); - service.patch('x', { invisible: true }); + service.patch('x', { requiredPolicy: 'X' }); const flat = service.flat; const tree = service.tree; @@ -93,21 +132,40 @@ describe('Routes Service', () => { }); it('should return false when route name is not found', () => { - const service = new RoutesService(); service.add(routes); const result = service.patch('A man has no name.', { invisible: true }); expect(result).toBe(false); }); }); + describe('#refresh', () => { + it('should call add once with empty array', () => { + const add = jest.spyOn(service, 'add'); + service.refresh(); + expect(add).toHaveBeenCalledTimes(1); + expect(add).toHaveBeenCalledWith([]); + }); + + it('should be called upon successful GetAppConfiguration action', () => { + const refresh = jest.spyOn(service, 'refresh'); + mockActions.next({ action: new GetAppConfiguration(), status: 'SUCCESSFUL' }); + expect(refresh).toHaveBeenCalledTimes(1); + }); + }); + describe('#search', () => { - it('should return node found when route name is not found', () => { - const service = new RoutesService(); + it('should return node found based on query', () => { service.add(routes); const result = service.search({ invisible: true }); expect(result.name).toBe('bar'); expect(result.children.length).toBe(1); expect(result.children[0].name).toBe('baz'); }); + + it('should return null when query is not found', () => { + service.add(routes); + const result = service.search({ requiredPolicy: 'X' }); + expect(result).toBe(null); + }); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts index 7ef062986a..57ffdd5285 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/route-utils.ts @@ -1,4 +1,21 @@ import { PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router'; +import { ABP } from '../models/common'; +import { RoutesService } from '../services/routes.service'; +import { TreeNode } from './tree-utils'; + +export function findRoute(routes: RoutesService, path: string): TreeNode { + const node = routes.find(route => route.path === path); + + return node || path === '/' + ? node + : findRoute( + routes, + path + .split('/') + .slice(0, -1) + .join('/'), + ); +} export function getRoutePath(router: Router) { const emptyGroup = { segments: [] } as UrlSegmentGroup; diff --git a/npm/ng-packs/packages/identity/config/src/enums/index.ts b/npm/ng-packs/packages/identity/config/src/enums/index.ts index 3bda94b078..4a7a6a0e23 100644 --- a/npm/ng-packs/packages/identity/config/src/enums/index.ts +++ b/npm/ng-packs/packages/identity/config/src/enums/index.ts @@ -1 +1,2 @@ +export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts b/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts new file mode 100644 index 0000000000..f001c03964 --- /dev/null +++ b/npm/ng-packs/packages/identity/config/src/enums/policy-names.ts @@ -0,0 +1,5 @@ +export const enum eIdentityPolicyNames { + IdentityManagement = 'AbpIdentity.Roles || AbpIdentity.Users', + Roles = 'AbpIdentity.Roles', + Users = 'AbpIdentity.Users', +} diff --git a/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts index 3f88c68464..1ab2cbb3ee 100644 --- a/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/identity/config/src/providers/route.provider.ts @@ -1,6 +1,7 @@ import { eLayoutType, RoutesService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; +import { eIdentityPolicyNames } from '../enums/policy-names'; import { eIdentityRouteNames } from '../enums/route-names'; export const IDENTITY_ROUTE_PROVIDERS = [ @@ -14,6 +15,7 @@ export function configureRoutes(routes: RoutesService) { path: '/identity', name: eIdentityRouteNames.IdentityManagement, parentName: eThemeSharedRouteNames.Administration, + requiredPolicy: eIdentityPolicyNames.IdentityManagement, iconClass: 'fa fa-id-card-o', layout: eLayoutType.application, order: 1, @@ -22,14 +24,14 @@ export function configureRoutes(routes: RoutesService) { path: '/identity/roles', name: eIdentityRouteNames.Roles, parentName: eIdentityRouteNames.IdentityManagement, - requiredPolicy: 'AbpIdentity.Roles', + requiredPolicy: eIdentityPolicyNames.Roles, order: 1, }, { path: '/identity/users', name: eIdentityRouteNames.Users, parentName: eIdentityRouteNames.IdentityManagement, - requiredPolicy: 'AbpIdentity.Users', + requiredPolicy: eIdentityPolicyNames.Users, order: 2, }, ]); diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/index.ts b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts index 3bda94b078..4a7a6a0e23 100644 --- a/npm/ng-packs/packages/setting-management/config/src/enums/index.ts +++ b/npm/ng-packs/packages/setting-management/config/src/enums/index.ts @@ -1 +1,2 @@ +export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts b/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts new file mode 100644 index 0000000000..df66e95e3b --- /dev/null +++ b/npm/ng-packs/packages/setting-management/config/src/enums/policy-names.ts @@ -0,0 +1,3 @@ +export const enum eSettingManagementPolicyNames { + Settings = 'AbpAccount.SettingManagement', +} diff --git a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts index 3ed75d33b0..badc46ae9a 100644 --- a/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/setting-management/config/src/providers/route.provider.ts @@ -2,6 +2,7 @@ import { eLayoutType, RoutesService, SettingTabsService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; import { debounceTime, map } from 'rxjs/operators'; +import { eSettingManagementPolicyNames } from '../enums/policy-names'; import { eSettingManagementRouteNames } from '../enums/route-names'; export const SETTING_MANAGEMENT_ROUTE_PROVIDERS = [ @@ -21,7 +22,7 @@ export function configureRoutes(routes: RoutesService) { name: eSettingManagementRouteNames.Settings, path: '/setting-management', parentName: eThemeSharedRouteNames.Administration, - requiredPolicy: 'AbpAccount.SettingManagement', + requiredPolicy: eSettingManagementPolicyNames.Settings, layout: eLayoutType.application, order: 6, iconClass: 'fa fa-cog', @@ -37,6 +38,6 @@ export function hideRoutes(routes: RoutesService, tabs: SettingTabsService) { debounceTime(0), map(nodes => !nodes.length), ) - .subscribe(invisible => routes.patch('AbpSettingManagement::Settings', { invisible })); + .subscribe(invisible => routes.patch(eSettingManagementRouteNames.Settings, { invisible })); }; } diff --git a/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts index 3bda94b078..4a7a6a0e23 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/index.ts @@ -1 +1,2 @@ +export * from './policy-names'; export * from './route-names'; diff --git a/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts b/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts new file mode 100644 index 0000000000..4b78f4d397 --- /dev/null +++ b/npm/ng-packs/packages/tenant-management/config/src/enums/policy-names.ts @@ -0,0 +1,4 @@ +export const enum eTenantManagementPolicyNames { + TenantManagement = 'AbpTenantManagement.Tenants', + Tenants = 'AbpTenantManagement.Tenants', +} diff --git a/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts index 0be264745d..b191b46ce3 100644 --- a/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts +++ b/npm/ng-packs/packages/tenant-management/config/src/providers/route.provider.ts @@ -1,6 +1,7 @@ import { eLayoutType, RoutesService } from '@abp/ng.core'; import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; import { APP_INITIALIZER } from '@angular/core'; +import { eTenantManagementPolicyNames } from '../enums/policy-names'; import { eTenantManagementRouteNames } from '../enums/route-names'; export const TENANT_MANAGEMENT_ROUTE_PROVIDERS = [ @@ -14,6 +15,7 @@ export function configureRoutes(routes: RoutesService) { path: '/tenant-management', name: eTenantManagementRouteNames.TenantManagement, parentName: eThemeSharedRouteNames.Administration, + requiredPolicy: eTenantManagementPolicyNames.TenantManagement, layout: eLayoutType.application, iconClass: 'fa fa-users', order: 2, @@ -22,7 +24,7 @@ export function configureRoutes(routes: RoutesService) { path: '/tenant-management/tenants', name: eTenantManagementRouteNames.Tenants, parentName: eTenantManagementRouteNames.TenantManagement, - requiredPolicy: 'AbpTenantManagement.Tenants', + requiredPolicy: eTenantManagementPolicyNames.Tenants, order: 1, }, ]); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html index 4d22ddc8d2..d987f22860 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/routes/routes.component.html @@ -1,7 +1,7 @@