Skip to content

Commit

Permalink
fix: removeFromProjectMembership API http method, project membership … (
Browse files Browse the repository at this point in the history
  • Loading branch information
irmastnt committed Jan 8, 2024
1 parent 568f078 commit c1ab00c
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 123 deletions.
@@ -1,10 +1,10 @@
<dasch-swiss-app-progress-indicator *ngIf="isProjectsLoading$ | async"></dasch-swiss-app-progress-indicator>

<div *ngIf="(isProjectsLoading$ | async) === false">
<div *ngIf="isProjectAdmin$ | async" class="content large middle">
<div *ngIf="isCurrentProjectAdminOrSysAdmin$ | async" class="content large middle">
<!-- add user to the project -->
<app-add-user
*ngIf="(project$ | async)?.status && ((isSysAdmin$ | async) === true || (isProjectAdmin$ | async) === true)"
*ngIf="(project$ | async)?.status && ((isSysAdmin$ | async) === true)"
[projectUuid]="projectUuid"
(refreshParent)="refresh()"
#addUserComponent></app-add-user>
Expand All @@ -28,7 +28,7 @@
</div>
</div>

<div *ngIf="(isProjectAdmin$ | async) === false" class="content large middle">
<div *ngIf="(isCurrentProjectAdminOrSysAdmin$ | async) === false" class="content large middle">
<app-status [status]="403"></app-status>
</div>
</div>
Expand Up @@ -3,7 +3,12 @@ import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { LoadProjectMembersAction, ProjectsSelectors, UserSelectors } from '@dasch-swiss/vre/shared/app-state';
import {
IKeyValuePairs,
LoadProjectMembersAction,
ProjectsSelectors,
UserSelectors,
} from '@dasch-swiss/vre/shared/app-state';
import { Actions, Select, Store } from '@ngxs/store';
import { Observable, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
Expand Down Expand Up @@ -47,9 +52,9 @@ export class CollaborationComponent extends ProjectBase implements OnInit, OnDes
);
}

@Select(ProjectsSelectors.projectMembers) projectMembers$: Observable<ReadUser[]>;
@Select(ProjectsSelectors.isProjectsLoading)
isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.projectMembers) projectMembers$: Observable<IKeyValuePairs<ReadUser>>;
@Select(ProjectsSelectors.isProjectsLoading) isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin) isCurrentProjectAdminOrSysAdmin$: Observable<boolean>;
@Select(UserSelectors.isSysAdmin) isSysAdmin$: Observable<boolean>;
@Select(UserSelectors.user) user$: Observable<ReadUser>;
@Select(ProjectsSelectors.currentProject) project$: Observable<ReadProject>;
Expand Down
Expand Up @@ -3,23 +3,19 @@ import {
ChangeDetectorRef,
Component,
EventEmitter,
Inject,
Input,
OnInit,
Output,
} from '@angular/core';
import {
ApiResponseError,
CreateChildNodeRequest,
KnoraApiConnection,
List,
ListNodeInfo,
StringLiteral,
UpdateChildNodeRequest,
} from '@dasch-swiss/dsp-js';
import { ListApiService } from '@dasch-swiss/vre/shared/app-api';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import { AppErrorHandler } from '@dasch-swiss/vre/shared/app-error-handler';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';

@Component({
Expand Down Expand Up @@ -72,11 +68,7 @@ export class EditListItemComponent implements OnInit {
formInvalidMessage: string;

constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _listApiService: ListApiService,
private _errorHandler: AppErrorHandler,
private _projectService: ProjectService,
private _cd: ChangeDetectorRef
) {}

Expand Down Expand Up @@ -178,7 +170,7 @@ export class EditListItemComponent implements OnInit {
*/
insertChildNode() {
const createChildNodeRequest: CreateChildNodeRequest = new CreateChildNodeRequest();
createChildNodeRequest.name = `${this._projectService.iriToUuid(this.projectIri)}-${Math.random()
createChildNodeRequest.name = `${ProjectService.IriToUuid(this.projectIri)}-${Math.random()
.toString(36)
.substring(2)}${Math.random().toString(36).substring(2)}`;
createChildNodeRequest.parentNodeIri = this.parentIri;
Expand Down
8 changes: 4 additions & 4 deletions apps/dsp-app/src/app/project/ontology/ontology.component.ts
Expand Up @@ -34,13 +34,13 @@ import {
import { AppErrorHandler } from '@dasch-swiss/vre/shared/app-error-handler';
import {
DefaultClass,
DefaultProperties,
DefaultResourceClasses,
SortingService,
OntologyService,
ProjectService,
DefaultProperties,
PropertyCategory,
PropertyInfoObject,
OntologyService,
SortingService,
} from '@dasch-swiss/vre/shared/app-helper-services';
import {
ClearCurrentOntologyAction,
Expand Down Expand Up @@ -429,7 +429,7 @@ export class OntologyComponent extends ProjectBase implements OnInit, OnDestroy
const ontology = this._store.selectSnapshot(OntologiesSelectors.currentOntology);
const title = iri ? ontology.label : 'Data model';

const uuid = this._projectService.iriToUuid(this.projectUuid);
const uuid = ProjectService.IriToUuid(this.projectUuid);
const existingOntologyNames = this._store.selectSnapshot(OntologiesSelectors.currentProjectExistingOntologyNames);

const dialogConfig: MatDialogConfig = {
Expand Down
80 changes: 43 additions & 37 deletions apps/dsp-app/src/app/project/project-base.ts
Expand Up @@ -4,6 +4,7 @@ import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import {
IKeyValuePairs,
LoadProjectAction,
LoadProjectOntologiesAction,
OntologiesSelectors,
Expand All @@ -22,35 +23,30 @@ export class ProjectBase implements OnInit, OnDestroy {
project: ReadProject; // TODO use project$ instead

// permissions of logged-in user
get isAdmin$(): Observable<boolean> {
return combineLatest([
this.user$.pipe(filter(user => user !== null)),
this.userProjectAdminGroups$,
this._route.params,
this._route.parent.params,
]).pipe(
takeUntil(this.destroyed),
map(([user, userProjectGroups, params, parentParams]) => {
const projectIri = this._projectService.uuidToIri(params.uuid ? params.uuid : parentParams.uuid);
return ProjectService.IsProjectAdminOrSysAdmin(user, userProjectGroups, projectIri);
})
);
}
isAdmin$: Observable<boolean> = combineLatest([
this._store.select(UserSelectors.user).pipe(filter(user => user !== null)) as Observable<ReadUser>,
this._store.select(UserSelectors.userProjectAdminGroups),
this._route.params,
this._route.parent.params,
]).pipe(
takeUntil(this.destroyed),
map(([user, userProjectGroups, params, parentParams]) => {
const projectIri = this._projectService.uuidToIri(params.uuid ? params.uuid : parentParams.uuid);
return ProjectService.IsProjectAdminOrSysAdmin(user, userProjectGroups, projectIri);
})
);

get projectIri() {
return this._projectService.uuidToIri(this.projectUuid);
}

@Select(UserSelectors.user) user$: Observable<ReadUser>;
@Select(UserSelectors.userProjectAdminGroups)
userProjectAdminGroups$: Observable<string[]>;
@Select(ProjectsSelectors.isCurrentProjectAdmin)
isProjectAdmin$: Observable<boolean>;
@Select(ProjectsSelectors.isCurrentProjectMember)
isProjectMember$: Observable<boolean>;
@Select(UserSelectors.userProjectAdminGroups) userProjectAdminGroups$: Observable<string[]>;
@Select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin) isCurrentProjectAdminOrSysAdmin: Observable<boolean>;
@Select(ProjectsSelectors.isCurrentProjectMember) isProjectMember$: Observable<boolean>;
@Select(ProjectsSelectors.currentProject) project$: Observable<ReadProject>;
@Select(ProjectsSelectors.isProjectsLoading)
isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.isProjectsLoading) isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.projectMembers) projectMembers$: Observable<IKeyValuePairs<ReadUser>>;

constructor(
protected _store: Store,
Expand All @@ -68,16 +64,7 @@ export class ProjectBase implements OnInit, OnDestroy {
}

ngOnInit(): void {
this.project = this._store.selectSnapshot(ProjectsSelectors.currentProject);
if (this.projectUuid && (!this.project || this.project.id !== this.projectIri)) {
// get current project data, project members and project groups
// and set the project state here
this.loadProject();
} else if (!this.isOntologiesAvailable()) {
this.isProjectsLoading$.pipe(takeWhile(isLoading => isLoading === false)).subscribe((isLoading: boolean) => {
this._store.dispatch(new LoadProjectOntologiesAction(this.projectUuid));
});
}
this.loadProject();
}

ngOnDestroy(): void {
Expand All @@ -95,11 +82,30 @@ export class ProjectBase implements OnInit, OnDestroy {
}

private loadProject(): void {
this._store.dispatch(new LoadProjectAction(this.projectUuid, true));
this._actions$
.pipe(ofActionSuccessful(LoadProjectAction))
.pipe(take(1))
.subscribe(() => this.setProjectData());
this.isProjectsLoading$
.pipe(
takeWhile(isLoading => isLoading === false && this.projectUuid !== undefined),
take(1)
)
.subscribe({
next: () => {
const projectMembers = this._store.selectSnapshot(ProjectsSelectors.projectMembers)[this.projectIri];
this.project = this._store.selectSnapshot(ProjectsSelectors.currentProject);
if (!this.project || this.project.id !== this.projectIri || !projectMembers) {
// get current project data, project members and project groups
// and set the project state here
this._store.dispatch(new LoadProjectAction(this.projectUuid, true));
} else if (!this.isOntologiesAvailable()) {
this._store.dispatch(new LoadProjectOntologiesAction(this.projectUuid));
}
},
complete: () => {
this._actions$
.pipe(ofActionSuccessful(LoadProjectAction))
.pipe(take(1))
.subscribe(() => this.setProjectData());
},
});
}

private setProjectData(): void {
Expand Down
Expand Up @@ -13,18 +13,15 @@ export class ProjectTileComponent {
@Input() project: StoredProject;
@Input() sysAdmin: boolean; // used to show settings button

constructor(
private _router: Router,
private _projectService: ProjectService
) {}
constructor(private _router: Router) {}

navigateToProject(id: string) {
const uuid = this._projectService.iriToUuid(id);
const uuid = ProjectService.IriToUuid(id);
this._router.navigate([RouteConstants.project, uuid]);
}

navigateToSettings(id: string) {
const uuid = this._projectService.iriToUuid(id);
const uuid = ProjectService.IriToUuid(id);
this._router.navigate([RouteConstants.project, uuid, RouteConstants.settings, RouteConstants.collaboration]);
}
}
Expand Up @@ -66,7 +66,7 @@ <h5 class="mat-subtitle-1 info-names">
<!-- group: only in project view -->
<td class="table-select-group">
<app-select-group
*ngIf="(isProjectAdmin$ | async) && projectUuid"
*ngIf="(isCurrentProjectAdminOrSysAdmin$ | async) === true && projectUuid"
[projectCode]="(project$ | async)?.shortcode"
[projectid]="(project$ | async)?.id"
[permissions]="
Expand All @@ -79,7 +79,7 @@ <h5 class="mat-subtitle-1 info-names">

<!-- action: menu with edit, remove, etc. -->
<td class="table-action">
<button mat-icon-button [matMenuTriggerFor]="projectUserMenu" [disabled]="disableMenu()">
<button mat-icon-button [matMenuTriggerFor]="projectUserMenu" [disabled]="disableMenu$ | async">
<mat-icon>more_horiz</mat-icon>
</button>
<mat-menu #projectUserMenu="matMenu" xPosition="before" class="menu">
Expand Down
Expand Up @@ -3,23 +3,15 @@ import {
ChangeDetectorRef,
Component,
EventEmitter,
Inject,
Input,
OnInit,
Output,
} from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import {
ApiResponseError,
Constants,
KnoraApiConnection,
Permissions,
ReadProject,
ReadUser,
} from '@dasch-swiss/dsp-js';
import { ApiResponseError, Constants, Permissions, ReadProject, ReadUser } from '@dasch-swiss/dsp-js';
import { UserApiService } from '@dasch-swiss/vre/shared/app-api';
import { DspApiConnectionToken, RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { AppErrorHandler } from '@dasch-swiss/vre/shared/app-error-handler';
import { ProjectService, SortingService } from '@dasch-swiss/vre/shared/app-helper-services';
import {
Expand All @@ -31,9 +23,9 @@ import {
SetUserAction,
UserSelectors,
} from '@dasch-swiss/vre/shared/app-state';
import { Actions, ofActionSuccessful, Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { switchMap, take } from 'rxjs/operators';
import { Actions, Select, Store, ofActionSuccessful } from '@ngxs/store';
import { Observable, combineLatest } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { ConfirmDialogComponent } from '../../../main/action/confirm-dialog/confirm-dialog.component';
import { DialogComponent } from '../../../main/dialog/dialog.component';

Expand Down Expand Up @@ -105,27 +97,37 @@ export class UsersListComponent implements OnInit {
// ... and sort by 'username'
sortBy = 'username';

disableMenu$: Observable<boolean> = combineLatest([
this._store.select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin),
this._store.select(UserSelectors.isSysAdmin),
]).pipe(
map(([isCurrentProjectAdminOrSysAdmin, isSysAdmin]) => {
if (this.project && this.project.status === false) {
return true;
} else {
const isProjectAdmin = this.projectUuid ? isCurrentProjectAdminOrSysAdmin : false;
return !isProjectAdmin && !isSysAdmin;
}
})
);

@Select(UserSelectors.isSysAdmin) isSysAdmin$: Observable<boolean>;
@Select(UserSelectors.user) user$: Observable<ReadUser>;
@Select(UserSelectors.username) username$: Observable<string>;
@Select(ProjectsSelectors.isCurrentProjectAdmin)
isProjectAdmin$: Observable<boolean>;
@Select(UserSelectors.userProjectAdminGroups) userProjectAdminGroups$: Observable<string[]>;
@Select(ProjectsSelectors.isCurrentProjectAdminOrSysAdmin) isCurrentProjectAdminOrSysAdmin$: Observable<boolean>;
@Select(ProjectsSelectors.currentProject) project$: Observable<ReadProject>;
@Select(ProjectsSelectors.isProjectsLoading)
isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.isProjectsLoading) isProjectsLoading$: Observable<boolean>;
@Select(UserSelectors.isLoading) isUsersLoading$: Observable<boolean>;

constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _userApiService: UserApiService,
private _dialog: MatDialog,
private _errorHandler: AppErrorHandler,
private _route: ActivatedRoute,
private _router: Router,
private _sortingService: SortingService,
private _store: Store,
private _projectService: ProjectService,
private _actions$: Actions,
private _cd: ChangeDetectorRef
) {
Expand Down Expand Up @@ -238,9 +240,9 @@ export class UsersListComponent implements OnInit {
*/
updateProjectAdminMembership(id: string, permissions: Permissions): void {
const currentUser = this._store.selectSnapshot(UserSelectors.user);
if (this.userIsProjectAdmin(permissions)) {
const userIsProjectAdmin = this.userIsProjectAdmin(permissions);
if (userIsProjectAdmin) {
// true = user is already project admin --> remove from admin rights

this._userApiService.removeFromProjectMembership(id, this.project.id, true).subscribe(
response => {
// if this user is not the logged-in user
Expand Down Expand Up @@ -281,7 +283,7 @@ export class UsersListComponent implements OnInit {
);
} else {
// false: user isn't project admin yet --> add admin rights
this._userApiService.addToProjectMembership(id, this.project.id).subscribe(
this._userApiService.addToProjectMembership(id, this.project.id, true).subscribe(
response => {
if (currentUser.username !== response.user.username) {
this._store.dispatch(new SetUserAction(response.user));
Expand Down Expand Up @@ -438,19 +440,6 @@ export class UsersListComponent implements OnInit {
);
}

disableMenu(): boolean {
// disable menu in case of:
// project.status = false
if (this.project && this.project.status === false) {
return true;
} else {
return (
!this._store.selectSnapshot(UserSelectors.isSysAdmin) &&
!this._store.selectSnapshot(ProjectsSelectors.isCurrentProjectAdmin)
);
}
}

sortList(key: any) {
this.sortBy = key;
this.list = this._sortingService.keySortByAlphabetical(this.list, this.sortBy as any);
Expand Down

0 comments on commit c1ab00c

Please sign in to comment.