/
resource-properties.component.ts
307 lines (272 loc) · 11.4 KB
/
resource-properties.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import {
Component, EventEmitter,
Inject,
Input,
OnDestroy,
OnInit,
Output,
ViewChild
} from '@angular/core';
import {
CardinalityUtil,
Constants,
DeleteValue,
KnoraApiConnection,
PermissionUtil,
ReadLinkValue,
ReadResource,
ReadResourceSequence,
ReadTextValueAsXml,
ReadValue,
ResourcePropertyDefinition,
SystemPropertyDefinition
} from '@dasch-swiss/dsp-js';
import {
AddedEventValue,
AddValueComponent,
DeletedEventValue,
DisplayEditComponent,
DspApiConnectionToken,
Events,
PropertyInfoValues,
UpdatedEventValues,
ValueOperationEventService,
ValueService
} from '@dasch-swiss/dsp-ui';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-resource-properties',
templateUrl: './resource-properties.component.html',
styleUrls: ['./resource-properties.component.scss']
})
export class ResourcePropertiesComponent implements OnInit, OnDestroy {
@ViewChild('displayEdit') displayEditComponent: DisplayEditComponent;
@ViewChild('addValue') addValueComponent: AddValueComponent;
/**
* parent resource
*
* @param (parentResource)
*/
@Input() parentResource: ReadResource;
/**
* array of property object with ontology class prop def, list of properties and corresponding values
*
* @param (propArray)
*/
@Input() propArray: PropertyInfoValues[];
/**
* array of system property object with list of system properties
*
* @param (systemPropArray)
*/
@Input() systemPropArray: SystemPropertyDefinition[];
/**
* show all properties, even if they don't have a value.
*
* @param (showAllProps)
*/
@Input() showAllProps = false;
@Output() referredResourceClicked: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();
@Output() referredResourceHovered: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();
addButtonIsVisible: boolean; // used to toggle add value button
addValueFormIsVisible: boolean; // used to toggle add value form field
propID: string; // used in template to show only the add value form of the corresponding value
valueOperationEventSubscriptions: Subscription[] = []; // array of ValueOperationEvent subscriptions
constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private _valueOperationEventService: ValueOperationEventService,
private _valueService: ValueService
) { }
ngOnInit() {
if (this.parentResource) {
// get user permissions
const allPermissions = PermissionUtil.allUserPermissions(
this.parentResource.userHasPermission as 'RV' | 'V' | 'M' | 'D' | 'CR'
);
// if user has modify permissions, set addButtonIsVisible to true so the user see's the add button
this.addButtonIsVisible = allPermissions.indexOf(PermissionUtil.Permissions.M) !== -1;
}
// listen for the AddValue event to be emitted and call hideAddValueForm()
// this._valueOperationEventService.on(Events.ValueAdded, () => this.hideAddValueForm())
this.valueOperationEventSubscriptions = [];
// subscribe to the ValueOperationEventService and listen for an event to be emitted
this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on(
Events.ValueAdded, (newValue: AddedEventValue) => {
if (newValue) {
this.addValueToResource(newValue.addedValue);
this.hideAddValueForm();
}
}));
this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on(
Events.ValueUpdated, (updatedValue: UpdatedEventValues) => {
this.updateValueInResource(updatedValue.currentValue, updatedValue.updatedValue);
this.hideAddValueForm();
}));
this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on(
Events.ValueDeleted, (deletedValue: DeletedEventValue) => this.deleteValueFromResource(deletedValue.deletedValue)
));
}
ngOnDestroy() {
// unsubscribe from the event bus when component is destroyed
// if (this.valueOperationEventSubscription !== undefined) {
// this.valueOperationEventSubscription.unsubscribe();
// }
// unsubscribe from the ValueOperationEventService when component is destroyed
if (this.valueOperationEventSubscriptions !== undefined) {
this.valueOperationEventSubscriptions.forEach(sub => sub.unsubscribe());
}
}
/**
* called from the template when the user clicks on the add button
*/
showAddValueForm(prop: PropertyInfoValues) {
this.propID = prop.propDef.id;
this.addValueFormIsVisible = true;
}
/**
* called from the template when the user clicks on the cancel button
*/
hideAddValueForm() {
this.addValueFormIsVisible = false;
this.addButtonIsVisible = true;
this.propID = undefined;
}
/**
* given a resource property, check if an add button should be displayed under the property values
*
* @param prop the resource property
*/
addValueIsAllowed(prop: PropertyInfoValues): boolean {
// if the ontology flags this as a read-only property,
// don't ever allow to add a value
if (prop.propDef instanceof ResourcePropertyDefinition && !prop.propDef.isEditable) {
return false;
}
const isAllowed = CardinalityUtil.createValueForPropertyAllowed(
prop.propDef.id, prop.values.length, this.parentResource.entityInfo.classes[this.parentResource.type]);
// check if:
// cardinality allows for a value to be added
// value component does not already have an add value form open
// user has write permissions
return isAllowed && this.propID !== prop.propDef.id && this.addButtonIsVisible;
}
/**
* updates the UI in the event of a new value being added to show the new value
*
* @param valueToAdd the value to add to the end of the values array of the filtered property
*/
addValueToResource(valueToAdd: ReadValue): void {
if (this.propArray) {
this.propArray
.filter(propInfoValueArray =>
propInfoValueArray.propDef.id === valueToAdd.property) // filter to the correct property
.forEach(propInfoValue =>
propInfoValue.values.push(valueToAdd)); // push new value to array
if (valueToAdd instanceof ReadTextValueAsXml) {
this._updateStandoffLinkValue();
}
} else {
console.error('No properties exist for this resource');
}
}
/**
* updates the UI in the event of an existing value being updated to show the updated value
*
* @param valueToReplace the value to be replaced within the values array of the filtered property
* @param updatedValue the value to replace valueToReplace with
*/
updateValueInResource(valueToReplace: ReadValue, updatedValue: ReadValue): void {
if (this.propArray && updatedValue !== null) {
this.propArray
.filter(propInfoValueArray =>
propInfoValueArray.propDef.id === valueToReplace.property) // filter to the correct property
.forEach(filteredpropInfoValueArray => {
filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property
if (val.id === valueToReplace.id) { // find the value that should be updated using the id of valueToReplace
filteredpropInfoValueArray.values[index] = updatedValue; // replace value with the updated value
}
});
});
if (updatedValue instanceof ReadTextValueAsXml) {
this._updateStandoffLinkValue();
}
} else {
console.error('No properties exist for this resource');
}
}
/**
* updates the UI in the event of an existing value being deleted
*
* @param valueToDelete the value to remove from the values array of the filtered property
*/
deleteValueFromResource(valueToDelete: DeleteValue): void {
if (this.propArray) {
this.propArray
.filter(propInfoValueArray => // filter to the correct type
this._valueService.compareObjectTypeWithValueType(propInfoValueArray.propDef.objectType, valueToDelete.type))
.forEach(filteredpropInfoValueArray => {
filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property
if (val.id === valueToDelete.id) { // find the value that was deleted using the id
filteredpropInfoValueArray.values.splice(index, 1); // remove the value from the values array
if (val instanceof ReadTextValueAsXml) {
this._updateStandoffLinkValue();
}
}
});
}
);
} else {
console.error('No properties exist for this resource');
}
}
/**
* updates the standoff link value for the resource being displayed.
*
*/
private _updateStandoffLinkValue(): void {
if (this.parentResource === undefined) {
// this should never happen:
// if the user was able to click on a standoff link,
// then the resource must have been initialised before.
return;
}
const gravsearchQuery = `
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/simple/v2#>
CONSTRUCT {
?res knora-api:isMainResource true .
?res knora-api:hasStandoffLinkTo ?target .
} WHERE {
BIND(<${this.parentResource.id}> as ?res) .
OPTIONAL {
?res knora-api:hasStandoffLinkTo ?target .
}
}
OFFSET 0
`;
this._dspApiConnection.v2.search.doExtendedSearch(gravsearchQuery).subscribe(
(res: ReadResourceSequence) => {
// one resource is expected
if (res.resources.length !== 1) {
return;
}
const newStandoffLinkVals = res.resources[0].getValuesAs(Constants.HasStandoffLinkToValue, ReadLinkValue);
this.propArray.filter(
resPropInfoVal => (resPropInfoVal.propDef.id === Constants.HasStandoffLinkToValue)
).forEach(
standoffLinkResPropInfoVal => {
// delete all the existing standoff link values
standoffLinkResPropInfoVal.values = [];
// push standoff link values retrieved for the resource
newStandoffLinkVals.forEach(
standoffLinkVal => {
standoffLinkResPropInfoVal.values.push(standoffLinkVal);
}
);
});
},
err => {
console.error(err);
}
);
}
}