From 980581b94061a6cb10abc400683eb7e0ef4faa66 Mon Sep 17 00:00:00 2001 From: luisa-beerboom <101706784+luisa-beerboom@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:08:13 +0200 Subject: [PATCH] Fix committee admin account list (#3897) --- .../pages/meetings/view-models/view-user.ts | 2 + .../account-main/account-main.component.ts | 2 +- .../account-list/account-list.component.ts | 28 ++++---- .../common/account-controller.service.ts | 32 ++++++++- .../committee-meeting-preview.component.html | 9 +-- .../perms/cml-perms.directive.spec.ts | 69 ++++++++++++++++--- .../directives/perms/cml-perms.directive.ts | 24 ++++++- .../perms/oml-perms.directive.spec.ts | 24 ++++++- 8 files changed, 158 insertions(+), 32 deletions(-) diff --git a/client/src/app/site/pages/meetings/view-models/view-user.ts b/client/src/app/site/pages/meetings/view-models/view-user.ts index 4639dd5062..820367ced9 100644 --- a/client/src/app/site/pages/meetings/view-models/view-user.ts +++ b/client/src/app/site/pages/meetings/view-models/view-user.ts @@ -1,3 +1,4 @@ +import { Observable } from 'rxjs'; import { User } from 'src/app/domain/models/users/user'; import { BaseViewModel } from 'src/app/site/base/base-view-model'; @@ -335,6 +336,7 @@ interface IUserRelations { meeting_users: ViewMeetingUser[]; poll_voted: ViewPoll[]; committee_managements: ViewCommittee[]; + committee_managements_as_observable: Observable; options: ViewOption[]; votes: ViewVote[]; poll_candidates: ViewPollCandidate[]; diff --git a/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts b/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts index 6f442e449a..1bce1f15a6 100644 --- a/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/components/account-main/account-main.component.ts @@ -21,7 +21,7 @@ const accountListSubsciptionContent = { follow: [ { idField: `meeting_id`, - fieldset: [`is_active_in_organization_id`, `is_archived_in_organization_id`], + fieldset: [`is_active_in_organization_id`, `is_archived_in_organization_id`, `default_group_id`], follow: [{ idField: `committee_id`, fieldset: [`manager_ids`] }] } ] diff --git a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.ts b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.ts index 9c7e645ed9..30fdac024b 100644 --- a/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.ts +++ b/client/src/app/site/pages/organization/pages/accounts/pages/account-list/components/account-list/account-list.component.ts @@ -42,6 +42,20 @@ export class AccountListComponent extends BaseListViewComponent { return this.operator.hasOrganizationPermissions(OML.can_manage_users); } + public get fakeFilters(): Observable<{ [key: string]: () => void }> { + if (this.meeting) { + return this.meeting.pipe( + map(meeting => { + if (meeting) { + return { [meeting.name]: () => this.navigateToBaseList() }; + } + return {}; + }) + ); + } + return null; + } + public constructor( protected override translate: TranslateService, public readonly controller: AccountControllerService, @@ -83,20 +97,6 @@ export class AccountListComponent extends BaseListViewComponent { this.router.navigate([`/accounts`]); } - public get fakeFilters(): Observable<{ [key: string]: () => void }> { - if (this.meeting) { - return this.meeting.pipe( - map(meeting => { - if (meeting) { - return { [meeting.name]: () => this.navigateToBaseList() }; - } - return {}; - }) - ); - } - return null; - } - public async deleteUsers(accounts: ViewUser[] = this.selectedRows): Promise { await this.controller.doDeleteOrRemove(accounts); } diff --git a/client/src/app/site/pages/organization/pages/accounts/services/common/account-controller.service.ts b/client/src/app/site/pages/organization/pages/accounts/services/common/account-controller.service.ts index a388837c3c..26cc263eb5 100644 --- a/client/src/app/site/pages/organization/pages/accounts/services/common/account-controller.service.ts +++ b/client/src/app/site/pages/organization/pages/accounts/services/common/account-controller.service.ts @@ -1,6 +1,8 @@ import { Injectable } from '@angular/core'; import { marker as _ } from '@colsen1991/ngx-translate-extract-marker'; -import { firstValueFrom } from 'rxjs'; +import { firstValueFrom, map, Observable } from 'rxjs'; +import { Id } from 'src/app/domain/definitions/key-types'; +import { OML } from 'src/app/domain/definitions/organization-permission'; import { Identifiable } from 'src/app/domain/interfaces'; import { User } from 'src/app/domain/models/users/user'; import { Action } from 'src/app/gateways/actions'; @@ -20,6 +22,8 @@ import { AccountCommonServiceModule } from './account-common-service.module'; providedIn: AccountCommonServiceModule }) export class AccountControllerService extends BaseController { + private _committee_users_set: Set = new Set(); + public constructor( controllerServiceCollector: ControllerServiceCollectorService, protected override repo: UserRepositoryService, @@ -28,6 +32,32 @@ export class AccountControllerService extends BaseController { private operator: OperatorService ) { super(controllerServiceCollector, User, repo); + this.operator.user.committee_managements_as_observable.subscribe(committees => { + this._committee_users_set = new Set(committees.flatMap(committee => committee.user_ids ?? [])); + }); + } + + public override getViewModelListObservable(): Observable { + return super + .getViewModelListObservable() + .pipe(map(accounts => this.filterAccountsForCommitteeAdmins(accounts))); + } + + public override getSortedViewModelListObservable(): Observable { + return super + .getSortedViewModelListObservable() + .pipe(map(accounts => this.filterAccountsForCommitteeAdmins(accounts))); + } + + public override getViewModelList(): ViewUser[] { + return this.filterAccountsForCommitteeAdmins(super.getViewModelList()); + } + + private filterAccountsForCommitteeAdmins(accounts: ViewUser[]): ViewUser[] { + if (this.operator.hasOrganizationPermissions(OML.can_manage_users)) { + return accounts; + } + return accounts.filter(account => this._committee_users_set.has(account.id)); } public bulkAddUserToMeeting(users: ViewUser[], ...meetings: ViewMeeting[]): Action { diff --git a/client/src/app/site/pages/organization/pages/committees/modules/committee-meeting-preview/committee-meeting-preview.component.html b/client/src/app/site/pages/organization/pages/committees/modules/committee-meeting-preview/committee-meeting-preview.component.html index f6e557954d..fc586663ab 100644 --- a/client/src/app/site/pages/organization/pages/committees/modules/committee-meeting-preview/committee-meeting-preview.component.html +++ b/client/src/app/site/pages/organization/pages/committees/modules/committee-meeting-preview/committee-meeting-preview.component.html @@ -78,11 +78,12 @@ diff --git a/client/src/app/ui/directives/perms/cml-perms.directive.spec.ts b/client/src/app/ui/directives/perms/cml-perms.directive.spec.ts index 51c3bdb8f7..991ed6dc2e 100644 --- a/client/src/app/ui/directives/perms/cml-perms.directive.spec.ts +++ b/client/src/app/ui/directives/perms/cml-perms.directive.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Observable, Subject } from 'rxjs'; import { Id } from 'src/app/domain/definitions/key-types'; -import { CML } from 'src/app/domain/definitions/organization-permission'; +import { CML, OML } from 'src/app/domain/definitions/organization-permission'; import { OperatorService } from 'src/app/site/services/operator.service'; import { CmlPermsDirective } from './cml-perms.directive'; @@ -15,6 +15,7 @@ type TestConditionalType = { complement: boolean; id: number; nonAdmin: boolean; + orOML: OML | undefined; }; @Component({ @@ -29,12 +30,22 @@ type TestConditionalType = { *osCmlPerms="permission; committeeId: conditionals.id; complement: conditionals.complement" id="complement" > +
+ + +
+
+ +
+
` }) class TestComponent extends BasePermsTestComponent { public permission = CML.can_manage; public constructor() { - super({ and: true, or: true, complement: true, id: 1, nonAdmin: false }); + super({ and: true, or: true, complement: true, id: 1, nonAdmin: false, orOML: undefined }); } } @@ -45,8 +56,13 @@ class MockOperatorService { private _operatorUpdatedSubject = new Subject(); private _permList: CML[] = []; + private _oml: OML | undefined = undefined; private _isAdmin = false; + public hasOrganizationPermissions(...checkPerms: OML[]): boolean { + return checkPerms.some(perm => perm === this._oml); + } + public hasCommitteePermissions(committeeId: Id | null, ...checkPerms: CML[]): boolean { return this._isAdmin || this.hasCommitteePermissionsNonAdminCheck(committeeId, ...checkPerms); } @@ -55,11 +71,12 @@ class MockOperatorService { return checkPerms.some(perm => this._permList.includes(perm)); } - public changeOperatorPermsForTest(newPermList: CML[], isAdmin?: boolean): void { + public changeOperatorPermsForTest(newPermList: CML[], oml?: OML | undefined): void { this._permList = newPermList; - if (isAdmin !== undefined) { - this._isAdmin = isAdmin; + if (oml) { + this._isAdmin = oml === OML.superadmin; } + this._oml = oml; this._operatorUpdatedSubject.next(); } } @@ -96,10 +113,10 @@ describe(`CmlPermsDirective`, () => { operatorService.changeOperatorPermsForTest([]); update(); expect(getElement(`#normal`)).toBeFalsy(); - operatorService.changeOperatorPermsForTest([CML.can_manage], true); + operatorService.changeOperatorPermsForTest([CML.can_manage], OML.superadmin); update(); expect(getElement(`#normal`)).toBeTruthy(); - operatorService.changeOperatorPermsForTest([]); + operatorService.changeOperatorPermsForTest([], OML.superadmin); update(); expect(getElement(`#normal`)).toBeTruthy(); }); @@ -112,10 +129,10 @@ describe(`CmlPermsDirective`, () => { operatorService.changeOperatorPermsForTest([]); update(); expect(getElement(`#normal`)).toBeFalsy(); - operatorService.changeOperatorPermsForTest([CML.can_manage], true); + operatorService.changeOperatorPermsForTest([CML.can_manage], OML.superadmin); update(); expect(getElement(`#normal`)).toBeTruthy(); - operatorService.changeOperatorPermsForTest([]); + operatorService.changeOperatorPermsForTest([], OML.superadmin); update(); expect(getElement(`#normal`)).toBeFalsy(); }); @@ -161,4 +178,38 @@ describe(`CmlPermsDirective`, () => { update(); expect(getElement(`#normal`)).toBeFalsy(); }); + + it(`check if orOML works`, async () => { + operatorService.changeOperatorPermsForTest([], OML.can_manage_organization); + update(); + expect(getElement(`#oml`)).toBeFalsy(); + operatorService.changeOperatorPermsForTest([], undefined); + fixture.componentInstance.setTestComponentData({ orOML: OML.can_manage_users }); + update(); + expect(getElement(`#oml`)).toBeFalsy(); + operatorService.changeOperatorPermsForTest([CML.can_manage]); + update(); + expect(getElement(`#oml`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([CML.can_manage], OML.can_manage_users); + update(); + expect(getElement(`#oml`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([], OML.can_manage_users); + update(); + expect(getElement(`#oml`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([], OML.can_manage_organization); + update(); + expect(getElement(`#oml`)).toBeFalsy(); + }); + + it(`check if then and else work`, async () => { + update(); + console.log(`BEFORE: THEN:`, getElement(`#then`), `ELSE:`, getElement(`#else`)); + // expect(getElement(`#else`)).toBeTruthy(); + // expect(getElement(`#then`)).toBeFalsy(); + operatorService.changeOperatorPermsForTest([CML.can_manage]); + update(); + console.log(`AFTER: THEN:`, getElement(`#then`), `ELSE:`, getElement(`#else`)); + expect(getElement(`#else`)).toBeFalsy(); + expect(getElement(`#then`)).toBeTruthy(); + }); }); diff --git a/client/src/app/ui/directives/perms/cml-perms.directive.ts b/client/src/app/ui/directives/perms/cml-perms.directive.ts index 9f96bc9599..a7e5a6fe9b 100644 --- a/client/src/app/ui/directives/perms/cml-perms.directive.ts +++ b/client/src/app/ui/directives/perms/cml-perms.directive.ts @@ -1,6 +1,6 @@ -import { Directive, Input } from '@angular/core'; +import { Directive, Input, TemplateRef } from '@angular/core'; import { Id } from 'src/app/domain/definitions/key-types'; -import { CML } from 'src/app/domain/definitions/organization-permission'; +import { CML, OML } from 'src/app/domain/definitions/organization-permission'; import { BasePermsDirective } from './base-perms.directive'; @@ -40,10 +40,30 @@ export class CmlPermsDirective extends BasePermsDirective { this.updatePermission(); } + @Input() + public set osCmlPermsThen(template: TemplateRef) { + this.setThenTemplate(template); + } + + @Input() + public set osCmlPermsElse(template: TemplateRef) { + this.setElseTemplate(template); + } + + @Input() + public set osCmlPermsOrOML(value: OML | undefined) { + this._orOML = value; + this.updatePermission(); + } + private _committeeId: Id | undefined = undefined; private _checkNonAdmin = false; + private _orOML: OML | undefined = undefined; protected hasPermissions(): boolean { + if (this._orOML && this.operator.hasOrganizationPermissions(this._orOML)) { + return true; + } if (!this._committeeId) { return false; } diff --git a/client/src/app/ui/directives/perms/oml-perms.directive.spec.ts b/client/src/app/ui/directives/perms/oml-perms.directive.spec.ts index 47813e51a7..81c768abf2 100644 --- a/client/src/app/ui/directives/perms/oml-perms.directive.spec.ts +++ b/client/src/app/ui/directives/perms/oml-perms.directive.spec.ts @@ -20,6 +20,7 @@ type TestConditionalType = {
+
@@ -43,15 +44,21 @@ class MockOperatorService { private _operatorUpdatedSubject = new Subject(); private _permList: OML[] = []; + private _isCommitteeAdmin = true; public hasOrganizationPermissions(...checkPerms: OML[]): boolean { return checkPerms.some(perm => this._permList.includes(perm)); } - public changeOperatorPermsForTest(newPermList: OML[]): void { + public changeOperatorPermsForTest(newPermList: OML[], isCommitteeAdmin: boolean = this._isCommitteeAdmin): void { this._permList = newPermList; + this._isCommitteeAdmin = isCommitteeAdmin; this._operatorUpdatedSubject.next(); } + + public isAnyCommitteeAdmin(): boolean { + return this._isCommitteeAdmin; + } } describe(`OmlPermsDirective`, () => { @@ -129,4 +136,19 @@ describe(`OmlPermsDirective`, () => { expect(getElement(`#else`)).toBeFalsy(); expect(getElement(`#then`)).toBeTruthy(); }); + + it(`check if allowCommitteeAdmin works`, async () => { + operatorService.changeOperatorPermsForTest([OML.can_manage_organization]); + update(); + expect(getElement(`#committee`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([OML.can_manage_organization], true); + update(); + expect(getElement(`#committee`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([]); + update(); + expect(getElement(`#committee`)).toBeTruthy(); + operatorService.changeOperatorPermsForTest([], false); + update(); + expect(getElement(`#committee`)).toBeFalsy(); + }); });