diff --git a/package-lock.json b/package-lock.json
index dabf8a78f7..515766f31b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,7 @@
"@angular/router": "^11.2.9",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^3.0.0",
- "@dasch-swiss/dsp-ui": "^1.7.4",
+ "@dasch-swiss/dsp-ui": "^1.7.5",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "5.0.0",
"3d-force-graph": "^1.60.12",
@@ -76,28 +76,6 @@
"typescript": "4.0.7"
}
},
- ".yalc/@dasch-swiss/dsp-ui": {
- "version": "1.7.1",
- "extraneous": true,
- "license": "AGPL-3.0-or-later",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "peerDependencies": {
- "@angular/cdk": "^11.2.5",
- "@angular/common": "~11.2.6",
- "@angular/core": "~11.2.6",
- "@angular/material": "^11.2.5",
- "@ckeditor/ckeditor5-angular": "^1.2.3",
- "@dasch-swiss/dsp-js": "^2.7.0",
- "ckeditor5-custom-build": "github:dasch-swiss/ckeditor_custom_build",
- "jdnconvertiblecalendar": "^0.0.7",
- "jdnconvertiblecalendardateadapter": "^0.0.17",
- "ngx-color-picker": "^11.0.0",
- "openseadragon": "^2.4.2",
- "svg-overlay": "github:openseadragon/svg-overlay"
- }
- },
"node_modules/@angular-devkit/architect": {
"version": "0.1102.14",
"dev": true,
@@ -2137,9 +2115,9 @@
}
},
"node_modules/@dasch-swiss/dsp-ui": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-ui/-/dsp-ui-1.7.4.tgz",
- "integrity": "sha512-fQyHcgl8w68T3XcvamR0jhcTiUqo3b+VB+uwJHWP6pjHx4uF9DQCSKHtDVmBkmAbCo+99bD3I4hDROrvNbq/TQ==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-ui/-/dsp-ui-1.7.5.tgz",
+ "integrity": "sha512-SeJ4sBlhtH+lQVrhucsJdik4XI9Ek7u20j5OmyjDxG6ekoZFXmdhGcn1BmS5VjJAo48H67z3L+7kj05Za8s94Q==",
"dependencies": {
"tslib": "^2.0.0"
},
@@ -2150,6 +2128,7 @@
"@angular/material": "^11.2.5",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^3.0.0",
+ "angular-split": "^4.0.0",
"ckeditor5-custom-build": "github:dasch-swiss/ckeditor_custom_build",
"jdnconvertiblecalendar": "^0.0.7",
"jdnconvertiblecalendardateadapter": "^0.0.17",
@@ -9928,9 +9907,10 @@
}
},
"node_modules/jszip": {
- "version": "3.6.0",
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+ "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
"dev": true,
- "license": "(MIT OR GPL-3.0)",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
@@ -15867,9 +15847,10 @@
}
},
"node_modules/tar": {
- "version": "6.1.0",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz",
+ "integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==",
"dev": true,
- "license": "ISC",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -16548,9 +16529,10 @@
}
},
"node_modules/url-parse": {
- "version": "1.5.1",
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
+ "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -20036,9 +20018,9 @@
}
},
"@dasch-swiss/dsp-ui": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-ui/-/dsp-ui-1.7.4.tgz",
- "integrity": "sha512-fQyHcgl8w68T3XcvamR0jhcTiUqo3b+VB+uwJHWP6pjHx4uF9DQCSKHtDVmBkmAbCo+99bD3I4hDROrvNbq/TQ==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-ui/-/dsp-ui-1.7.5.tgz",
+ "integrity": "sha512-SeJ4sBlhtH+lQVrhucsJdik4XI9Ek7u20j5OmyjDxG6ekoZFXmdhGcn1BmS5VjJAo48H67z3L+7kj05Za8s94Q==",
"requires": {
"tslib": "^2.0.0"
}
@@ -25369,7 +25351,9 @@
}
},
"jszip": {
- "version": "3.6.0",
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+ "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
"dev": true,
"requires": {
"lie": "~3.3.0",
@@ -29308,7 +29292,9 @@
"dev": true
},
"tar": {
- "version": "6.1.0",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz",
+ "integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==",
"dev": true,
"requires": {
"chownr": "^2.0.0",
@@ -29765,7 +29751,9 @@
}
},
"url-parse": {
- "version": "1.5.1",
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
+ "integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
diff --git a/package.json b/package.json
index f7d903ad59..f9fa8dd9a6 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"@angular/router": "^11.2.9",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^3.0.0",
- "@dasch-swiss/dsp-ui": "^1.7.4",
+ "@dasch-swiss/dsp-ui": "^1.7.5",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "5.0.0",
"3d-force-graph": "^1.60.12",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index a9e0dcbf83..6969afeda0 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -97,6 +97,7 @@ import { ResourceComponent } from './workspace/resource/resource.component';
import { ResultsComponent } from './workspace/results/results.component';
import { AudioComponent } from './workspace/resource/representation/audio/audio.component';
import { IntermediateComponent } from './workspace/intermediate/intermediate.component';
+import { ResourceLinkFormComponent } from './workspace/resource/resource-link-form/resource-link-form.component';
// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
@@ -180,6 +181,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
VisualizerComponent,
AudioComponent,
IntermediateComponent,
+ ResourceLinkFormComponent,
],
imports: [
AppRoutingModule,
diff --git a/src/app/main/dialog/dialog.component.html b/src/app/main/dialog/dialog.component.html
index 3106899217..8e496466c1 100644
--- a/src/app/main/dialog/dialog.component.html
+++ b/src/app/main/dialog/dialog.component.html
@@ -387,6 +387,13 @@
+
+
diff --git a/src/app/main/dialog/dialog.component.ts b/src/app/main/dialog/dialog.component.ts
index 1dd784b289..a89926f6d7 100644
--- a/src/app/main/dialog/dialog.component.ts
+++ b/src/app/main/dialog/dialog.component.ts
@@ -1,5 +1,6 @@
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FilteredResources } from '@dasch-swiss/dsp-ui';
import { PropertyInfoObject } from 'src/app/project/ontology/default-data/default-properties';
export interface DialogData {
@@ -16,6 +17,7 @@ export interface DialogData {
position?: number;
parentIri?: string;
projectCode?: string;
+ selectedResources?: FilteredResources;
}
export interface ConfirmationWithComment {
diff --git a/src/app/workspace/intermediate/intermediate.component.html b/src/app/workspace/intermediate/intermediate.component.html
index 41c3e6293f..1a17dd3fc6 100644
--- a/src/app/workspace/intermediate/intermediate.component.html
+++ b/src/app/workspace/intermediate/intermediate.component.html
@@ -9,26 +9,15 @@
-
-
-
-
+
-
+
compare_arrows
diff --git a/src/app/workspace/intermediate/intermediate.component.spec.ts b/src/app/workspace/intermediate/intermediate.component.spec.ts
index f1a9dd8f04..5ec766c9e6 100644
--- a/src/app/workspace/intermediate/intermediate.component.spec.ts
+++ b/src/app/workspace/intermediate/intermediate.component.spec.ts
@@ -1,10 +1,13 @@
import { Component, DebugElement, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { By } from '@angular/platform-browser';
-import { FilteredResouces } from '@dasch-swiss/dsp-ui';
+import { KnoraApiConnection } from '@dasch-swiss/dsp-js';
+import { AppInitService, DspActionModule, DspApiConfigToken, DspApiConnectionToken, FilteredResources } from '@dasch-swiss/dsp-ui';
+import { TestConfig } from 'test.config';
import { IntermediateComponent } from './intermediate.component';
/**
@@ -17,12 +20,13 @@ class OneSelectedResourcesComponent {
@ViewChild('intermediateView') intermediateComponent: IntermediateComponent;
- resources: FilteredResouces = {
+ resources: FilteredResources = {
'count': 1,
'resListIndex': [1],
- 'resIds': [
- 'http://rdfh.ch/0803/83616f8d8501'
- ],
+ 'resInfo': [{
+ 'id': 'http://rdfh.ch/0803/83616f8d8501',
+ 'label': '65r'
+ }],
'selectionType': 'multiple'
};
@@ -40,13 +44,22 @@ class ThreeSelectedResourcesComponent {
@ViewChild('intermediateView') intermediateComponent: IntermediateComponent;
- resources: FilteredResouces = {
+ resources: FilteredResources = {
'count': 3,
'resListIndex': [3, 2, 1],
- 'resIds': [
- 'http://rdfh.ch/0803/83616f8d8501',
- 'http://rdfh.ch/0803/71e0b9958a01',
- 'http://rdfh.ch/0803/683d5cd26f01'
+ 'resInfo': [
+ {
+ 'id': 'http://rdfh.ch/0803/83616f8d8501',
+ 'label': '65r'
+ },
+ {
+ 'id': 'http://rdfh.ch/0803/71e0b9958a01',
+ 'label': '76r'
+ },
+ {
+ 'id': 'http://rdfh.ch/0803/683d5cd26f01',
+ 'label': '17v'
+ },
],
'selectionType': 'multiple'
};
@@ -71,12 +84,24 @@ describe('IntermediateComponent', () => {
ThreeSelectedResourcesComponent
],
imports: [
+ DspActionModule,
MatButtonModule,
+ MatDialogModule,
MatIconModule,
MatTooltipModule
+ ],
+ providers: [
+ AppInitService,
+ {
+ provide: DspApiConfigToken,
+ useValue: TestConfig.ApiConfig
+ },
+ {
+ provide: DspApiConnectionToken,
+ useValue: new KnoraApiConnection(TestConfig.ApiConfig)
+ }
]
- })
- .compileComponents();
+ }).compileComponents();
});
beforeEach(() => {
diff --git a/src/app/workspace/intermediate/intermediate.component.ts b/src/app/workspace/intermediate/intermediate.component.ts
index 195b6ad9d7..1cf66b28b4 100644
--- a/src/app/workspace/intermediate/intermediate.component.ts
+++ b/src/app/workspace/intermediate/intermediate.component.ts
@@ -1,5 +1,8 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
-import { FilteredResouces } from '@dasch-swiss/dsp-ui';
+import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
+import { FilteredResources } from '@dasch-swiss/dsp-ui';
+import { DialogComponent } from 'src/app/main/dialog/dialog.component';
+import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
@Component({
selector: 'app-intermediate',
@@ -8,7 +11,7 @@ import { FilteredResouces } from '@dasch-swiss/dsp-ui';
})
export class IntermediateComponent implements OnInit {
- @Input() resources: FilteredResouces;
+ @Input() resources: FilteredResources;
@Output() action: EventEmitter
= new EventEmitter();
@@ -20,8 +23,40 @@ export class IntermediateComponent implements OnInit {
}
};
- constructor() { }
+ constructor(
+ private _dialog: MatDialog,
+ private _errorHandler: ErrorHandlerService,
+ ) { }
ngOnInit(): void { }
+ /**
+ * opens the dialog box with a form to create a link resource, to edit resources etc.
+ * @param type 'link' --> TODO: will be expanded with other types like edit, delete etc.
+ * @param data
+ */
+ openDialog(type: 'link', data: FilteredResources) {
+
+ const title = 'Create a collection of ' + data.count + ' resources';
+
+ const dialogConfig: MatDialogConfig = {
+ width: '640px',
+ maxHeight: '80vh',
+ position: {
+ top: '112px'
+ },
+ data: { mode: type + 'Resources', title: title, selectedResources: data }
+ };
+
+ const dialogRef = this._dialog.open(
+ DialogComponent,
+ dialogConfig
+ );
+
+ dialogRef.afterClosed().subscribe((resId: string) => {
+
+ // do something with the intermediate view... but what should we do / display? Maybe the new resource...
+ });
+ }
+
}
diff --git a/src/app/workspace/resource/project.service.spec.ts b/src/app/workspace/resource/project.service.spec.ts
new file mode 100644
index 0000000000..ca524ee8ed
--- /dev/null
+++ b/src/app/workspace/resource/project.service.spec.ts
@@ -0,0 +1,41 @@
+import { TestBed } from '@angular/core/testing';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { DspActionModule, DspApiConnectionToken, DspCoreModule } from '@dasch-swiss/dsp-ui';
+import { ProjectService } from './project.service';
+
+
+describe('ProjectService', () => {
+ let service: ProjectService;
+
+ beforeEach(() => {
+
+ const apiEndpointSpyObj = {
+ v2: {
+ auth: jasmine.createSpyObj('auth', ['logout'])
+ }
+ };
+
+ TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ DspActionModule,
+ DspCoreModule,
+ MatDialogModule,
+ MatSnackBarModule
+ ],
+ providers: [
+ {
+ provide: DspApiConnectionToken,
+ useValue: apiEndpointSpyObj
+ },
+ ]
+ });
+ service = TestBed.inject(ProjectService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/workspace/resource/project.service.ts b/src/app/workspace/resource/project.service.ts
new file mode 100644
index 0000000000..75993b872c
--- /dev/null
+++ b/src/app/workspace/resource/project.service.ts
@@ -0,0 +1,65 @@
+import { Inject, Injectable } from '@angular/core';
+import { ApiResponseData, ApiResponseError, Constants, KnoraApiConnection, ProjectsResponse, StoredProject, UserResponse } from '@dasch-swiss/dsp-js';
+import { DspApiConnectionToken, SessionService } from '@dasch-swiss/dsp-ui';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { CacheService } from 'src/app/main/cache/cache.service';
+import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ProjectService {
+
+ constructor(
+ @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
+ private _cache: CacheService,
+ private _errorHandler: ErrorHandlerService,
+ private _session: SessionService
+ ) { }
+
+ /**
+ * initializes projects
+ * @returns projects
+ */
+ initializeProjects(): Observable {
+ const usersProjects: StoredProject[] = [];
+
+ // get info about logged-in user from the session object
+ const session = this._session.getSession();
+
+ if (session.user.sysAdmin === false) {
+ return this._cache.get(session.user.name, this._dspApiConnection.admin.usersEndpoint.getUserByUsername(session.user.name)).pipe(
+ map((response: ApiResponseData) => {
+
+ for (const project of response.body.user.projects) {
+ if (project.status) {
+ usersProjects.push(project);
+ }
+ }
+ return usersProjects;
+ },
+ (error: ApiResponseError) => {
+ this._errorHandler.showMessage(error);
+ return [];
+ })
+ );
+ } else {
+ return this._dspApiConnection.admin.projectsEndpoint.getProjects().pipe(
+ map((response: ApiResponseData) => {
+ for (const project of response.body.projects) {
+ if (project.status && project.id !== Constants.SystemProjectIRI && project.id !== Constants.DefaultSharedOntologyIRI) {
+ usersProjects.push(project);
+ }
+ }
+ return usersProjects;
+ },
+ (error: ApiResponseError) => {
+ this._errorHandler.showMessage(error);
+ return [];
+ })
+ );
+ }
+
+ }
+}
diff --git a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.spec.ts b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.spec.ts
index 09dd2c7578..5f391254ab 100644
--- a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.spec.ts
+++ b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.spec.ts
@@ -35,13 +35,19 @@ import {
UsersEndpointAdmin
} from '@dasch-swiss/dsp-js';
import { OntologyCache } from '@dasch-swiss/dsp-js/src/cache/ontology-cache/OntologyCache';
-import { DspActionModule, DspApiConnectionToken, IntValueComponent, Session, SessionService, ValueService } from '@dasch-swiss/dsp-ui';
+import {
+ DspActionModule,
+ DspApiConnectionToken,
+ IntValueComponent,
+ Session,
+ SessionService,
+ ValueService
+} from '@dasch-swiss/dsp-ui';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { AjaxResponse } from 'rxjs/ajax';
import { CacheService } from 'src/app/main/cache/cache.service';
import { BaseValueDirective } from 'src/app/main/directive/base-value.directive';
-import { ResourceComponent } from '../resource.component';
import { ResourceInstanceFormComponent } from './resource-instance-form.component';
import { SwitchPropertiesComponent } from './select-properties/switch-properties/switch-properties.component';
@@ -252,7 +258,7 @@ describe('ResourceInstanceFormComponent', () => {
const cacheServiceSpy = jasmine.createSpyObj('CacheService', ['get']);
- const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'navigateByUrl']);
+ // const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'navigateByUrl']);
TestBed.configureTestingModule({
declarations: [
@@ -275,9 +281,7 @@ describe('ResourceInstanceFormComponent', () => {
MatSelectModule,
MatSnackBarModule,
ReactiveFormsModule,
- RouterTestingModule.withRoutes([
- { path: 'resource', component: ResourceComponent }
- ]),
+ RouterTestingModule,
TranslateModule.forRoot()
],
providers: [
@@ -341,11 +345,6 @@ describe('ResourceInstanceFormComponent', () => {
}
);
- // const routerSpy = TestBed.inject(Router);
-
- // (routerSpy as jasmine.SpyObj).navigate.and.stub();
- // (routerSpy as jasmine.SpyObj).navigateByUrl.and.stub();
-
testHostFixture = TestBed.createComponent(TestHostComponent);
testHostComponent = testHostFixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(testHostFixture);
diff --git a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts
index 59a0aea139..10e418c007 100644
--- a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts
+++ b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts
@@ -2,31 +2,25 @@ import { Component, EventEmitter, Inject, OnDestroy, OnInit, Output, ViewChild }
import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import {
- ApiResponseData,
ApiResponseError,
Constants,
CreateFileValue,
CreateResource,
CreateValue,
KnoraApiConnection,
- OntologiesMetadata,
- ProjectsResponse,
- ReadOntology,
+ OntologiesMetadata, ReadOntology,
ReadResource,
ResourceClassAndPropertyDefinitions,
ResourceClassDefinition,
ResourcePropertyDefinition,
- StoredProject,
- UserResponse
+ StoredProject
} from '@dasch-swiss/dsp-js';
import {
- DspApiConnectionToken,
- Session,
- SessionService
+ DspApiConnectionToken
} from '@dasch-swiss/dsp-ui';
import { Subscription } from 'rxjs';
-import { CacheService } from 'src/app/main/cache/cache.service';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
+import { ProjectService } from '../project.service';
import { SelectOntologyComponent } from './select-ontology/select-ontology.component';
import { SelectPropertiesComponent } from './select-properties/select-properties.component';
import { SelectResourceClassComponent } from './select-resource-class/select-resource-class.component';
@@ -57,9 +51,6 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy {
// form validation status
formValid = false;
- session: Session;
- username: string;
-
showNextStepForm: boolean;
usersProjects: StoredProject[];
@@ -86,15 +77,11 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy {
constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
- private _cache: CacheService,
private _errorHandler: ErrorHandlerService,
private _fb: FormBuilder,
- private _router: Router,
- private _session: SessionService
- ) {
- this.session = this._session.getSession();
- this.username = this.session.user.name;
- }
+ private _project: ProjectService,
+ private _router: Router
+ ) { }
ngOnInit(): void {
@@ -104,7 +91,16 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy {
this.propertiesParentForm = this._fb.group({});
// initialize projects to be used for the project selection in the creation form
- this.initializeProjects();
+ this._project.initializeProjects().subscribe(
+ (proj: StoredProject[]) => {
+ this.usersProjects = proj;
+
+ // notifies the user that he/she is not part of any project
+ if (proj.length === 0) {
+ this.errorMessage = 'You are not a part of any active projects or something went wrong';
+ }
+ }
+ );
// boolean to show only the first step of the form (= selectResourceForm)
this.showNextStepForm = true;
@@ -185,7 +181,7 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy {
this.resource = res;
const goto = '/resource/' + encodeURIComponent(this.resource.id);
- this._router.navigateByUrl(goto, { skipLocationChange: false });
+ this._router.navigate([]).then(result => window.open(goto, '_blank'));
this.closeDialog.emit();
},
@@ -199,48 +195,6 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy {
}
}
- /**
- * get the user's project(s)
- */
- initializeProjects(): void {
- this.usersProjects = [];
-
- if (this.username && this.session.user.sysAdmin === false) {
- this._cache.get(this.username, this._dspApiConnection.admin.usersEndpoint.getUserByUsername(this.username)).subscribe(
- (response: ApiResponseData) => {
-
- for (const project of response.body.user.projects) {
- if (project.status) {
- this.usersProjects.push(project);
- }
- }
-
- // notifies the user that he/she is not part of any project
- if (this.usersProjects.length === 0) {
- this.errorMessage = 'You are not a part of any active projects.';
- }
- },
- (error: ApiResponseError) => {
- this._errorHandler.showMessage(error);
- }
- );
- } else if (this.session.user.sysAdmin === true) {
- this._dspApiConnection.admin.projectsEndpoint.getProjects().subscribe(
- (response: ApiResponseData) => {
- for (const project of response.body.projects) {
- if (project.status && project.id !== Constants.SystemProjectIRI && project.id !== Constants.DefaultSharedOntologyIRI) {
- this.usersProjects.push(project);
- }
- }
- },
- (error: ApiResponseError) => {
- this._errorHandler.showMessage(error);
- }
- );
- }
-
- }
-
/**
* get all the ontologies of the selected project
* @param projectIri
diff --git a/src/app/workspace/resource/resource-instance-form/select-project/select-project.component.ts b/src/app/workspace/resource/resource-instance-form/select-project/select-project.component.ts
index 4ec0f4c1c0..458e3be1bc 100644
--- a/src/app/workspace/resource/resource-instance-form/select-project/select-project.component.ts
+++ b/src/app/workspace/resource/resource-instance-form/select-project/select-project.component.ts
@@ -9,8 +9,7 @@ import {
Output
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { KnoraApiConnection, StoredProject } from '@dasch-swiss/dsp-js';
-import { DspApiConnectionToken } from '@dasch-swiss/dsp-ui';
+import { StoredProject } from '@dasch-swiss/dsp-js';
import { Subscription } from 'rxjs';
const resolvedPromise = Promise.resolve(null);
diff --git a/src/app/workspace/resource/resource-link-form/resource-link-form.component.html b/src/app/workspace/resource/resource-link-form/resource-link-form.component.html
new file mode 100644
index 0000000000..37ea351862
--- /dev/null
+++ b/src/app/workspace/resource/resource-link-form/resource-link-form.component.html
@@ -0,0 +1,56 @@
+
+
+
+ You have to be a member in at least one project to link the selected resources.
+
diff --git a/src/app/workspace/resource/resource-link-form/resource-link-form.component.scss b/src/app/workspace/resource/resource-link-form/resource-link-form.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/app/workspace/resource/resource-link-form/resource-link-form.component.spec.ts b/src/app/workspace/resource/resource-link-form/resource-link-form.component.spec.ts
new file mode 100644
index 0000000000..d830de8fd0
--- /dev/null
+++ b/src/app/workspace/resource/resource-link-form/resource-link-form.component.spec.ts
@@ -0,0 +1,279 @@
+import { Component, DebugElement, EventEmitter, Inject, Input, OnInit, Output, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { By } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterTestingModule } from '@angular/router/testing';
+import {
+ ApiResponseData,
+ Constants,
+ CreateLinkValue,
+ CreateResource,
+ MockProjects,
+ MockResource,
+ MockUsers,
+ ReadResource, ResourcesEndpointV2,
+ StoredProject,
+ UserResponse,
+ UsersEndpointAdmin
+} from '@dasch-swiss/dsp-js';
+import {
+ DspActionModule,
+ DspApiConnectionToken,
+ FilteredResources,
+ Session,
+ SessionService
+} from '@dasch-swiss/dsp-ui';
+import { TranslateModule } from '@ngx-translate/core';
+import { of } from 'rxjs';
+import { AjaxResponse } from 'rxjs/ajax';
+import { CacheService } from 'src/app/main/cache/cache.service';
+import { ResourceLinkFormComponent } from './resource-link-form.component';
+
+const resolvedPromise = Promise.resolve(null);
+
+/**
+ * test host component to simulate parent component.
+ */
+@Component({
+ template: `
+ `
+})
+class TestHostComponent implements OnInit {
+
+ @ViewChild('resourceLinkFormComp') resourceLinkFormComponent: ResourceLinkFormComponent;
+
+ resources: FilteredResources = {
+ 'count': 3,
+ 'resListIndex': [3, 2, 1],
+ 'resInfo': [
+ {
+ 'id': 'http://rdfh.ch/0803/83616f8d8501',
+ 'label': '65r'
+ },
+ {
+ 'id': 'http://rdfh.ch/0803/71e0b9958a01',
+ 'label': '76r'
+ },
+ {
+ 'id': 'http://rdfh.ch/0803/683d5cd26f01',
+ 'label': '17v'
+ },
+ ],
+ 'selectionType': 'multiple'
+ };
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
+
+/**
+ * mock select-project component to use in tests.
+ */
+@Component({
+ selector: 'app-select-project'
+})
+class MockSelectProjectComponent implements OnInit {
+ @Input() formGroup: FormGroup;
+ @Input() usersProjects: StoredProject[];
+ @Output() projectSelected = new EventEmitter();
+
+ form: FormGroup;
+
+ constructor(@Inject(FormBuilder) private _fb: FormBuilder) { }
+
+ ngOnInit() {
+ this.form = this._fb.group({
+ projects: [null, Validators.required]
+ });
+
+ resolvedPromise.then(() => {
+ // add form to the parent form group
+ this.formGroup.addControl('projects', this.form);
+ });
+ }
+}
+
+describe('ResourceLinkFormComponent', () => {
+ let testHostComponent: TestHostComponent;
+ let testHostFixture: ComponentFixture;
+ let resourceLinkFormComponentDe: DebugElement;
+
+ beforeEach(waitForAsync(() => {
+ const dspConnSpy = {
+ admin: {
+ usersEndpoint: jasmine.createSpyObj('usersEndpoint', ['getUserByUsername'])
+ },
+ v2: {
+ onto: jasmine.createSpyObj('onto', ['getOntologiesByProjectIri']),
+ ontologyCache: jasmine.createSpyObj('ontologyCache', ['getOntology', 'getResourceClassDefinition']),
+ res: jasmine.createSpyObj('res', ['createResource'])
+ }
+ };
+
+ const sessionServiceSpy = jasmine.createSpyObj('SessionService', ['getSession']);
+
+ const cacheServiceSpy = jasmine.createSpyObj('CacheService', ['get']);
+
+ TestBed.configureTestingModule({
+ declarations: [
+ ResourceLinkFormComponent,
+ TestHostComponent,
+ MockSelectProjectComponent
+ ],
+ imports: [
+ BrowserAnimationsModule,
+ DspActionModule,
+ MatButtonModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatInputModule,
+ MatSelectModule,
+ MatSnackBarModule,
+ MatTooltipModule,
+ ReactiveFormsModule,
+ RouterTestingModule,
+ TranslateModule.forRoot()
+ ],
+ providers: [
+ {
+ provide: DspApiConnectionToken,
+ useValue: dspConnSpy
+ },
+ {
+ provide: SessionService,
+ useValue: sessionServiceSpy
+ },
+ {
+ provide: CacheService,
+ useValue: cacheServiceSpy
+ }
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+
+ const sessionSpy = TestBed.inject(SessionService);
+
+ (sessionSpy as jasmine.SpyObj).getSession.and.callFake(
+ () => {
+ const session: Session = {
+ id: 12345,
+ user: {
+ name: 'username',
+ jwt: 'myToken',
+ lang: 'en',
+ sysAdmin: false,
+ projectAdmin: []
+ }
+ };
+
+ return session;
+ }
+ );
+
+ const cacheSpy = TestBed.inject(CacheService);
+
+ (cacheSpy as jasmine.SpyObj).get.and.callFake(
+ () => {
+ const response: UserResponse = new UserResponse();
+
+ const project = MockProjects.mockProject();
+
+ response.user.projects = new Array();
+
+ response.user.projects.push(project.body.project);
+
+ return of(ApiResponseData.fromAjaxResponse({ response } as AjaxResponse));
+ }
+ );
+
+ testHostFixture = TestBed.createComponent(TestHostComponent);
+ testHostComponent = testHostFixture.componentInstance;
+ testHostFixture.detectChanges();
+
+ const dspConnSpy = TestBed.inject(DspApiConnectionToken);
+
+ (dspConnSpy.admin.usersEndpoint as jasmine.SpyObj).getUserByUsername.and.callFake(
+ () => {
+ const loggedInUser = MockUsers.mockUser();
+ return of(loggedInUser);
+ }
+ );
+
+ const hostCompDe = testHostFixture.debugElement;
+
+ resourceLinkFormComponentDe = hostCompDe.query(By.directive(ResourceLinkFormComponent));
+
+ });
+
+
+ it('should initialize the usersProjects array', () => {
+ expect(testHostComponent.resourceLinkFormComponent.usersProjects.length).toEqual(1);
+ });
+
+
+ it('should show the select project component', () => {
+
+ const comp = resourceLinkFormComponentDe.query(By.directive(MockSelectProjectComponent));
+
+ expect((comp.componentInstance as MockSelectProjectComponent).usersProjects.length).toEqual(1);
+ });
+
+ it('should submit the form', () => {
+ const dspConnSpy = TestBed.inject(DspApiConnectionToken);
+
+ (dspConnSpy.v2.res as jasmine.SpyObj).createResource.and.callFake(
+ () => {
+ let resource = new ReadResource();
+
+ MockResource.getTestThing().subscribe((res) => {
+ resource = res;
+ });
+
+ return of(resource);
+ }
+ );
+
+ testHostComponent.resourceLinkFormComponent.form.controls['label'].setValue('My Label');
+
+ testHostFixture.detectChanges();
+
+ const props = {};
+ const createVal: CreateLinkValue[] = [];
+ // res 1
+ testHostComponent.resources.resInfo.forEach(res => {
+ const linkVal = new CreateLinkValue();
+ linkVal.linkedResourceIri = res.id;
+ linkVal.type = Constants.LinkValue;
+ createVal.push(linkVal);
+ });
+
+ props[Constants.KnoraApiV2 + Constants.HashDelimiter + 'hasLinkToValue'] = createVal;
+
+ const expectedCreateResource = new CreateResource();
+ expectedCreateResource.label = 'My Label';
+ expectedCreateResource.type = Constants.KnoraApiV2 + Constants.HashDelimiter + 'LinkObj';
+ expectedCreateResource.properties = props;
+
+ // --> TODO create a Router spy to mock the navigation
+ testHostComponent.resourceLinkFormComponent.submitData();
+
+ expect(dspConnSpy.v2.res.createResource).toHaveBeenCalledTimes(1);
+
+
+ });
+
+});
diff --git a/src/app/workspace/resource/resource-link-form/resource-link-form.component.ts b/src/app/workspace/resource/resource-link-form/resource-link-form.component.ts
new file mode 100644
index 0000000000..b598ff4a18
--- /dev/null
+++ b/src/app/workspace/resource/resource-link-form/resource-link-form.component.ts
@@ -0,0 +1,164 @@
+import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
+import {
+ ApiResponseError,
+ Constants,
+ CreateLinkValue,
+ CreateResource,
+ CreateTextValueAsString,
+ KnoraApiConnection,
+ ReadResource,
+ StoredProject
+} from '@dasch-swiss/dsp-js';
+import { DspApiConnectionToken, FilteredResources } from '@dasch-swiss/dsp-ui';
+import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
+import { ProjectService } from '../project.service';
+
+@Component({
+ selector: 'app-resource-link-form',
+ templateUrl: './resource-link-form.component.html',
+ styleUrls: ['./resource-link-form.component.scss']
+})
+export class ResourceLinkFormComponent implements OnInit {
+
+ @Input() resources: FilteredResources;
+
+ @Output() closeDialog: EventEmitter = new EventEmitter();
+
+ /**
+ * form group, errors and validation messages
+ */
+ form: FormGroup;
+
+ formErrors = {
+ 'label': ''
+ };
+
+ validationMessages = {
+ 'label': {
+ 'required': 'A label is required.'
+ }
+ };
+
+ usersProjects: StoredProject[];
+
+ selectedProject: string;
+
+ error = false;
+ loading = false;
+
+ constructor(
+ @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
+ private _errorHandler: ErrorHandlerService,
+ private _fb: FormBuilder,
+ private _project: ProjectService,
+ private _router: Router
+ ) { }
+
+ ngOnInit(): void {
+
+ // initialize projects to be used for the project selection in the creation form
+ this._project.initializeProjects().subscribe(
+ (proj: StoredProject[]) => {
+ this.usersProjects = proj;
+ }
+ );
+
+ this.form = this._fb.group({
+ 'label': new FormControl({
+ value: '', disabled: false
+ }, [
+ Validators.required
+ ]),
+ 'comment': new FormControl(),
+ 'project': new FormControl()
+ });
+
+ this.form.valueChanges
+ .subscribe(data => this.onValueChanged(data));
+ }
+
+ /**
+ * this method is for the form error handling
+ *
+ * @param data Data which changed.
+ */
+ onValueChanged(data?: any) {
+
+ if (!this.form) {
+ return;
+ }
+
+ const form = this.form;
+
+ Object.keys(this.formErrors).map(field => {
+ this.formErrors[field] = '';
+ const control = form.get(field);
+ if (control && control.dirty && !control.valid) {
+ const messages = this.validationMessages[field];
+ Object.keys(control.errors).map(key => {
+ this.formErrors[field] += messages[key] + ' ';
+ });
+
+ }
+ });
+ }
+
+ /**
+ * submits the data
+ */
+ submitData() {
+
+ this.loading = true;
+
+ // build link resource as type CreateResource
+ const linkObj = new CreateResource();
+
+ linkObj.label = this.form.controls['label'].value;
+
+ linkObj.type = Constants.KnoraApiV2 + Constants.HashDelimiter + 'LinkObj';
+
+ linkObj.attachedToProject = this.selectedProject;
+
+ const hasLinkToValue = [];
+
+ this.resources.resInfo.forEach(res => {
+ const linkVal = new CreateLinkValue();
+ linkVal.type = Constants.LinkValue;
+ linkVal.linkedResourceIri = res.id;
+ hasLinkToValue.push(linkVal);
+ });
+
+ const comment = this.form.controls['comment'].value;
+ if (comment) {
+ const commentVal = new CreateTextValueAsString();
+ commentVal.type = Constants.TextValue;
+ commentVal.text = comment;
+ linkObj.properties = {
+ [Constants.KnoraApiV2 + Constants.HashDelimiter + 'hasLinkToValue']: hasLinkToValue,
+ [Constants.KnoraApiV2 + Constants.HashDelimiter + 'hasComment']: [commentVal],
+ };
+ } else {
+ linkObj.properties = {
+ [Constants.KnoraApiV2 + Constants.HashDelimiter + 'hasLinkToValue']: hasLinkToValue,
+ };
+ }
+
+ this._dspApiConnection.v2.res.createResource(linkObj).subscribe(
+ (res: ReadResource) => {
+ // --> TODO: do something with the successful response
+ const goto = '/resource/' + encodeURIComponent(res.id);
+ this._router.navigate([]).then(result => window.open(goto, '_blank'));
+ this.closeDialog.emit();
+ this.loading = false;
+ },
+ (error: ApiResponseError) => {
+ this._errorHandler.showMessage(error);
+ this.error = true;
+ this.loading = false;
+ }
+ );
+
+ }
+}
diff --git a/src/app/workspace/results/results.component.html b/src/app/workspace/results/results.component.html
index 1d8d66dc90..d81152f757 100644
--- a/src/app/workspace/results/results.component.html
+++ b/src/app/workspace/results/results.component.html
@@ -4,22 +4,20 @@
+ (selectedResources)="openSelectedResources($event)">
-
-
+ 0">
diff --git a/src/app/workspace/results/results.component.ts b/src/app/workspace/results/results.component.ts
index 933d053472..bf2c784de3 100644
--- a/src/app/workspace/results/results.component.ts
+++ b/src/app/workspace/results/results.component.ts
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Params } from '@angular/router';
-import { FilteredResouces, SearchParams } from '@dasch-swiss/dsp-ui';
+import { FilteredResources, SearchParams } from '@dasch-swiss/dsp-ui';
@Component({
selector: 'app-results',
@@ -17,16 +17,17 @@ export class ResultsComponent {
resourceIri: string;
// display single resource or intermediate page in case of multiple selection
- multipleResources: FilteredResouces;
viewMode: 'single' | 'intermediate' | 'compare' = 'single';
- // number of all results
- numberOfAllResults: number;
+ // which resources are selected?
+ selectedResources: FilteredResources;
// search params
searchQuery: string;
searchMode: 'fulltext' | 'gravsearch';
+ loading = true;
+
constructor(
private _route: ActivatedRoute,
private _titleService: Title
@@ -53,24 +54,18 @@ export class ResultsComponent {
this._titleService.setTitle('Search results for ' + this.searchParams.mode + ' search');
}
- openResource(id: string) {
- this.viewMode = 'single';
- this.multipleResources = undefined;
- this.resourceIri = id;
- }
-
- // this funtion is called when 'withMultipleSelection' is true and
- // multiple resources are selected for comparision
- openMultipleResources(resources: FilteredResouces) {
+ openSelectedResources(res: FilteredResources) {
- if (this.viewMode !== 'compare') {
+ this.selectedResources = res;
- this.viewMode = ((resources && resources.count > 0) ? 'intermediate' : 'single');
-
- this.multipleResources = (this.viewMode !== 'single' ? resources : undefined);
+ if (!res || res.count <= 1) {
+ this.viewMode = 'single';
+ } else {
+ if (this.viewMode !== 'compare') {
+ this.viewMode = ((res && res.count > 0) ? 'intermediate' : 'single');
+ }
}
-
}
}