diff --git a/.eslintrc.json b/.eslintrc.json index 6790da491..cf034af7e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,6 @@ "@components/**", "@guards/**", "@i18n/**", - "@state/**", "@utils/**", "@models/**", "@environments/**", diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index b13a5055e..45c892ee1 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -6,8 +6,8 @@ import { BaseScreenComponent } from '@components/base-screen/base-screen.compone import { RouteReuseStrategy, RouterModule, Routes } from '@angular/router'; import { NgModule } from '@angular/core'; import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component'; -import { AppStateGuard } from '@state/app-state.guard'; import { DossiersGuard } from '@guards/dossiers.guard'; +import { DossierTemplatesGuard } from '@guards/dossier-templates.guard'; const routes: Routes = [ { @@ -36,7 +36,7 @@ const routes: Routes = [ loadChildren: () => import('./modules/dossier/dossiers.module').then(m => m.DossiersModule), canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard, DossiersGuard], + routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, DossiersGuard], requiredRoles: ['RED_USER', 'RED_MANAGER'], }, }, @@ -51,7 +51,7 @@ const routes: Routes = [ ], canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, }, { diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index b6d58256f..ace6dc80f 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -1,13 +1,22 @@ -import { Component, ViewContainerRef } from '@angular/core'; +import { Component, OnInit, ViewContainerRef } from '@angular/core'; import { RouterHistoryService } from '@services/router-history.service'; +import { UserService } from '@services/user.service'; @Component({ selector: 'redaction-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) -export class AppComponent { +export class AppComponent implements OnInit { // ViewContainerRef needs to be injected for the color picker to work // RouterHistoryService needs to be injected for last dossiers screen to be updated on first app load - constructor(public viewContainerRef: ViewContainerRef, private readonly _routerHistoryService: RouterHistoryService) {} + constructor( + public viewContainerRef: ViewContainerRef, + private readonly _routerHistoryService: RouterHistoryService, + private readonly _userService: UserService, + ) {} + + async ngOnInit(): Promise { + await this._userService.initialize(); + } } diff --git a/apps/red-ui/src/app/components/base-screen/base-screen.component.ts b/apps/red-ui/src/app/components/base-screen/base-screen.component.ts index 3392423a7..b7087c041 100644 --- a/apps/red-ui/src/app/components/base-screen/base-screen.component.ts +++ b/apps/red-ui/src/app/components/base-screen/base-screen.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; import { UserService } from '@services/user.service'; -import { AppStateService } from '@state/app-state.service'; import { UserPreferenceService } from '@services/user-preference.service'; import { NavigationStart, Router } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { FileDownloadService } from '@upload-download/services/file-download.service'; import { TranslateService } from '@ngx-translate/core'; import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @@ -34,13 +32,11 @@ export class BaseScreenComponent { name: _('top-bar.navigation-items.my-account.children.account'), routerLink: '/main/account', show: true, - action: this.appStateService.reset, }, { name: _('top-bar.navigation-items.my-account.children.admin'), routerLink: '/main/admin', show: this.currentUser.isManager || this.currentUser.isUserAdmin, - action: this.appStateService.reset, }, { name: _('top-bar.navigation-items.my-account.children.downloads'), @@ -75,7 +71,6 @@ export class BaseScreenComponent { readonly isSearchScreen$ = this._navigationStart$.pipe(map(isSearchScreen)); constructor( - readonly appStateService: AppStateService, readonly userService: UserService, readonly userPreferenceService: UserPreferenceService, readonly titleService: Title, diff --git a/apps/red-ui/src/app/guards/dictionary-exists.guard.ts b/apps/red-ui/src/app/guards/dictionary-exists.guard.ts new file mode 100644 index 000000000..f7d07f8d9 --- /dev/null +++ b/apps/red-ui/src/app/guards/dictionary-exists.guard.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; +import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants'; +import { DictionariesMapService } from '../services/entity-services/dictionaries-map.service'; + +@Injectable({ providedIn: 'root' }) +export class DictionaryExistsGuard implements CanActivate { + constructor(private readonly _dictionariesMapService: DictionariesMapService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot): Promise { + const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID); + const type: string = route.paramMap.get(DICTIONARY_TYPE); + + if (!this._dictionariesMapService.get(dossierTemplateId, type)) { + await this._router.navigate(['main', 'admin', 'dossier-templates', dossierTemplateId, 'dictionaries']); + return false; + } + + return true; + } +} diff --git a/apps/red-ui/src/app/guards/dossier-files-guard.ts b/apps/red-ui/src/app/guards/dossier-files-guard.ts index bf08f4737..2b5a353b4 100644 --- a/apps/red-ui/src/app/guards/dossier-files-guard.ts +++ b/apps/red-ui/src/app/guards/dossier-files-guard.ts @@ -4,6 +4,7 @@ import { DossiersService } from '@services/entity-services/dossiers.service'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesService } from '@services/entity-services/files.service'; import { firstValueFrom } from 'rxjs'; +import { DOSSIER_ID } from '@utils/constants'; @Injectable({ providedIn: 'root' }) export class DossierFilesGuard implements CanActivate { @@ -15,7 +16,7 @@ export class DossierFilesGuard implements CanActivate { ) {} async canActivate(route: ActivatedRouteSnapshot): Promise { - const dossierId = route.paramMap.get('dossierId'); + const dossierId = route.paramMap.get(DOSSIER_ID); if (!this._dossiersService.has(dossierId)) { await this._router.navigate(['/main', 'dossiers']); diff --git a/apps/red-ui/src/app/guards/dossier-template-exists.guard.ts b/apps/red-ui/src/app/guards/dossier-template-exists.guard.ts new file mode 100644 index 000000000..850ba72ca --- /dev/null +++ b/apps/red-ui/src/app/guards/dossier-template-exists.guard.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; +import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; +import { DOSSIER_TEMPLATE_ID } from '@utils/constants'; + +@Injectable({ providedIn: 'root' }) +export class DossierTemplateExistsGuard implements CanActivate { + constructor(private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot): Promise { + const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID); + + if (!this._dossierTemplatesService.find(dossierTemplateId)) { + await this._router.navigate(['main', 'admin', 'dossier-templates']); + return false; + } + + return true; + } +} diff --git a/apps/red-ui/src/app/guards/dossier-templates.guard.ts b/apps/red-ui/src/app/guards/dossier-templates.guard.ts new file mode 100644 index 000000000..4d0995f44 --- /dev/null +++ b/apps/red-ui/src/app/guards/dossier-templates.guard.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { CanActivate } from '@angular/router'; +import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; +import { firstValueFrom } from 'rxjs'; +import { DictionaryService } from '@shared/services/dictionary.service'; + +@Injectable({ providedIn: 'root' }) +export class DossierTemplatesGuard implements CanActivate { + constructor( + private readonly _dossierTemplatesService: DossierTemplatesService, + private readonly _dictionaryService: DictionaryService, + ) {} + + async canActivate(): Promise { + await firstValueFrom(this._dossierTemplatesService.loadAll()); + return true; + } +} diff --git a/apps/red-ui/src/app/guards/dossiers.guard.ts b/apps/red-ui/src/app/guards/dossiers.guard.ts index 66a37f509..7f056bf71 100644 --- a/apps/red-ui/src/app/guards/dossiers.guard.ts +++ b/apps/red-ui/src/app/guards/dossiers.guard.ts @@ -1,16 +1,11 @@ import { Injectable } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { CanActivate } from '@angular/router'; import { DossiersService } from '@services/entity-services/dossiers.service'; -import { TranslateService } from '@ngx-translate/core'; import { firstValueFrom } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DossiersGuard implements CanActivate { - constructor( - private readonly _dossiersService: DossiersService, - private readonly _translateService: TranslateService, - private readonly _router: Router, - ) {} + constructor(private readonly _dossiersService: DossiersService) {} async canActivate(): Promise { await firstValueFrom(this._dossiersService.loadAll()); diff --git a/apps/red-ui/src/app/guards/file-preview.guard.ts b/apps/red-ui/src/app/guards/file-preview.guard.ts index 294ed782d..6db6c56d3 100644 --- a/apps/red-ui/src/app/guards/file-preview.guard.ts +++ b/apps/red-ui/src/app/guards/file-preview.guard.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; +import { DOSSIER_ID, FILE_ID } from '@utils/constants'; @Injectable({ providedIn: 'root' }) export class FilePreviewGuard implements CanActivate { @@ -12,8 +13,8 @@ export class FilePreviewGuard implements CanActivate { ) {} async canActivate(route: ActivatedRouteSnapshot): Promise { - const dossierId = route.paramMap.get('dossierId'); - const fileId = route.paramMap.get('fileId'); + const dossierId = route.paramMap.get(DOSSIER_ID); + const fileId = route.paramMap.get(FILE_ID); const dossier = this._dossiersService.find(dossierId); diff --git a/apps/red-ui/src/app/models/file/file-data.model.ts b/apps/red-ui/src/app/models/file/file-data.model.ts index dea3703f0..3356e3ce5 100644 --- a/apps/red-ui/src/app/models/file/file-data.model.ts +++ b/apps/red-ui/src/app/models/file/file-data.model.ts @@ -24,7 +24,7 @@ export class FileDataModel { private readonly _file: File, private _redactionLog: IRedactionLog, public viewedPages?: IViewedPage[], - private _dictionaryData?: { [p: string]: Dictionary }, + private _dictionaryData?: Dictionary[], private _areDevFeaturesEnabled?: boolean, ) { this._buildAllAnnotations(); @@ -83,7 +83,8 @@ export class FileDataModel { // copy the redactionLog Entry const changeLogValues = this.#getChangeLogValues(redactionLogEntry); - if (!this._dictionaryData[redactionLogEntry.type]) { + const dictionaryData = this._dictionaryData.find(dict => dict.type === redactionLogEntry.type); + if (!dictionaryData) { this.missingTypes.add(redactionLogEntry.type); return; } @@ -94,7 +95,7 @@ export class FileDataModel { changeLogValues.isChangeLogEntry, changeLogValues.hidden, this.redactionLog.legalBasis, - !!this._dictionaryData[redactionLogEntry.type]?.hint, + !!dictionaryData?.hint, ); if ( diff --git a/apps/red-ui/src/app/modules/account/account-routing.module.ts b/apps/red-ui/src/app/modules/account/account-routing.module.ts index 56164ffa9..76a71cfff 100644 --- a/apps/red-ui/src/app/modules/account/account-routing.module.ts +++ b/apps/red-ui/src/app/modules/account/account-routing.module.ts @@ -3,7 +3,6 @@ import { RouterModule } from '@angular/router'; import { CompositeRouteGuard } from '@iqser/common-ui'; import { AuthGuard } from '../auth/auth.guard'; import { RedRoleGuard } from '../auth/red-role.guard'; -import { AppStateGuard } from '@state/app-state.guard'; import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component'; const routes = [ @@ -13,7 +12,7 @@ const routes = [ component: BaseAccountScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, loadChildren: () => import('./screens/user-profile/user-profile.module').then(m => m.UserProfileModule), }, @@ -22,7 +21,7 @@ const routes = [ component: BaseAccountScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_USER'], }, loadChildren: () => import('./screens/notifications/notifications.module').then(m => m.NotificationsModule), diff --git a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts index 33215e35e..f4351eaed 100644 --- a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { AuthGuard } from '../auth/auth.guard'; import { CompositeRouteGuard } from '@iqser/common-ui'; import { RedRoleGuard } from '../auth/red-role.guard'; -import { AppStateGuard } from '@state/app-state.guard'; import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component'; import { DictionaryOverviewScreenComponent } from './screens/dictionary-overview/dictionary-overview-screen.component'; import { PendingChangesGuard } from '@guards/can-deactivate.guard'; @@ -18,6 +17,10 @@ import { TrashScreenComponent } from './screens/trash/trash-screen.component'; import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component'; import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; +import { DossierTemplatesGuard } from '../../guards/dossier-templates.guard'; +import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants'; +import { DossierTemplateExistsGuard } from '../../guards/dossier-template-exists.guard'; +import { DictionaryExistsGuard } from '../../guards/dictionary-exists.guard'; const routes: Routes = [ { path: '', redirectTo: 'dossier-templates', pathMatch: 'full' }, @@ -29,7 +32,7 @@ const routes: Routes = [ component: BaseAdminScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, loadChildren: () => import('./screens/dossier-templates-listing/dossier-templates-listing.module').then( @@ -37,7 +40,7 @@ const routes: Routes = [ ), }, { - path: ':dossierTemplateId', + path: `:${DOSSIER_TEMPLATE_ID}`, children: [ { path: 'info', @@ -53,16 +56,16 @@ const routes: Routes = [ component: DictionaryListingScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, }, { - path: ':dictionary', + path: `:${DICTIONARY_TYPE}`, component: DictionaryOverviewScreenComponent, canActivate: [CompositeRouteGuard], canDeactivate: [PendingChangesGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard, DictionaryExistsGuard], }, }, ], @@ -73,7 +76,7 @@ const routes: Routes = [ canActivate: [CompositeRouteGuard], canDeactivate: [PendingChangesGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule), }, @@ -82,7 +85,7 @@ const routes: Routes = [ component: FileAttributesListingScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, }, { @@ -90,7 +93,7 @@ const routes: Routes = [ component: BaseDossierTemplateScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule), }, @@ -99,7 +102,7 @@ const routes: Routes = [ component: BaseDossierTemplateScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, loadChildren: () => import('./screens/reports/reports.module').then(m => m.ReportsModule), }, @@ -108,7 +111,7 @@ const routes: Routes = [ component: DossierAttributesListingScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, }, { @@ -116,7 +119,7 @@ const routes: Routes = [ component: DefaultColorsScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], }, }, { @@ -127,10 +130,13 @@ const routes: Routes = [ }, { path: '', redirectTo: 'info', pathMatch: 'full' }, ], + canActivate: [CompositeRouteGuard], + data: { routeGuards: [DossierTemplateExistsGuard] }, }, ], + canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard], requiredRoles: ['RED_MANAGER', 'RED_ADMIN'], }, }, @@ -139,7 +145,7 @@ const routes: Routes = [ component: UserListingScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_USER_ADMIN'], }, }, @@ -148,7 +154,7 @@ const routes: Routes = [ component: LicenseInformationScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_ADMIN'], }, }, @@ -157,7 +163,7 @@ const routes: Routes = [ component: DigitalSignatureScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_ADMIN'], }, }, @@ -166,7 +172,7 @@ const routes: Routes = [ component: AuditScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_ADMIN'], }, }, @@ -176,7 +182,7 @@ const routes: Routes = [ canActivate: [CompositeRouteGuard], canDeactivate: [PendingChangesGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_ADMIN'], }, }, @@ -185,7 +191,7 @@ const routes: Routes = [ component: TrashScreenComponent, canActivate: [CompositeRouteGuard], data: { - routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], + routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_MANAGER'], }, }, diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts index db715d7b9..7578ddd03 100644 --- a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts +++ b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts @@ -1,9 +1,10 @@ import { Component, HostBinding, Input, OnInit } from '@angular/core'; import { UserPreferenceService } from '@services/user-preference.service'; -import { AppStateService } from '@state/app-state.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { adminSideNavTranslations } from '../translations/admin-side-nav-translations'; import { UserService } from '@services/user.service'; +import { ActivatedRoute } from '@angular/router'; +import { DICTIONARY_TYPE } from '@utils/constants'; type Type = 'settings' | 'dossierTemplates'; @@ -68,7 +69,7 @@ export class AdminSideNavComponent implements OnInit { constructor( private readonly _userService: UserService, - private readonly _appStateService: AppStateService, + private readonly _route: ActivatedRoute, readonly userPreferenceService: UserPreferenceService, ) {} @@ -77,6 +78,6 @@ export class AdminSideNavComponent implements OnInit { } ngOnInit(): void { - this.prefix = this._appStateService.activeDictionaryType ? '../../' : '../'; + this.prefix = this._route.snapshot.paramMap.get(DICTIONARY_TYPE) ? '../../' : '../'; } } diff --git a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html b/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html index b91797330..b7c2079b4 100644 --- a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html +++ b/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html @@ -1,31 +1,22 @@