diff --git a/package.json b/package.json index 8d3526dc..2f85f5ea 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@firebase/app-types": "^0.1.2", "@ngx-translate/core": "^8.0.0", "@ngx-translate/http-loader": "^2.0.0", + "angular-split": "^1.0.0-rc.3", "angular-tree-component": "^6.1.0", "angularfire2": "^5.0.0-rc.6", "core-js": "^2.5.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f57aa807..4b95780f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,6 +21,7 @@ import {AppMaterialModule} from './app-material-module'; import 'hammerjs'; // // import other third party modules +import { AngularSplitModule } from 'angular-split'; // import {DndModule} from 'ng2-dnd'; import {ApiService} from './model/services/api.service'; @@ -317,6 +318,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) { AngularFireModule.initializeApp(environment.firebase), InfiniteScrollModule, RecaptchaModule.forRoot(), + AngularSplitModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, diff --git a/src/app/model/services/api.service.ts b/src/app/model/services/api.service.ts index 186d33c0..fa97db24 100644 --- a/src/app/model/services/api.service.ts +++ b/src/app/model/services/api.service.ts @@ -122,6 +122,28 @@ export class ApiService { }); } + httpGetBasicOnto(url: string, options?: RequestOptionsArgs): Observable { + + if (!options) options = {withCredentials: true}; + + url = (url.slice(0, 4) === 'http' ? url : environment.api + url); + + return this._http.get(url, options).map((response: Response) => { + try { + let apiServiceResult: ApiServiceResult = new ApiServiceResult(); + apiServiceResult.status = response.status; + apiServiceResult.statusText = response.statusText; + apiServiceResult.body = response.json(); + apiServiceResult.url = url; + return apiServiceResult; + } catch (e) { + return ApiService.handleError(response, url); + } + }).catch((error: any) => { + return Observable.throw(ApiService.handleError(error, url)); + }); + } + /** diff --git a/src/app/model/services/basic-ontology.service.ts b/src/app/model/services/basic-ontology.service.ts index 35b44961..b49567bb 100644 --- a/src/app/model/services/basic-ontology.service.ts +++ b/src/app/model/services/basic-ontology.service.ts @@ -27,9 +27,12 @@ export class BasicOntologyService extends ApiService { * * @returns {Observable} */ + // getBasicOntology(): Observable { + // let url = environment.url; + // return this.httpGet(url + '/data/base-data/basic-ontology.json', {withCredentials: false}); + // } getBasicOntology(): Observable { - let url = environment.url; - return this.httpGet(url + '/data/base-data/basic-ontology.json', {withCredentials: false}); + return this.httpGetBasicOnto('/ontology/knora-api/v2'); } } diff --git a/src/app/model/test-data/basic-ontology.ts b/src/app/model/test-data/basic-ontology.ts index 7bf93d8d..78b6b837 100644 --- a/src/app/model/test-data/basic-ontology.ts +++ b/src/app/model/test-data/basic-ontology.ts @@ -12,8 +12,8 @@ * License along with SALSAH. If not, see . * */ -import {JsonObject, JsonProperty} from 'json2typescript'; -import {PropertyItem} from "../webapi/knora/v1/properties/property-item"; +import { JsonObject, JsonProperty } from 'json2typescript'; +import { PropertyItem } from '../webapi/knora/v1/properties/property-item'; /** * has four default categories and four groups @@ -123,7 +123,7 @@ export class ResourceClass { export class BasicOntology { // defaultProperties - @JsonProperty('defaultProperties', [PropertyItem]) + @JsonProperty('defaultProperties', [PropertyItem], true) public defaultProperties: PropertyItem[] = undefined; // defaultPermissions @@ -131,7 +131,7 @@ export class BasicOntology { public defaultPermissions: Permissions = undefined; // defaultResourceClasses - @JsonProperty('resourceClasses', [ResourceClass]) + @JsonProperty('resourceClasses', [ResourceClass], true) public resourceClasses: ResourceClass[] = undefined; } diff --git a/src/app/model/webapi/knora/v1/properties/property-item.ts b/src/app/model/webapi/knora/v1/properties/property-item.ts index f2b5e2a6..07f1c83a 100644 --- a/src/app/model/webapi/knora/v1/properties/property-item.ts +++ b/src/app/model/webapi/knora/v1/properties/property-item.ts @@ -12,7 +12,7 @@ * License along with SALSAH. If not, see . * */ -import {JsonObject, JsonProperty} from "json2typescript"; +import { JsonObject, JsonProperty } from 'json2typescript'; @JsonObject export class PropertyItem { @@ -47,36 +47,36 @@ export class PropertyItem { @JsonProperty('gui_name', String, true) public gui_name: string = undefined; -/* - @JsonProperty('comments', [String], true) - public comments: string[] = undefined; + /* + @JsonProperty('comments', [String], true) + public comments: string[] = undefined; - @JsonProperty('is_annotation', String) - public is_annotation: string = undefined; + @JsonProperty('is_annotation', String) + public is_annotation: string = undefined; - @JsonProperty('locations', [Location], true) - public locations: Location[] = undefined; + @JsonProperty('locations', [Location], true) + public locations: Location[] = undefined; - @JsonProperty('regular_property', Number) - public regular_property: number = undefined; + @JsonProperty('regular_property', Number) + public regular_property: number = undefined; - @JsonProperty('value_firstprops', [String], true) - public value_firstprops: string[] = undefined; + @JsonProperty('value_firstprops', [String], true) + public value_firstprops: string[] = undefined; - @JsonProperty('value_iconsrcs', [String], true) - public value_iconsrcs: string[] = undefined; + @JsonProperty('value_iconsrcs', [String], true) + public value_iconsrcs: string[] = undefined; - @JsonProperty('value_ids', [String], true) - public value_ids: string[] = undefined; + @JsonProperty('value_ids', [String], true) + public value_ids: string[] = undefined; - @JsonProperty('value_restype', [String], true) - public value_restype: string[] = undefined; + @JsonProperty('value_restype', [String], true) + public value_restype: string[] = undefined; - @JsonProperty('value_rights', [Number], true) - public value_rights: Number[] = undefined; + @JsonProperty('value_rights', [Number], true) + public value_rights: Number[] = undefined; - @JsonProperty('values', [Object], true) - public values: any[] = undefined; -*/ + @JsonProperty('values', [Object], true) + public values: any[] = undefined; + */ } diff --git a/src/app/view/modules/dialog/form-dialog/form-dialog.component.ts b/src/app/view/modules/dialog/form-dialog/form-dialog.component.ts index acb472fe..68afc094 100644 --- a/src/app/view/modules/dialog/form-dialog/form-dialog.component.ts +++ b/src/app/view/modules/dialog/form-dialog/form-dialog.component.ts @@ -75,7 +75,7 @@ export class FormDialogComponent implements OnInit { toggleFullSize() { this.fullSize = (!this.fullSize); - if (this.fullSize) { + if (this.data.form == 'ontology') { this._dialogRef.updateSize('100vw', '100vh'); this._dialogRef.updatePosition(); } else { diff --git a/src/app/view/modules/form/edit-resource-class/edit-resource-class.component.html b/src/app/view/modules/form/edit-resource-class/edit-resource-class.component.html index 62f84f1e..cd779fd6 100644 --- a/src/app/view/modules/form/edit-resource-class/edit-resource-class.component.html +++ b/src/app/view/modules/form/edit-resource-class/edit-resource-class.component.html @@ -203,13 +203,16 @@
{{editResFormLabels.addProp.addLabel}}
add - + - close + + clear -
- - - -
- - - - - - - Ontology - - - Settings - object - - - - + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + + + Add Resource Classes + + + + + + + + + + + + {{type}} + + + + + + + + + + + + +
+ +

Current Resource Classes

+ + {{res.label}} + close + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + Add Properties + + + + + + + + + + + + + + + + + + {{guiItem}} + + + +
+
+ {{createOntologyLabels.addProp.occurrence}} +
+ + + {{cardinality}} + + +
Selected value: {{group.value}}
+ +
+ + + + +
+
+ +

Current Properties

+ + {{prop.label}} + close + + +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/src/app/view/modules/form/ontology-form/ontology-form.component.scss b/src/app/view/modules/form/ontology-form/ontology-form.component.scss index e69de29b..0a7423cd 100644 --- a/src/app/view/modules/form/ontology-form/ontology-form.component.scss +++ b/src/app/view/modules/form/ontology-form/ontology-form.component.scss @@ -0,0 +1,10 @@ +.mat-chip { + max-width: 200px; +} + +.indented{ + padding-left:80px; +} +.edit-button { + float: right; +} diff --git a/src/app/view/modules/form/ontology-form/ontology-form.component.ts b/src/app/view/modules/form/ontology-form/ontology-form.component.ts index e31c7ef3..a73265a9 100644 --- a/src/app/view/modules/form/ontology-form/ontology-form.component.ts +++ b/src/app/view/modules/form/ontology-form/ontology-form.component.ts @@ -12,7 +12,15 @@ * License along with SALSAH. If not, see . * */ -import {Component, Input, OnInit} from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { BasicOntology, PropertyObject, ResourceClass } from '../../../../model/test-data/basic-ontology'; +import { FormBuilder, FormControl, FormGroup, Validator, Validators } from '@angular/forms'; +import { ResourceTypeInfo } from '../../../../model/webapi/knora'; +import { PropertyItem } from '../../../../model/webapi/knora/v1/properties/property-item'; +import { AppConfig } from '../../../../app.config'; +import { ApiServiceResult } from '../../../../model/services/api-service-result'; +import { BasicOntologyService } from '../../../../model/services/basic-ontology.service'; +import { ApiServiceError } from '../../../../model/services/api-service-error'; @Component({ selector: 'salsah-ontology-form', @@ -32,13 +40,394 @@ export class OntologyFormComponent implements OnInit { // in this case, the project admin adds a new ontology to the project @Input() restrictedBy?: string = undefined; + isLoading: boolean = true; - constructor() { + direction: string = 'horizontal'; + + isDisabled: boolean = false; + + panelOpenState: boolean = false; + resPanelOpenState: boolean = false; + propPanelOpenState: boolean = false; + + useTransition: boolean = true; + areas = [ + {size: 40, order: 2, content: 'Resource Classes'}, + {size: 40, order: 3, content: 'Properties'}, + ] + + // result to send to the server + public ontologyResForm: FormGroup; // our model driven form + public ontologyPropForm: FormGroup; // our model driven form + + basicOntology: BasicOntology = new BasicOntology(); + errorMessage: any; + + public index: any = 0; + public rindex: any = 0; + public pindex: any = 0; + + public createOntologyLabels: any = { + label: 'Ontology label', + name: 'Ontology short name', + resource: { + label: 'Label', + name: 'Short name', + description: 'Description', + type: 'Type', + icon: 'Icon', + }, + addProp: { + select: 'Select a property', + label: 'Label', + name: 'Short name', + type: 'Type', + description: 'Description', + gui: 'GUI element', + attributes: 'GUI attributes', + id: 'Property ID', + occurrence: 'Occurrence', + valuetype_id: 'Value type ID', + vocabulary: 'Vocabulary', + icon: 'Icon' + }, + buttons: { + save: 'Save', + reset: 'Reset', + close: 'Close', + edit: 'Edit', + skip: 'Skip', + next: 'Next', + add: 'Add' + } + }; + + guiItems: string[] = [ + 'text', + 'richtext', + 'textarea', + 'number', + 'searchbox', + 'fileupload', + 'date', + 'radio', + 'hlist', + 'pulldown', + 'spinbox', + 'richtext', + 'checkbox', + 'interval', + 'colorpicker', + 'geometry' + ]; + + cardinalityList: string[] = [ + '1', + '1-n', + '0-1', + '0-n' + ]; + + shortnameMinLength: number = 3; + shortnameMaxLength: number = 16; + + // the following form fields would have an error check + formErrors = { + 'name': '', + 'labels': '', + 'resName': '', + 'resLabel': '', + 'resType': '', + 'resDescription': '', + 'propName': '', + 'propLabel': '', + 'propType': '', + 'propDescription': '', + 'propGUItype': '', + 'propCardinality': '' + }; + // ...with the following messages + validationMessages = { + 'name': { + 'required': 'Ontology short name is required', + 'minlength': 'Short name must be at least ' + this.shortnameMinLength + ' characters long.', + 'maxlength': 'Short name cannot be more than ' + this.shortnameMaxLength + ' characters long.', + }, + 'labels': { + 'required': 'Ontology label is required' + }, + 'resName': { + 'required': 'Resource name is required', + 'maxlength': 'Short name cannot be more than 4 characters long.', + }, + 'resLabel': { + 'required': 'Resource label is required' + }, + 'resType': { + 'required': 'Resource type is required' + }, + 'propName': { + 'required': 'Property name is required', + 'maxlength': 'Short name cannot be more than 4 characters long.', + }, + 'propLabel': { + 'required': 'Property label is required' + }, + 'propType': { + 'required': 'Resource type is required' + }, + 'propGUItype': { + 'required': 'Property GUI type name is required' + }, + 'propCardinality': { + 'required': 'Property cardinality is required' + }, + }; + + // newOntology: any; + public newOntology: any[] = [{ + name: '', + label: '' + }]; + public newResource: any[] = [{ + name: '', + label: '', + icon: '', + description: '' + }]; + public newProperty: any[] = [{ + name: '', + label: '', + description: '', + gui: '', + cardinality: '' + }]; + + resClassTypes: any[] = [ + 'Empty Resource', + 'Annotation', + 'AudioRepresentation', + 'DDDRepresentation', + 'DocumentRepresentation', + 'MovingImageRepresentation', + 'StillImageRepresentation', + 'TextRepresentation', + 'LinkObj', + 'Region', + ]; + + + constructor(private _fb: FormBuilder, + private _basicOntologyService: BasicOntologyService) { } ngOnInit() { this.isLoading = false; + this.buildOntologyResForm(); + this.buildOntologyPropForm(); + this.getBasicOntologyInfo(); + } + + buildOntologyResForm() { + this.ontologyResForm = this._fb.group({ + resName: new FormControl({ // this is the resource's short name + value: '', disabled: false + }, [ + // Validators.required, + // Validators.maxLength(4) + ]), + resLabel: new FormControl({ // this is the resource's label + value: '', disabled: false + }, [ + Validators.required, + ]), + resType: new FormControl({ + value: '', disabled: false + }, [ + // Validators.required + ]), + resIcon: new FormControl({ + value: '', disabled: false + }), + resDescription: new FormControl({ + value: '', disabled: false + }), + }); + + // validation messages + this.ontologyResForm.valueChanges + .subscribe(data => this.onValueChanged(this.ontologyResForm, data)); + } + + buildOntologyPropForm() { + this.ontologyPropForm = this._fb.group({ + propName: new FormControl({ // this is the property's short name + value: '', disabled: false + }, [ + // Validators.required, + // Validators.maxLength(4) + ]), + propLabel: new FormControl({ // this is the property's label + value: '', disabled: false + }, [ + Validators.required, + ]), + propType: new FormControl({ + value: '', disabled: false + }, [ + // Validators.required + ]), + propGUItype: new FormControl({ + value: '', disabled: false + }, [ + // Validators.required + ]), + propCardinality: new FormControl({ + value: '', disabled: false + }, [ + // Validators.required + ]), + propDescription: new FormControl({ + value: '', disabled: false + }), + }); + + // validation messages + this.ontologyPropForm.valueChanges + .subscribe(data => this.onValueChanged(this.ontologyPropForm, data)); + } + + // build form validation messages + onValueChanged(myForm: FormGroup, data?: any) { + if (!myForm) { + return; + } + // const form = this.ontologyForm; + + for (const field in this.formErrors) { + const control = myForm.get(field); + this.formErrors[field] = ''; + if (control && control.dirty && !control.valid) { + const messages = this.validationMessages[field]; + for (const key in control.errors) { + this.formErrors[field] += messages[key] + ' '; + } + } + + } + } + + getBasicOntologyInfo() { + this._basicOntologyService.getBasicOntology() + .subscribe( + (result: ApiServiceResult) => { + this.basicOntology = result.getBody(BasicOntology); + }, + (error: ApiServiceError) => { + this.errorMessage = error; + } + ); + // console.log(this.basicOntology); + } + + // Form action buttons + + resetNewRes() { + this.ontologyPropForm.reset(); + } + + saveNewRes() { + console.log('Your submitted resource is:', this.ontologyResForm.value); + const i = this.index; + // Assign the form values to newResource//////////// + this.newResource[i] = { + name: this.ontologyResForm.value.resName, + label: this.ontologyResForm.value.resLabel, + icon: this.ontologyResForm.value.resIcon, + description: this.ontologyResForm.value.resDescription + }; + //////////////////////////////////////////////////// + console.log('Your submitted resources are:', this.newResource); + this.ontologyResForm.reset(); + this.resPanelOpenState = false; + this.index++; + } + + resetNewProp() { + this.ontologyPropForm.reset(); } + saveNewProp() { + console.log('Your submitted property is:', this.ontologyPropForm.value); + const i = this.pindex; + //Assign the form values to newResource//////////// + this.newProperty[i] = { + name: this.ontologyPropForm.value.propName, + label: this.ontologyPropForm.value.propLabel, + description: this.ontologyPropForm.value.propDescription, + gui: this.ontologyPropForm.value.propGUItype, + cardinality: this.ontologyPropForm.value.propCardinality + }; + //////////////////////////////////////////////////// + console.log('Your submitted properties are:', this.newProperty); + this.ontologyPropForm.reset(); + this.propPanelOpenState = false; + this.pindex++; + } + + delProp(item: any) { + const index: number = this.newProperty.indexOf(item); + this.newProperty.splice(index, 1); + this.pindex--; + } + + delRes(item: any) { + const index: number = this.newResource.indexOf(item); + this.newResource.splice(index, 1); + this.index--; + } + + //////////Angular split-screen functions ///////////////////////////////////// + gutterClick(e: { gutterNum: number, sizes: Array }) { + if (e.gutterNum === 1) { + if (this.areas[0].size > 0) { + this.areas[1].size += this.areas[0].size; + this.areas[0].size = 0; + } + else if (this.areas[1].size > 25) { + this.areas[1].size -= 25; + this.areas[0].size = 25; + } + else { + this.areas[0].size = 25; + this.areas[1].size = 50; + this.areas[2].size = 25; + } + } + else if (e.gutterNum === 2) { + if (this.areas[2].size > 0) { + this.areas[1].size += this.areas[2].size; + this.areas[2].size = 0; + } + else if (this.areas[1].size > 25) { + this.areas[1].size -= 25; + this.areas[2].size = 25; + } + else { + this.areas[0].size = 25; + this.areas[1].size = 50; + this.areas[2].size = 25; + } + } + } + + dragEnd(e: { gutterNum: number, sizes: Array }) { + this.areas[0].size = e.sizes[0]; + this.areas[1].size = e.sizes[1]; + this.areas[2].size = e.sizes[2]; + } + + /////////////////////////////////////////////////////////////////////////////// + } diff --git a/src/app/view/modules/framework/framework-for-listings/framework-for-listings.component.ts b/src/app/view/modules/framework/framework-for-listings/framework-for-listings.component.ts index 184b4749..5ab3b146 100644 --- a/src/app/view/modules/framework/framework-for-listings/framework-for-listings.component.ts +++ b/src/app/view/modules/framework/framework-for-listings/framework-for-listings.component.ts @@ -237,7 +237,7 @@ export class FrameworkForListingsComponent implements OnInit, OnChanges, AfterVi } dialogRef.afterClosed().subscribe(() => { - console.log('afterClosed: ', this.list.restrictedBy); + // console.log('afterClosed: ', this.list.restrictedBy); this.getData(this.list.restrictedBy); }); diff --git a/yarn.lock b/yarn.lock index a416dd3f..3a10118e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -452,6 +452,12 @@ amqplib@^0.5.2: readable-stream "1.x >=1.1.9" safe-buffer "^5.0.1" +angular-split@^1.0.0-rc.3: + version "1.0.0-rc.3" + resolved "https://registry.yarnpkg.com/angular-split/-/angular-split-1.0.0-rc.3.tgz#eb63fd60d8fd94587d75c19524d90d8712f136e0" + dependencies: + tslib "^1.7.1" + angular-tree-component@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/angular-tree-component/-/angular-tree-component-6.1.0.tgz#9d9a6b28a6881c2072cd6306b55229579e894071"