Skip to content

Commit 8bdfd01

Browse files
[DURACOM-426] init integration of authority framework
1 parent 43e358b commit 8bdfd01

19 files changed

Lines changed: 979 additions & 19 deletions

src/app/core/shared/metadata.models.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
/* eslint-disable max-classes-per-file */
2+
import { hasValue } from '@dspace/shared/utils/empty.util';
23
import {
34
autoserialize,
45
Deserialize,
56
Serialize,
67
} from 'cerialize';
78
import { v4 as uuidv4 } from 'uuid';
89

10+
911
export const VIRTUAL_METADATA_PREFIX = 'virtual::';
1012

1113
/** A single metadata value and its properties. */
@@ -56,6 +58,24 @@ export class MetadataValue implements MetadataValueInterface {
5658
@autoserialize
5759
confidence: number;
5860

61+
/**
62+
* Returns true if this Metadatum's authority key starts with 'virtual::'
63+
*/
64+
get isVirtual(): boolean {
65+
return hasValue(this.authority) && this.authority.startsWith(VIRTUAL_METADATA_PREFIX);
66+
}
67+
68+
/**
69+
* If this is a virtual Metadatum, it returns everything in the authority key after 'virtual::'.
70+
* Returns undefined otherwise.
71+
*/
72+
get virtualValue(): string {
73+
if (this.isVirtual) {
74+
return this.authority.substring(this.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length);
75+
} else {
76+
return undefined;
77+
}
78+
}
5979
}
6080

6181
/** Constraints for matching metadata values. */

src/app/core/shared/metadata.utils.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import {
2+
hasValue,
3+
isEmpty,
24
isNotEmpty,
35
isNotUndefined,
46
isUndefined,
57
} from '@dspace/shared/utils/empty.util';
68
import escape from 'lodash/escape';
79
import groupBy from 'lodash/groupBy';
10+
import isObject from 'lodash/isObject';
811
import sortBy from 'lodash/sortBy';
12+
import { validate as uuidValidate } from 'uuid';
913

1014
import {
1115
MetadataMapInterface,
@@ -14,6 +18,11 @@ import {
1418
MetadatumViewModel,
1519
} from './metadata.models';
1620

21+
22+
23+
export const AUTHORITY_GENERATE = 'will be generated::';
24+
export const AUTHORITY_REFERENCE = 'will be referenced::';
25+
export const PLACEHOLDER_VALUE = '#PLACEHOLDER_PARENT_METADATA_VALUE#';
1726
/**
1827
* Utility class for working with DSpace object metadata.
1928
*
@@ -148,6 +157,40 @@ export class Metadata {
148157
return isNotUndefined(Metadata.first(metadata, keyOrKeys, hitHighlights, filter));
149158
}
150159

160+
161+
/**
162+
* Returns true if this Metadatum's authority key contains a reference
163+
*/
164+
public static hasAuthorityReference(authority: string): boolean {
165+
return hasValue(authority) && (typeof authority === 'string' && (authority.startsWith(AUTHORITY_GENERATE) || authority.startsWith(AUTHORITY_REFERENCE)));
166+
}
167+
168+
/**
169+
* Returns true if this Metadatum's authority key is a valid
170+
*/
171+
public static hasValidAuthority(authority: string): boolean {
172+
return hasValue(authority) && !Metadata.hasAuthorityReference(authority);
173+
}
174+
175+
/**
176+
* Returns true if this Metadatum's authority key is a valid UUID
177+
*/
178+
public static hasValidItemAuthority(authority: string): boolean {
179+
return hasValue(authority) && uuidValidate(authority);
180+
}
181+
182+
/**
183+
* Returns true if this Metadatum's value is defined
184+
*/
185+
public static hasValue(value: MetadataValue|string): boolean {
186+
if (isEmpty(value)) {
187+
return false;
188+
}
189+
if (isObject(value) && value.hasOwnProperty('value')) {
190+
return isNotEmpty(value.value);
191+
}
192+
return true;
193+
}
151194
/**
152195
* Checks if a value matches a filter.
153196
*

src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<div class="d-flex flex-row">
55
<div class="flex-grow-1 ds-flex-cell ds-value-cell"><b class="dont-break-out preserve-line-breaks">{{ dsoType + '.edit.metadata.headers.value' | translate }}</b></div>
66
<div class="ds-flex-cell ds-lang-cell"><b>{{ dsoType + '.edit.metadata.headers.language' | translate }}</b></div>
7+
<div class="ds-flex-cell ds-authority-cell"><b>{{ dsoType + '.edit.metadata.headers.authority' | translate }}</b></div>
78
<div class="text-center ds-flex-cell ds-edit-cell"><b>{{ dsoType + '.edit.metadata.headers.edit' | translate }}</b></div>
89
</div>
910
</div>

src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('DsoEditMetadataHeadersComponent', () => {
2929
fixture.detectChanges();
3030
});
3131

32-
it('should display three headers', () => {
33-
expect(fixture.debugElement.queryAll(By.css('.ds-flex-cell')).length).toEqual(3);
32+
it('should display four headers', () => {
33+
expect(fixture.debugElement.queryAll(By.css('.ds-flex-cell')).length).toEqual(4);
3434
});
3535
});

src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-shared/dso-edit-metadata-cells.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
max-width: var(--ds-dso-edit-lang-width);
1313
}
1414

15+
.ds-authority-cell {
16+
min-width: var(--ds-dso-edit-authority-width);
17+
max-width: var(--ds-dso-edit-authority-width);
18+
}
19+
20+
1521
.ds-edit-cell {
1622
min-width: var(--ds-dso-edit-actions-width);
1723
}

src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,26 @@
77
@if (!mdValue.editing && !mdRepresentation) {
88
<div class="dont-break-out preserve-line-breaks">{{ mdValue.newValue.value }}</div>
99
}
10+
@if (mdValue.editing && !mdRepresentation && ((isAuthorityControlled() | async) !== true || (enabledFreeTextEditing && (isSuggesterVocabulary() | async) !== true))) {
11+
<textarea class="form-control" rows="5" [(ngModel)]="mdValue.newValue.value"
12+
[attr.aria-label]="(dsoType + '.edit.metadata.edit.value') | translate"
13+
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
14+
}
15+
@if (mdValue.editing && (isScrollableVocabulary() | async) && !enabledFreeTextEditing) {
16+
<ds-dynamic-scrollable-dropdown [bindId]="mdField"
17+
[group]="group"
18+
[model]="getModel()"
19+
(change)="onChangeAuthorityField($event)">
20+
</ds-dynamic-scrollable-dropdown>
21+
}
22+
@if (mdValue.editing && (((isHierarchicalVocabulary() | async) && !enabledFreeTextEditing) || (isSuggesterVocabulary() | async))) {
23+
<ds-dynamic-onebox [group]="group"
24+
[model]="getModel()"
25+
(change)="onChangeAuthorityField($event)">
26+
</ds-dynamic-onebox>
27+
}
28+
29+
1030
@if (mdValue.editing && !mdRepresentation) {
1131
<ds-dso-edit-metadata-value-field-loader [context]="context"
1232
[dso]="dso"
@@ -31,6 +51,37 @@
3151
</span>
3252
</div>
3353
}
54+
@if(mdValue.editing && (isAuthorityControlled() | async) && (isSuggesterVocabulary() | async)) {
55+
<div class="mt-2">
56+
<div class="btn-group w-75">
57+
<i dsAuthorityConfidenceState
58+
class="fas fa-fw p-0 mr-1 mt-auto mb-auto"
59+
aria-hidden="true"
60+
[authorityValue]="mdValue.newValue.confidence"
61+
[iconMode]="true"
62+
></i>
63+
<input class="form-control form-outline" data-test="authority-input" [(ngModel)]="mdValue.newValue.authority" [disabled]="!editingAuthority"
64+
[attr.aria-label]="(dsoType + '.edit.metadata.edit.authority.key') | translate"
65+
(change)="onChangeAuthorityKey()" />
66+
@if(editingAuthority) {
67+
<button class="btn btn-outline-success btn-sm ng-star-inserted" id="metadata-confirm-btn"
68+
[title]="dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate"
69+
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate }}"
70+
(click)="onChangeEditingAuthorityStatus(false)">
71+
<i class="fas fa-lock-open fa-fw"></i>
72+
</button>
73+
} @else {
74+
<button class="btn btn-outline-secondary btn-sm ng-star-inserted" id="metadata-confirm-btn"
75+
[title]="dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate"
76+
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate }}"
77+
(click)="onChangeEditingAuthorityStatus(true)">
78+
<i class="fas fa-lock fa-fw"></i>
79+
</button>
80+
}
81+
</div>
82+
</div>
83+
84+
}
3485
@if (mdRepresentation) {
3586
<div class="d-flex">
3687
<a class="me-2" target="_blank"
@@ -48,6 +99,15 @@
4899
<div class="dont-break-out preserve-line-breaks">{{ mdValue.newValue.language }}</div>
49100
}
50101
</div>
102+
<div class="ds-flex-cell ds-authority-cell" role="cell">
103+
@if (!mdValue.editing) {
104+
<div class="dont-break-out preserve-line-breaks">{{ mdValue.newValue.authority }}</div>
105+
}
106+
@if(mdValue.editing) {
107+
<textarea class="form-control" rows="5" [(ngModel)]="mdValue.newValue.authority"
108+
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
109+
}
110+
</div>
51111
<div class="text-center ds-flex-cell ds-edit-cell" role="cell">
52112
<div class="btn-group">
53113
<div class="edit-field">

0 commit comments

Comments
 (0)