diff --git a/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.html b/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.html index a319daad3eb5aa24380d8b91d6dc51a4f2ddafef..4f34707c8eff5ae9e02433624eeff4661d75894f 100644 --- a/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.html +++ b/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.html @@ -13,6 +13,8 @@ [selection]="selection" [hideColumns]="hideColumns" [filter]="filterValue" + [groupsToDisable]="membersGroups" + [disableGroups]="true" [disableRouting]="true" [disableMembers]="true"> </perun-web-apps-groups-list> diff --git a/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.ts b/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.ts index 4c24775c139089999b8e8269159ffbd898d8e104..8b911a0913173b1a53a73cb6fc685557e36e7ca9 100644 --- a/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.ts +++ b/apps/admin-gui/src/app/shared/components/dialogs/add-member-group-dialog/add-member-group-dialog.component.ts @@ -1,13 +1,15 @@ import { Component, Inject, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Group, GroupsManagerService, Member, MembersManagerService } from '@perun-web-apps/perun/openapi'; +import { Group, GroupsManagerService, Member, MembersManagerService, RichGroup } from '@perun-web-apps/perun/openapi'; import { GuiAuthResolver, NotificatorService } from '@perun-web-apps/perun/services'; import { TranslateService } from '@ngx-translate/core'; import { SelectionModel } from '@angular/cdk/collections'; +import { Urns } from '@perun-web-apps/perun/urns'; export interface AddMemberGroupDialogData { theme: string; memberId: number; + membersGroups: Set<number>; } @Component({ @@ -31,19 +33,30 @@ export class AddMemberGroupDialogComponent implements OnInit { loading = false; member: Member; - groups: Group[] = []; + membersGroups: Set<number>; + groups: RichGroup[] = []; selection = new SelectionModel<Group>(true, []); + attrNames = [ + Urns.GROUP_SYNC_ENABLED, + Urns.GROUP_LAST_SYNC_STATE, + Urns.GROUP_LAST_SYNC_TIMESTAMP, + Urns.GROUP_STRUCTURE_SYNC_ENABLED, + Urns.GROUP_LAST_STRUCTURE_SYNC_STATE, + Urns.GROUP_LAST_STRUCTURE_SYNC_TIMESTAMP + ]; + hideColumns = [ 'vo', 'menu' ]; filterValue = ""; ngOnInit(): void { this.theme = this.data.theme; + this.membersGroups = this.data.membersGroups; this.loading = true; this.memberManager.getMemberById(this.data.memberId).subscribe(member => { this.member = member; - this.groupManager.getAllGroups(this.member.voId).subscribe(groups => { + this.groupManager.getAllRichGroupsWithAttributesByNames(this.member.voId, this.attrNames).subscribe(groups => { this.groups = groups.filter(grp => this.authResolver.isAuthorized('addMember_Group_Member_policy', [grp])); this.loading = false; }, error => this.loading = false); diff --git a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts index 8ab80cc75e08f35ce4a1b0617210ba6959905221..22e38c46a89967e8cb01402d897a42b218a48af1 100644 --- a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts +++ b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts @@ -104,6 +104,7 @@ export class MemberGroupsComponent implements OnInit { config.width = "850px"; config.data = { memberId: this.memberId, + membersGroups: new Set<number>(this.groups.map(grp => grp.id)), theme: "member-theme" }; diff --git a/apps/admin-gui/src/assets/i18n/en.json b/apps/admin-gui/src/assets/i18n/en.json index a08987e13f11c9dcd777a549ada0b32275ee2a1b..3695ab940685b9d1cb262206e76de010182b8501 100644 --- a/apps/admin-gui/src/assets/i18n/en.json +++ b/apps/admin-gui/src/assets/i18n/en.json @@ -1807,8 +1807,9 @@ "TABLE_SEARCH": "Filter by name, description or ID", "TABLE_GROUP_DESCRIPTION": "Description", "DISABLED_HINT": "You cannot add members to this group or user is already member.", - "ALREADY_MEMBER_TOOLTIP": "Already member", - "CREATE_RELATION_AUTH_TOOLTIP": "You don't have permission to create relation with this group." + "ALREADY_MEMBER_TOOLTIP": "User is already member of group", + "CREATE_RELATION_AUTH_TOOLTIP": "You don't have permission to create relation with this group.", + "SYNCHRONIZED_GROUP": "Group is filled with members from external source" }, "GROUP_MENU": { "MOVE": "Move group", diff --git a/libs/perun/components/src/lib/groups-list/groups-list.component.html b/libs/perun/components/src/lib/groups-list/groups-list.component.html index 49e221ce9286ea5d66476369aa344b9855b96f77..bfacd10d7a8255f3cc320263a1f91e54e042f786 100644 --- a/libs/perun/components/src/lib/groups-list/groups-list.component.html +++ b/libs/perun/components/src/lib/groups-list/groups-list.component.html @@ -27,13 +27,14 @@ </th> <td *matCellDef="let row" class="static-column-size" mat-cell> <span - matTooltip="{{getCheckboxTooltipMessage() | translate}}" - [matTooltipDisabled]="!disableSelect(row.id)"> + matTooltip="{{getCheckboxTooltipMessage(row) | translate}}" + [matTooltipPosition]="'above'" + [matTooltipDisabled]="!disableSelect(row)"> <mat-checkbox (change)="$event ? itemSelectionToggle(row) : null" (click)="$event.stopPropagation()" [aria-label]="checkboxLabel(row)" [checked]="selection.isSelected(row)" - [disabled]="(row.name === 'members' && disableMembers) || disableSelect(row.id)" + [disabled]="(row.name === 'members' && disableMembers) || disableSelect(row)" color="primary"> </mat-checkbox> </span> diff --git a/libs/perun/components/src/lib/groups-list/groups-list.component.ts b/libs/perun/components/src/lib/groups-list/groups-list.component.ts index e6c1ce1d912e4bf229aca2cbf9da3619ec0d69f2..92613ca775ff2aeb31629c79efe8e5de14985c22 100644 --- a/libs/perun/components/src/lib/groups-list/groups-list.component.ts +++ b/libs/perun/components/src/lib/groups-list/groups-list.component.ts @@ -47,7 +47,7 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { moveGroup = new EventEmitter<Group>(); @Input() - groups: Group[] = []; + groups: Group[] | RichGroup[] = []; @Input() selection = new SelectionModel<Group>(true, []); @@ -92,7 +92,7 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { refreshTable = new EventEmitter<void>(); displayedColumns: string[] = ['select', 'id', 'vo', 'name', 'description', 'menu']; - dataSource: MatTableDataSource<Group>; + dataSource: MatTableDataSource<Group | RichGroup>; exporting = false; disabledRouting = false; @@ -138,14 +138,13 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { } } - isAllSelected() { - let numSelected = this.selection.selected.length; - - if (numSelected > 0 && this.hasMembersGroup && this.disableMembers) { - numSelected++; - } + canBeSelected(group: Group) { + return (group.name !== 'members' || !this.disableMembers) && !this.disableSelect(group); + } - const numRows = this.dataSource.data.length; + isAllSelected() { + const numSelected = this.selection.selected.length; + const numRows = this.dataSource.data.filter(grp => this.canBeSelected(grp)).length; return numSelected === numRows; } @@ -153,9 +152,7 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach(row => { - if (row.name !== 'members') { - this.selection.select(row); - } else if (!this.disableMembers) { + if (this.canBeSelected(row)) { this.selection.select(row); } }); @@ -172,8 +169,8 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.id + 1}`; } - disableSelect(id: number): boolean { - return this.disableGroups && this.groupsToDisable.has(id); + disableSelect(grp: Group): boolean { + return this.disableGroups && (this.groupsToDisable.has(grp.id) || this.isSynchronized(grp)); } ngAfterViewInit(): void { @@ -237,8 +234,21 @@ export class GroupsListComponent implements AfterViewInit, OnChanges { this.removeAuth = this.setAuth(); } - getCheckboxTooltipMessage() { - return this.authType === 'create-relation-dialog' ? 'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.CREATE_RELATION_AUTH_TOOLTIP' : - 'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.ALREADY_MEMBER_TOOLTIP' + isSynchronized(grp: RichGroup) { + if (grp.attributes){ + return grp.attributes.some(att => + att.friendlyName === "synchronizationEnabled" && att.value !== null && att.value.toString() === "true"); + } + return false; + } + + getCheckboxTooltipMessage(row: Group | RichGroup) { + if (this.authType === 'create-relation-dialog'){ + return 'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.CREATE_RELATION_AUTH_TOOLTIP'; + } else if (this.isSynchronized(row)){ + return 'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.SYNCHRONIZED_GROUP'; + } else { + return 'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.ALREADY_MEMBER_TOOLTIP'; + } } }