diff --git a/projects/nae-example-app/src/app/doc/panels/panels.component.ts b/projects/nae-example-app/src/app/doc/panels/panels.component.ts index 87e588f285..e1b4a8c500 100644 --- a/projects/nae-example-app/src/app/doc/panels/panels.component.ts +++ b/projects/nae-example-app/src/app/doc/panels/panels.component.ts @@ -68,7 +68,8 @@ export class PanelsComponent implements OnInit { stringId: null, petriNetId: null, permissions: {}, - users: {} + users: {}, + workspaceId: '' }; this.workflow = { stringId: 'ID', @@ -83,7 +84,8 @@ export class PanelsComponent implements OnInit { email: 'test@netgrif.com', fullName: 'Test Testovič' }, - immediateData: [] + immediateData: [], + workspaceId: '' }; this.featuredFields$ = new BehaviorSubject>([ new HeaderColumn(HeaderColumnType.META, 'visualId', 'Visual ID', 'text'), diff --git a/projects/nae-example-app/src/app/doc/sidemenu-example/sidemenu-example.component.ts b/projects/nae-example-app/src/app/doc/sidemenu-example/sidemenu-example.component.ts index a8434c714f..63500921a8 100644 --- a/projects/nae-example-app/src/app/doc/sidemenu-example/sidemenu-example.component.ts +++ b/projects/nae-example-app/src/app/doc/sidemenu-example/sidemenu-example.component.ts @@ -40,7 +40,8 @@ export class SidemenuExampleComponent implements OnInit { immediateData: [], initials: 'EX', title: 'Example Dummy Process', - version: '1.0.0' + version: '1.0.0', + workspaceId: '' }), new Net({ stringId: '999', @@ -52,7 +53,8 @@ export class SidemenuExampleComponent implements OnInit { immediateData: [], initials: 'EXX', title: 'Other Example Dummy Process', - version: '1.0.0' + version: '1.0.0', + workspaceId: '' })]) }); } diff --git a/projects/netgrif-components-core/src/assets/i18n/de.json b/projects/netgrif-components-core/src/assets/i18n/de.json index a8f7aa7ba6..31e58761ac 100644 --- a/projects/netgrif-components-core/src/assets/i18n/de.json +++ b/projects/netgrif-components-core/src/assets/i18n/de.json @@ -118,6 +118,9 @@ "impersonating": "in Vertretung als" } }, + "navigation":{ + "workspace": "Arbeitsbereich auswählen" + }, "search": { "category": { "case": { diff --git a/projects/netgrif-components-core/src/assets/i18n/en.json b/projects/netgrif-components-core/src/assets/i18n/en.json index d2bbf05e8d..116f58dc38 100644 --- a/projects/netgrif-components-core/src/assets/i18n/en.json +++ b/projects/netgrif-components-core/src/assets/i18n/en.json @@ -118,6 +118,9 @@ "impersonating": "impersonating" } }, + "navigation":{ + "workspace": "Choose workspace" + }, "search": { "category": { "case": { diff --git a/projects/netgrif-components-core/src/assets/i18n/sk.json b/projects/netgrif-components-core/src/assets/i18n/sk.json index e47da7ff94..6d8006d6c3 100644 --- a/projects/netgrif-components-core/src/assets/i18n/sk.json +++ b/projects/netgrif-components-core/src/assets/i18n/sk.json @@ -118,6 +118,9 @@ "impersonating": "v zastúpení" } }, + "navigation":{ + "workspace": "Výber workspacu" + }, "search": { "category": { "case": { diff --git a/projects/netgrif-components-core/src/lib/authentication/models/user.transformer.ts b/projects/netgrif-components-core/src/lib/authentication/models/user.transformer.ts index 9bc4b572e5..03ae85447b 100644 --- a/projects/netgrif-components-core/src/lib/authentication/models/user.transformer.ts +++ b/projects/netgrif-components-core/src/lib/authentication/models/user.transformer.ts @@ -23,6 +23,7 @@ export class UserTransformer implements Transformer { user.realmId, user.name, user.surname, + user.workspaceId, this.transformAuthorities(user.authorities), user.processRoles, groups, diff --git a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts index edd1b6dde4..ce1fd9dfa6 100644 --- a/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts +++ b/projects/netgrif-components-core/src/lib/navigation/navigation-double-drawer/abstract-navigation-double-drawer.ts @@ -3,8 +3,8 @@ import {Component, Input, OnDestroy, OnInit, TemplateRef} from '@angular/core'; import {MatDrawerMode} from '@angular/material/sidenav'; import {ActivatedRoute, Router} from '@angular/router'; import {ResizeEvent} from 'angular-resizable-element'; -import {Observable, of, Subscription} from 'rxjs'; -import {map} from 'rxjs/operators'; +import {Observable, of, Subject, Subscription} from 'rxjs'; +import {map, tap} from 'rxjs/operators'; import {RoleAccess, View} from '../../../commons/schema'; import {AccessService} from '../../authorization/permission/access.service'; import {ConfigurationService} from '../../configuration/configuration.service'; @@ -43,6 +43,7 @@ import { RIGHT_SIDE_NEW_PAGE_SIZE, SETTINGS_TRANSITION_ID } from '../model/navigation-configs'; +import {Workspace} from "../../user/models/workspace"; @Component({ @@ -117,6 +118,8 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, }; protected _childCustomViews: { [uri: string]: { [key: string]: NavigationItem } }; + public userWorkspaces$: Observable>; + public userWorkspacesLength: number; protected constructor(protected _router: Router, protected _activatedRoute: ActivatedRoute, @@ -164,6 +167,10 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, this.resolveHiddenMenuItemFromChildViews(viewConfigurationPath + '/' + key, childView); }); } + + this.userWorkspaces$ = this._userService.workspaces$.pipe( + tap(arr => this.userWorkspacesLength = arr.length) + ); } get currentNode(): UriNodeResource { @@ -229,6 +236,14 @@ export abstract class AbstractNavigationDoubleDrawerComponent implements OnInit, return this._configRightMenu; } + setWorkspace(workspace: string) { + this._userService.changeWorkspace(workspace); + } + + activeWorkspace() { + return this._userService.user.workspaceId; + } + toggleMenu() { this.toggleRightMenu(); if (this.allClosable) { diff --git a/projects/netgrif-components-core/src/lib/process/net.ts b/projects/netgrif-components-core/src/lib/process/net.ts index a87b8c0194..9b5fd07f50 100644 --- a/projects/netgrif-components-core/src/lib/process/net.ts +++ b/projects/netgrif-components-core/src/lib/process/net.ts @@ -23,6 +23,10 @@ export class Net implements PetriNetReferenceWithPermissions { * @ignore */ private _identifier: string; + /** + * Workspace id + * */ + private _workspaceId: string; /** * @ignore * */ @@ -84,6 +88,7 @@ export class Net implements PetriNetReferenceWithPermissions { this._transactions = []; this._roles = []; this._uriNodeId = net.uriNodeId; + this._workspaceId = net.workspaceId } get stringId(): string { @@ -197,4 +202,13 @@ export class Net implements PetriNetReferenceWithPermissions { set uriNodeId(uriNodeId: string) { this._uriNodeId = uriNodeId; } + + + get workspaceId(): string { + return this._workspaceId; + } + + set workspaceId(value: string) { + this._workspaceId = value; + } } diff --git a/projects/netgrif-components-core/src/lib/process/process.service.ts b/projects/netgrif-components-core/src/lib/process/process.service.ts index b51b6cd0de..cb4bcbc241 100644 --- a/projects/netgrif-components-core/src/lib/process/process.service.ts +++ b/projects/netgrif-components-core/src/lib/process/process.service.ts @@ -269,7 +269,7 @@ export class ProcessService implements OnDestroy { protected loadNetReference(id: string): Observable { const returnReference = new ReplaySubject(1); this._petriNetResource.getOne(id, this.LATEST).subscribe(reference => { - returnReference.next(!reference.stringId ? null : reference); + returnReference.next(!reference?.stringId ? null : reference); returnReference.complete(); return; }, error => { diff --git a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/user-resource.service.ts b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/user-resource.service.ts index 6fa7cd39d5..932e92791a 100644 --- a/projects/netgrif-components-core/src/lib/resources/engine-endpoint/user-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/resources/engine-endpoint/user-resource.service.ts @@ -10,6 +10,7 @@ import {Page} from '../interface/page'; import {GroupsInterface} from '../interface/group'; import {AbstractResourceService} from '../abstract-endpoint/abstract-resource.service'; import {UserResource} from '../interface/user-resource'; +import {Workspace} from "../../user/models/workspace"; @Injectable({ providedIn: 'root', @@ -69,6 +70,30 @@ export class UserResourceService extends AbstractResourceService { .pipe(map(r => this.mapToPage(r))); } + /** + * Get all workspaces + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/user/workspaces + */ + public getAllWorkspaces(params?: Params): Observable> { + return this._resourceProvider.get$('users/workspaces', this.SERVER_URL, params) + .pipe(map(r => this.changeType(r, undefined))); + } + + /** + * Get all workspaces + * + * **Request Type:** GET + * + * **Request URL:** {{baseUrl}}/api/user/workspaces + */ + public changeWorkspace(workspaceId: string, params?: Params): Observable { + return this._resourceProvider.post$('users/workspace', this.SERVER_URL, {workspaceId}, params) + .pipe(map(r => this.changeType(r, undefined))); + } + /** * Get all users with specified roles * diff --git a/projects/netgrif-components-core/src/lib/resources/interface/case.ts b/projects/netgrif-components-core/src/lib/resources/interface/case.ts index a09d6c0f6d..bd6d0150eb 100644 --- a/projects/netgrif-components-core/src/lib/resources/interface/case.ts +++ b/projects/netgrif-components-core/src/lib/resources/interface/case.ts @@ -111,6 +111,10 @@ export interface Case { * **Example:** 5e4cd5070a975a19858772aa */ petriNetId: string; + /** + * Workspace id + * */ + workspaceId: string; /** * **Example:** home */ diff --git a/projects/netgrif-components-core/src/lib/resources/interface/petri-net-reference.ts b/projects/netgrif-components-core/src/lib/resources/interface/petri-net-reference.ts index 9ecffbd45e..5b0d863bf1 100644 --- a/projects/netgrif-components-core/src/lib/resources/interface/petri-net-reference.ts +++ b/projects/netgrif-components-core/src/lib/resources/interface/petri-net-reference.ts @@ -48,5 +48,9 @@ export interface PetriNetReference { * [ImmediateData]{@link ImmediateData} */ immediateData: Array; + /** + * Workspace id + * */ + workspaceId: string; } diff --git a/projects/netgrif-components-core/src/lib/resources/interface/task.ts b/projects/netgrif-components-core/src/lib/resources/interface/task.ts index 08bce8ad83..3dcec0ef06 100644 --- a/projects/netgrif-components-core/src/lib/resources/interface/task.ts +++ b/projects/netgrif-components-core/src/lib/resources/interface/task.ts @@ -70,4 +70,8 @@ export interface Task { delegateTitle?: string; immediateData?: Array; assignedUserPolicy?: AssignedUserPolicy; + /** + * Workspace id + * */ + workspaceId: string; } diff --git a/projects/netgrif-components-core/src/lib/user/models/iuser.ts b/projects/netgrif-components-core/src/lib/user/models/iuser.ts index 1c02dc2bee..4d88f815e8 100644 --- a/projects/netgrif-components-core/src/lib/user/models/iuser.ts +++ b/projects/netgrif-components-core/src/lib/user/models/iuser.ts @@ -34,4 +34,8 @@ export interface IUser { * **Example:** Netgrif */ lastName: string; + /** + * **Example:** default + */ + workspaceId: string; } diff --git a/projects/netgrif-components-core/src/lib/user/models/user.ts b/projects/netgrif-components-core/src/lib/user/models/user.ts index 86786fc2ca..aceac25a4d 100644 --- a/projects/netgrif-components-core/src/lib/user/models/user.ts +++ b/projects/netgrif-components-core/src/lib/user/models/user.ts @@ -13,6 +13,7 @@ export class User implements IUser { public realmId: string, public firstName: string, public lastName: string, + public workspaceId: string, public authorities: Array, public roles: Array, public groups?: Array, diff --git a/projects/netgrif-components-core/src/lib/user/models/workspace.ts b/projects/netgrif-components-core/src/lib/user/models/workspace.ts new file mode 100644 index 0000000000..e5572da65e --- /dev/null +++ b/projects/netgrif-components-core/src/lib/user/models/workspace.ts @@ -0,0 +1,5 @@ + +export interface Workspace { + id: string; + defaultWorkspace: boolean; +} diff --git a/projects/netgrif-components-core/src/lib/user/services/user.service.ts b/projects/netgrif-components-core/src/lib/user/services/user.service.ts index ae9316455f..b6fbd4695d 100644 --- a/projects/netgrif-components-core/src/lib/user/services/user.service.ts +++ b/projects/netgrif-components-core/src/lib/user/services/user.service.ts @@ -1,5 +1,5 @@ import {Injectable, OnDestroy} from '@angular/core'; -import {Observable, ReplaySubject, Subscription} from 'rxjs'; +import {BehaviorSubject, Observable, ReplaySubject, Subscription} from 'rxjs'; import {ProcessRole} from '../../resources/interface/process-role'; import {User} from '../models/user'; import {Credentials} from '../../authentication/models/credentials'; @@ -12,6 +12,7 @@ import {HttpErrorResponse} from '@angular/common/http'; import {SessionService} from '../../authentication/session/services/session.service'; import {UserResource} from '../../resources/interface/user-resource'; import {AnonymousService} from '../../authentication/anonymous/anonymous.service'; +import {Workspace} from "../models/workspace"; @Injectable({ providedIn: 'root' @@ -25,6 +26,7 @@ export class UserService implements OnDestroy { protected _subAuth: Subscription; protected _subAnonym: Subscription; private _publicLoadCalled: boolean; + protected _workspaces: ReplaySubject>; public readonly GLOBAL_ROLE_PREFIX = 'global_'; @@ -37,11 +39,14 @@ export class UserService implements OnDestroy { this._user = this.emptyUser(); this._loginCalled = false; this._userChange$ = new ReplaySubject(1); + this._workspaces = new ReplaySubject>(1); this._anonymousUserChange$ = new ReplaySubject(1); setTimeout(() => { this._subAuth = this._authService.authenticated$.subscribe(auth => { if (auth && !this._loginCalled) { this.loadUser(); + } else if (auth && this._loginCalled) { + this.loadWorkspaces(); } else if (!auth) { this.clearUser(); this.publishUserChange(); @@ -66,6 +71,10 @@ export class UserService implements OnDestroy { return this._userChange$.asObservable(); } + get workspaces$(): Observable> { + return this._workspaces.asObservable(); + } + get anonymousUser(): User { return this.anonymousUser; } @@ -177,7 +186,7 @@ export class UserService implements OnDestroy { } protected emptyUser() { - return new User('', '', '', '', '', '', [], [], [], []); + return new User('', '', '', '', '', '', '', [], [], [], []); } protected loadUser(): void { @@ -186,6 +195,7 @@ export class UserService implements OnDestroy { const backendUser = {...user, id: user.id.toString()}; this._user = this._userTransform.transform(backendUser); this.publishUserChange(); + this.loadWorkspaces(); } }, error => { if (error instanceof HttpErrorResponse && error.status === 401) { @@ -210,6 +220,24 @@ export class UserService implements OnDestroy { }); } + public loadWorkspaces() { + this._userResource.getAllWorkspaces().pipe(take(1)).subscribe(workspaces => { + if (workspaces) { + this._workspaces.next(workspaces) + } + }, error => { + this._log.error('Loading workspaces has failed!', error); + }); + } + + public changeWorkspace(workspaceId: string) { + this._userResource.changeWorkspace(workspaceId).subscribe(user => { + if (user) { + window.location.reload(); + } + }); + } + public clearUser() { this._user = this.emptyUser(); } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts index 3bf1ab5798..360eeb3f48 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication-method-service.ts @@ -5,7 +5,7 @@ import {UserResource} from '../../../resources/interface/user-resource'; export class MockAuthenticationMethodService extends AuthenticationMethodService { login(credentials: Credentials): Observable { - return of({email: 'mail', id: 'id', username: 'username', realmId: 'realmId', name: 'name', firstName: 'name', surname: 'surname', lastName: 'surname', fullName: 'name surname', + return of({email: 'mail', id: 'id', username: 'username', realmId: 'realmId', name: 'name', firstName: 'name', surname: 'surname', lastName: 'surname', fullName: 'name surname', workspaceId: '', groups: [], authorities: [], nextGroups: [], processRoles: []}); } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts index e97d420640..128ca20c4e 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-authentication.service.ts @@ -18,9 +18,8 @@ export class MockAuthenticationService extends AuthenticationService { super(_auth, _config, _sessionService, _userTransformer); } - login(credentials: Credentials): Observable { - return of(new User('id', 'username', 'mail', 'realmId', 'name', 'surname', ['ADMIN'], [{stringId: 'id', name: 'id', importId: 'id'}])); + return of(new User('id', 'username', 'mail', 'realmId', 'name', 'surname', '', ['ADMIN'], [{stringId: 'id', name: 'id', importId: 'id'}])); } logout(): Observable { diff --git a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-user-resource.service.ts b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-user-resource.service.ts index 87a0f2d7b5..2e7ef27760 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-user-resource.service.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-user-resource.service.ts @@ -21,7 +21,7 @@ export class MockUserResourceService { } public getLoggedUser(): Observable { - return of({email: 'mail', id: 'id', username: 'username', realmId: 'realmId', name: 'name', firstName: 'name', surname: 'surname', lastName: 'surname', fullName: 'name surname', + return of({email: 'mail', id: 'id', username: 'username', realmId: 'realmId', name: 'name', firstName: 'name', surname: 'surname', lastName: 'surname', fullName: 'name surname', workspaceId: '', groups: [], authorities: [], nextGroups: [], processRoles: []}); } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.ts b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.ts index b1508aaf80..11b4db979f 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.ts @@ -35,6 +35,7 @@ export function createMockCase(stringId = 'stringId', stringId, petriNetId, permissions: {}, - users: {} + users: {}, + workspaceId: '' }; } diff --git a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-net.ts b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-net.ts index ca77e9f484..48f84a53d7 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-net.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-net.ts @@ -31,6 +31,7 @@ export function createMockNet(stringId = 'stringId', uriNodeId: identifier, version: '1.0.0', initials: 'NET', + workspaceId: '', defaultCaseName: '', createdDate: [2021, 2, 4, 12, 50, 0, 1612443000], author: { @@ -43,6 +44,7 @@ export function createMockNet(stringId = 'stringId', net.transitions = transitions.map(t => ({ ...t, petriNetId: '', + workspaceId: '', immediateData: [] })); net.permissions = permissions; diff --git a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-task.ts b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-task.ts index 98dd509f58..40e3bd5605 100644 --- a/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-task.ts +++ b/projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-task.ts @@ -18,6 +18,7 @@ export function createMockTask(stringId = 'stringId', caseColor: 'string', caseTitle: 'string', userId: undefined, + workspaceId: 'string', roles: {}, users: {}, userRefs: {}, diff --git a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html index 2d4c530c7a..b14276cad9 100644 --- a/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html +++ b/projects/netgrif-components/src/lib/navigation/navigation-double-drawer/navigation-double-drawer.component.html @@ -114,6 +114,21 @@ + + + + + +