mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-14 21:43:04 +00:00
DS-4107 Retrieve and model metadata as a map
This commit is contained in:
@@ -7,10 +7,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let metadatum of metadata" class="metadata-row">
|
<ng-container *ngFor="let entry of metadata | keyvalue">
|
||||||
<td>{{metadatum.key}}</td>
|
<tr *ngFor="let value of entry.value" class="metadata-row">
|
||||||
<td>{{metadatum.value}}</td>
|
<td>{{entry.key}}</td>
|
||||||
<td>{{metadatum.language}}</td>
|
<td>{{value.value}}</td>
|
||||||
</tr>
|
<td>{{value.language}}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
@@ -1,6 +1,6 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {Item} from '../../../core/shared/item.model';
|
import {Item} from '../../../core/shared/item.model';
|
||||||
import {Metadatum} from '../../../core/shared/metadatum.model';
|
import {MetadataMap} from '../../../core/shared/metadata.interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-modify-item-overview',
|
selector: 'ds-modify-item-overview',
|
||||||
@@ -12,7 +12,7 @@ import {Metadatum} from '../../../core/shared/metadatum.model';
|
|||||||
export class ModifyItemOverviewComponent implements OnInit {
|
export class ModifyItemOverviewComponent implements OnInit {
|
||||||
|
|
||||||
@Input() item: Item;
|
@Input() item: Item;
|
||||||
metadata: Metadatum[];
|
metadata: MetadataMap;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.metadata = this.item.metadata;
|
this.metadata = this.item.metadata;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<a *ngFor="let metadatum of values; let last=last;" [href]="metadatum.value">
|
<a *ngFor="let value of values; let last=last;" [href]="value.value">
|
||||||
{{ linktext || metadatum.value }}<span *ngIf="!last" [innerHTML]="separator"></span>
|
{{ linktext || value.value }}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</a>
|
</a>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { MetadataValuesComponent } from '../metadata-values/metadata-values.component';
|
import { MetadataValuesComponent } from '../metadata-values/metadata-values.component';
|
||||||
|
import { MetadataValue } from '../../../core/shared/metadata.interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the configured 'values' into the ds-metadata-field-wrapper component as a link.
|
* This component renders the configured 'values' into the ds-metadata-field-wrapper component as a link.
|
||||||
@@ -18,7 +19,7 @@ export class MetadataUriValuesComponent extends MetadataValuesComponent {
|
|||||||
|
|
||||||
@Input() linktext: any;
|
@Input() linktext: any;
|
||||||
|
|
||||||
@Input() values: any;
|
@Input() values: MetadataValue[];
|
||||||
|
|
||||||
@Input() separator: string;
|
@Input() separator: string;
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<span *ngFor="let metadatum of values; let last=last;">
|
<span *ngFor="let value of values; let last=last;">
|
||||||
{{metadatum.value}}<span *ngIf="!last" [innerHTML]="separator"></span>
|
{{value.value}}<span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</span>
|
</span>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
import { MetadataValue } from '../../../core/shared/metadata.interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the configured 'values' into the ds-metadata-field-wrapper component.
|
* This component renders the configured 'values' into the ds-metadata-field-wrapper component.
|
||||||
@@ -12,7 +12,7 @@ import { Metadatum } from '../../../core/shared/metadatum.model';
|
|||||||
})
|
})
|
||||||
export class MetadataValuesComponent {
|
export class MetadataValuesComponent {
|
||||||
|
|
||||||
@Input() values: Metadatum[];
|
@Input() values: MetadataValue[];
|
||||||
|
|
||||||
@Input() separator: string;
|
@Input() separator: string;
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
|
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
|
||||||
<dd class="col-md-8">{{file.findMetadata("dc.description")}}</dd>
|
<dd class="col-md-8">{{file.firstMetadataValue("dc.description")}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
|
@@ -9,11 +9,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<table class="table table-responsive table-striped">
|
<table class="table table-responsive table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let metadatum of (metadata$ | async)">
|
<ng-container *ngFor="let entry of (metadata$ | async) | keyvalue">
|
||||||
<td>{{metadatum.key}}</td>
|
<tr *ngFor="let value of entry.value">
|
||||||
<td>{{metadatum.value}}</td>
|
<td>{{entry.key}}</td>
|
||||||
<td>{{metadatum.language}}</td>
|
<td>{{value.value}}</td>
|
||||||
</tr>
|
<td>{{value.language}}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section>
|
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section>
|
||||||
|
@@ -6,7 +6,7 @@ import { ActivatedRoute } from '@angular/router';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { ItemPageComponent } from '../simple/item-page.component';
|
import { ItemPageComponent } from '../simple/item-page.component';
|
||||||
import { Metadatum } from '../../core/shared/metadatum.model';
|
import { MetadataMap } from '../../core/shared/metadata.interfaces';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
|
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
@@ -34,7 +34,7 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit {
|
|||||||
|
|
||||||
itemRD$: Observable<RemoteData<Item>>;
|
itemRD$: Observable<RemoteData<Item>>;
|
||||||
|
|
||||||
metadata$: Observable<Metadatum[]>;
|
metadata$: Observable<MetadataMap>;
|
||||||
|
|
||||||
constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
|
constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
|
||||||
super(route, items, metadataService);
|
super(route, items, metadataService);
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div class="item-page-specific-field">
|
<div class="item-page-specific-field">
|
||||||
<ds-metadata-values [values]="item?.filterMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-values>
|
<ds-metadata-values [values]="item?.allMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-values>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<h2 class="item-page-title-field">
|
<h2 class="item-page-title-field">
|
||||||
<ds-metadata-values [values]="item?.filterMetadata(fields)"></ds-metadata-values>
|
<ds-metadata-values [values]="item?.allMetadata(fields)"></ds-metadata-values>
|
||||||
</h2>
|
</h2>
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div class="item-page-specific-field">
|
<div class="item-page-specific-field">
|
||||||
<ds-metadata-uri-values [values]="item?.filterMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-uri-values>
|
<ds-metadata-uri-values [values]="item?.allMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-uri-values>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { autoserialize } from 'cerialize';
|
import { autoserialize } from 'cerialize';
|
||||||
import { Metadatum } from '../core/shared/metadatum.model';
|
import { MetadataMap } from '../core/shared/metadata.interfaces';
|
||||||
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +16,6 @@ export class NormalizedSearchResult implements ListableObject {
|
|||||||
* The metadata that was used to find this item, hithighlighted
|
* The metadata that was used to find this item, hithighlighted
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
hitHighlights: Metadatum[];
|
hitHighlights: MetadataMap;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../core/shared/dspace-object.model';
|
||||||
import { Metadatum } from '../core/shared/metadatum.model';
|
import { MetadataMap } from '../core/shared/metadata.interfaces';
|
||||||
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,6 +14,6 @@ export class SearchResult<T extends DSpaceObject> implements ListableObject {
|
|||||||
/**
|
/**
|
||||||
* The metadata that was used to find this item, hithighlighted
|
* The metadata that was used to find this item, hithighlighted
|
||||||
*/
|
*/
|
||||||
hitHighlights: Metadatum[];
|
hitHighlights: MetadataMap;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -38,8 +38,8 @@ import { DSpaceObject } from '../shared/dspace-object.model';
|
|||||||
export class BrowseService {
|
export class BrowseService {
|
||||||
protected linkPath = 'browses';
|
protected linkPath = 'browses';
|
||||||
|
|
||||||
private static toSearchKeyArray(metadatumKey: string): string[] {
|
private static toSearchKeyArray(metadataKey: string): string[] {
|
||||||
const keyParts = metadatumKey.split('.');
|
const keyParts = metadataKey.split('.');
|
||||||
const searchFor = [];
|
const searchFor = [];
|
||||||
searchFor.push('*');
|
searchFor.push('*');
|
||||||
for (let i = 0; i < keyParts.length - 1; i++) {
|
for (let i = 0; i < keyParts.length - 1; i++) {
|
||||||
@@ -47,7 +47,7 @@ export class BrowseService {
|
|||||||
const nextPart = [...prevParts, '*'].join('.');
|
const nextPart = [...prevParts, '*'].join('.');
|
||||||
searchFor.push(nextPart);
|
searchFor.push(nextPart);
|
||||||
}
|
}
|
||||||
searchFor.push(metadatumKey);
|
searchFor.push(metadataKey);
|
||||||
return searchFor;
|
return searchFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +179,8 @@ export class BrowseService {
|
|||||||
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBrowseURLFor(metadatumKey: string, linkPath: string): Observable<string> {
|
getBrowseURLFor(metadataKey: string, linkPath: string): Observable<string> {
|
||||||
const searchKeyArray = BrowseService.toSearchKeyArray(metadatumKey);
|
const searchKeyArray = BrowseService.toSearchKeyArray(metadataKey);
|
||||||
return this.getBrowseDefinitions().pipe(
|
return this.getBrowseDefinitions().pipe(
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((browseDefinitions: BrowseDefinition[]) => browseDefinitions
|
map((browseDefinitions: BrowseDefinition[]) => browseDefinitions
|
||||||
@@ -191,7 +191,7 @@ export class BrowseService {
|
|||||||
),
|
),
|
||||||
map((def: BrowseDefinition) => {
|
map((def: BrowseDefinition) => {
|
||||||
if (isEmpty(def) || isEmpty(def._links) || isEmpty(def._links[linkPath])) {
|
if (isEmpty(def) || isEmpty(def._links) || isEmpty(def._links[linkPath])) {
|
||||||
throw new Error(`A browse endpoint for ${linkPath} on ${metadatumKey} isn't configured`);
|
throw new Error(`A browse endpoint for ${linkPath} on ${metadataKey} isn't configured`);
|
||||||
} else {
|
} else {
|
||||||
return def._links[linkPath];
|
return def._links[linkPath];
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { autoserialize, autoserializeAs, deserialize, serialize } from 'cerialize';
|
import { autoserialize, autoserializeAs, deserialize, serialize } from 'cerialize';
|
||||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
|
import { MetadataMap } from '../../shared/metadata.interfaces';
|
||||||
import { Metadatum } from '../../shared/metadatum.model';
|
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
import { mapsTo } from '../builders/build-decorators';
|
import { mapsTo } from '../builders/build-decorators';
|
||||||
import { NormalizedObject } from './normalized-object.model';
|
import { NormalizedObject } from './normalized-object.model';
|
||||||
@@ -46,10 +45,10 @@ export class NormalizedDSpaceObject extends NormalizedObject {
|
|||||||
type: ResourceType;
|
type: ResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array containing all metadata of this DSpaceObject
|
* All metadata of this DSpaceObject
|
||||||
*/
|
*/
|
||||||
@autoserializeAs(Metadatum)
|
@autoserialize
|
||||||
metadata: Metadatum[];
|
metadata: MetadataMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||||
|
@@ -7,7 +7,7 @@ import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.
|
|||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
|
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
|
||||||
import { Metadatum } from '../shared/metadatum.model';
|
import { MetadataMap, MetadataValue } from '../shared/metadata.interfaces';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchResponseParsingService implements ResponseParsingService {
|
export class SearchResponseParsingService implements ResponseParsingService {
|
||||||
@@ -16,17 +16,17 @@ export class SearchResponseParsingService implements ResponseParsingService {
|
|||||||
|
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
const payload = data.payload._embedded.searchResult;
|
const payload = data.payload._embedded.searchResult;
|
||||||
const hitHighlights = payload._embedded.objects
|
const hitHighlights: MetadataMap[] = payload._embedded.objects
|
||||||
.map((object) => object.hitHighlights)
|
.map((object) => object.hitHighlights)
|
||||||
.map((hhObject) => {
|
.map((hhObject) => {
|
||||||
|
const mdMap: MetadataMap = {};
|
||||||
if (hhObject) {
|
if (hhObject) {
|
||||||
return Object.keys(hhObject).map((key) => Object.assign(new Metadatum(), {
|
for (const key of Object.keys(hhObject)) {
|
||||||
key: key,
|
const value: MetadataValue = { value: hhObject[key].join('...'), language: null };
|
||||||
value: hhObject[key].join('...')
|
mdMap[key] = [ value ];
|
||||||
}))
|
}
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
return mdMap;
|
||||||
});
|
});
|
||||||
|
|
||||||
const dsoSelfLinks = payload._embedded.objects
|
const dsoSelfLinks = payload._embedded.objects
|
||||||
|
@@ -91,9 +91,9 @@ export class DSpaceRESTv2Service {
|
|||||||
const form: FormData = new FormData();
|
const form: FormData = new FormData();
|
||||||
form.append('name', dso.name);
|
form.append('name', dso.name);
|
||||||
if (dso.metadata) {
|
if (dso.metadata) {
|
||||||
for (const i of Object.keys(dso.metadata)) {
|
for (const key of Object.keys(dso.metadata)) {
|
||||||
if (isNotEmpty(dso.metadata[i].value)) {
|
for (const value of dso.allMetadataValues(key)) {
|
||||||
form.append(dso.metadata[i].key, dso.metadata[i].value);
|
form.append(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -294,6 +294,10 @@ export class MetadataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasType(value: string): boolean {
|
||||||
|
return this.currentObject.value.hasMetadata('dc.type', { value: value, ignoreCase: true });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this._item is a dissertation
|
* Returns true if this._item is a dissertation
|
||||||
*
|
*
|
||||||
@@ -301,14 +305,7 @@ export class MetadataService {
|
|||||||
* true if this._item has a dc.type equal to 'Thesis'
|
* true if this._item has a dc.type equal to 'Thesis'
|
||||||
*/
|
*/
|
||||||
private isDissertation(): boolean {
|
private isDissertation(): boolean {
|
||||||
let isDissertation = false;
|
return this.hasType('thesis');
|
||||||
for (const metadatum of this.currentObject.value.metadata) {
|
|
||||||
if (metadatum.key === 'dc.type') {
|
|
||||||
isDissertation = metadatum.value.toLowerCase() === 'thesis';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isDissertation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,40 +315,15 @@ export class MetadataService {
|
|||||||
* true if this._item has a dc.type equal to 'Technical Report'
|
* true if this._item has a dc.type equal to 'Technical Report'
|
||||||
*/
|
*/
|
||||||
private isTechReport(): boolean {
|
private isTechReport(): boolean {
|
||||||
let isTechReport = false;
|
return this.hasType('technical report');
|
||||||
for (const metadatum of this.currentObject.value.metadata) {
|
|
||||||
if (metadatum.key === 'dc.type') {
|
|
||||||
isTechReport = metadatum.value.toLowerCase() === 'technical report';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isTechReport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMetaTagValue(key: string): string {
|
private getMetaTagValue(key: string): string {
|
||||||
let value: string;
|
return this.currentObject.value.firstMetadataValue(key);
|
||||||
for (const metadatum of this.currentObject.value.metadata) {
|
|
||||||
if (metadatum.key === key) {
|
|
||||||
value = metadatum.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFirstMetaTagValue(keys: string[]): string {
|
private getFirstMetaTagValue(keys: string[]): string {
|
||||||
let value: string;
|
return this.currentObject.value.firstMetadataValue(keys);
|
||||||
for (const metadatum of this.currentObject.value.metadata) {
|
|
||||||
for (const key of keys) {
|
|
||||||
if (key === metadatum.key) {
|
|
||||||
value = metadatum.value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value !== undefined) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMetaTagValuesAndCombine(key: string): string {
|
private getMetaTagValuesAndCombine(key: string): string {
|
||||||
@@ -359,15 +331,7 @@ export class MetadataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getMetaTagValues(keys: string[]): string[] {
|
private getMetaTagValues(keys: string[]): string[] {
|
||||||
const values: string[] = [];
|
return this.currentObject.value.allMetadataValues(keys);
|
||||||
for (const metadatum of this.currentObject.value.metadata) {
|
|
||||||
for (const key of keys) {
|
|
||||||
if (key === metadatum.key) {
|
|
||||||
values.push(metadatum.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private addMetaTag(property: string, content: string): void {
|
private addMetaTag(property: string, content: string): void {
|
||||||
|
@@ -16,7 +16,7 @@ export class Collection extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description
|
* Corresponds to the metadata field dc.description
|
||||||
*/
|
*/
|
||||||
get introductoryText(): string {
|
get introductoryText(): string {
|
||||||
return this.findMetadata('dc.description');
|
return this.firstMetadataValue('dc.description');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +24,7 @@ export class Collection extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description.abstract
|
* Corresponds to the metadata field dc.description.abstract
|
||||||
*/
|
*/
|
||||||
get shortDescription(): string {
|
get shortDescription(): string {
|
||||||
return this.findMetadata('dc.description.abstract');
|
return this.firstMetadataValue('dc.description.abstract');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +32,7 @@ export class Collection extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.rights
|
* Corresponds to the metadata field dc.rights
|
||||||
*/
|
*/
|
||||||
get copyrightText(): string {
|
get copyrightText(): string {
|
||||||
return this.findMetadata('dc.rights');
|
return this.firstMetadataValue('dc.rights');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +40,7 @@ export class Collection extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.rights.license
|
* Corresponds to the metadata field dc.rights.license
|
||||||
*/
|
*/
|
||||||
get license(): string {
|
get license(): string {
|
||||||
return this.findMetadata('dc.rights.license');
|
return this.firstMetadataValue('dc.rights.license');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +48,7 @@ export class Collection extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description.tableofcontents
|
* Corresponds to the metadata field dc.description.tableofcontents
|
||||||
*/
|
*/
|
||||||
get sidebarText(): string {
|
get sidebarText(): string {
|
||||||
return this.findMetadata('dc.description.tableofcontents');
|
return this.firstMetadataValue('dc.description.tableofcontents');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -17,7 +17,7 @@ export class Community extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description
|
* Corresponds to the metadata field dc.description
|
||||||
*/
|
*/
|
||||||
get introductoryText(): string {
|
get introductoryText(): string {
|
||||||
return this.findMetadata('dc.description');
|
return this.firstMetadataValue('dc.description');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +25,7 @@ export class Community extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description.abstract
|
* Corresponds to the metadata field dc.description.abstract
|
||||||
*/
|
*/
|
||||||
get shortDescription(): string {
|
get shortDescription(): string {
|
||||||
return this.findMetadata('dc.description.abstract');
|
return this.firstMetadataValue('dc.description.abstract');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +33,7 @@ export class Community extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.rights
|
* Corresponds to the metadata field dc.rights
|
||||||
*/
|
*/
|
||||||
get copyrightText(): string {
|
get copyrightText(): string {
|
||||||
return this.findMetadata('dc.rights');
|
return this.firstMetadataValue('dc.rights');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +41,7 @@ export class Community extends DSpaceObject {
|
|||||||
* Corresponds to the metadata field dc.description.tableofcontents
|
* Corresponds to the metadata field dc.description.tableofcontents
|
||||||
*/
|
*/
|
||||||
get sidebarText(): string {
|
get sidebarText(): string {
|
||||||
return this.findMetadata('dc.description.tableofcontents');
|
return this.firstMetadataValue('dc.description.tableofcontents');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Metadatum } from './metadatum.model'
|
import { MetadataMap, MetadataValue, MetadataValueFilter } from './metadata.interfaces';
|
||||||
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { Metadata } from './metadata.model';
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { ResourceType } from './resource-type';
|
import { ResourceType } from './resource-type';
|
||||||
@@ -35,14 +35,14 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
|||||||
* The name for this DSpaceObject
|
* The name for this DSpaceObject
|
||||||
*/
|
*/
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return this.findMetadata('dc.title');
|
return this.firstMetadataValue('dc.title');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array containing all metadata of this DSpaceObject
|
* All metadata of this DSpaceObject
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
metadata: Metadatum[] = [];
|
metadata: MetadataMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||||
@@ -54,42 +54,29 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
|||||||
*/
|
*/
|
||||||
owner: Observable<RemoteData<DSpaceObject>>;
|
owner: Observable<RemoteData<DSpaceObject>>;
|
||||||
|
|
||||||
/**
|
/** Gets all matching metadata in this DSpaceObject. See `Metadata.all` for more information. */
|
||||||
* Find a metadata field by key and language
|
allMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): MetadataValue[] {
|
||||||
*
|
return Metadata.all(this.metadata, keyOrKeys, valueFilter);
|
||||||
* This method returns the value of the first element
|
|
||||||
* in the metadata array that matches the provided
|
|
||||||
* key and language
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @param language
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
findMetadata(key: string, language?: string): string {
|
|
||||||
const metadatum = this.metadata.find((m: Metadatum) => {
|
|
||||||
return m.key === key && (isEmpty(language) || m.language === language)
|
|
||||||
});
|
|
||||||
if (isNotEmpty(metadatum)) {
|
|
||||||
return metadatum.value;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Like `allMetadata`, but only returns string values. */
|
||||||
* Find metadata by an array of keys
|
allMetadataValues(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string[] {
|
||||||
*
|
return Metadata.allValues(this.metadata, keyOrKeys, valueFilter);
|
||||||
* This method returns the values of the element
|
}
|
||||||
* in the metadata array that match the provided
|
|
||||||
* key(s)
|
/** Gets the first matching metadata in this DSpaceObject, or `undefined`. */
|
||||||
*
|
firstMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): MetadataValue {
|
||||||
* @param key(s)
|
return Metadata.first(this.metadata, keyOrKeys, valueFilter);
|
||||||
* @return Array<Metadatum>
|
}
|
||||||
*/
|
|
||||||
filterMetadata(keys: string[]): Metadatum[] {
|
/** Like `firstMetadata`, but only returns a string value, or `undefined`. */
|
||||||
return this.metadata.filter((metadatum: Metadatum) => {
|
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
|
||||||
return keys.some((key) => key === metadatum.key);
|
return Metadata.firstValue(this.metadata, keyOrKeys, valueFilter);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/** Checks for matching metadata in this DSpaceObject. */
|
||||||
|
hasMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): boolean {
|
||||||
|
return Metadata.has(this.metadata, keyOrKeys, valueFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
30
src/app/core/shared/metadata.interfaces.ts
Normal file
30
src/app/core/shared/metadata.interfaces.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/** A map of metadata keys to an ordered list of MetadataValue objects. */
|
||||||
|
export interface MetadataMap {
|
||||||
|
[ key: string ]: MetadataValue[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A single metadata value and its properties. */
|
||||||
|
export interface MetadataValue {
|
||||||
|
|
||||||
|
/** The language. */
|
||||||
|
language: string;
|
||||||
|
|
||||||
|
/** The string value. */
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constraints for matching metadata values. */
|
||||||
|
export interface MetadataValueFilter {
|
||||||
|
|
||||||
|
/** The language constraint. */
|
||||||
|
language?: string;
|
||||||
|
|
||||||
|
/** The value constraint. */
|
||||||
|
value?: string;
|
||||||
|
|
||||||
|
/** Whether the value constraint should match without regard to case. */
|
||||||
|
ignoreCase?: boolean;
|
||||||
|
|
||||||
|
/** Whether the value constraint should match as a substring. */
|
||||||
|
substring?: boolean;
|
||||||
|
}
|
119
src/app/core/shared/metadata.model.ts
Normal file
119
src/app/core/shared/metadata.model.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
|
import { MetadataMap, MetadataValue, MetadataValueFilter } from './metadata.interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods for working with DSpace metadata.
|
||||||
|
*/
|
||||||
|
export class Metadata {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all matching metadata.
|
||||||
|
*
|
||||||
|
* @param {MetadataMap|MetadataMap[]} mapOrMaps The source map(s). Values will only be returned from one map --
|
||||||
|
* the first with at least one match.
|
||||||
|
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported, so `'*'` will
|
||||||
|
* match all keys, `'dc.date.*'` will match all qualified dc dates, and so on. Exact keys will be evaluated
|
||||||
|
* (and matches returned) in the order they are given in the parameter. When multiple keys match a wildcard,
|
||||||
|
* they are evaluated in the order they are stored in the map (alphanumeric if obtained from the REST api).
|
||||||
|
* If duplicate or overlapping keys are specified, the first one takes precedence. For example, specifying
|
||||||
|
* `['dc.date', 'dc.*', '*']` will cause any `dc.date` values to be evaluated (and returned, if matched)
|
||||||
|
* first, followed by any other `dc` metadata values, followed by any other (non-dc) metadata values.
|
||||||
|
* @param {MetadataValueFilter} filter The value filter to use.
|
||||||
|
* @returns {MetadataValue[]} the matching values or an empty array.
|
||||||
|
*/
|
||||||
|
public static all(mapOrMaps: MetadataMap | MetadataMap[], keyOrKeys: string | string[],
|
||||||
|
filter?: MetadataValueFilter): MetadataValue[] {
|
||||||
|
const mdMaps: MetadataMap[] = mapOrMaps instanceof Array ? mapOrMaps : [ mapOrMaps ];
|
||||||
|
const matches: MetadataValue[] = [];
|
||||||
|
for (const mdMap of mdMaps) {
|
||||||
|
for (const mdKey of Metadata.resolveKeys(mdMap, keyOrKeys)) {
|
||||||
|
const candidates = mdMap[mdKey];
|
||||||
|
if (candidates) {
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (Metadata.valueMatches(candidate, filter)) {
|
||||||
|
matches.push(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isEmpty(matches)) {
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Like `all`, but only returns string values. */
|
||||||
|
public static allValues(mapOrMaps: MetadataMap | MetadataMap[], keyOrKeys: string | string[],
|
||||||
|
filter?: MetadataValueFilter): string[] {
|
||||||
|
return Metadata.all(mapOrMaps, keyOrKeys, filter).map((mdValue) => mdValue.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the first matching MetadataValue object in a map, or `undefined`. */
|
||||||
|
public static first(mdMapOrMaps: MetadataMap | MetadataMap[], keyOrKeys: string | string[],
|
||||||
|
filter?: MetadataValueFilter): MetadataValue {
|
||||||
|
const mdMaps: MetadataMap[] = mdMapOrMaps instanceof Array ? mdMapOrMaps : [ mdMapOrMaps ];
|
||||||
|
for (const mdMap of mdMaps) {
|
||||||
|
for (const key of Metadata.resolveKeys(mdMap, keyOrKeys)) {
|
||||||
|
const values: MetadataValue[] = mdMap[key];
|
||||||
|
if (values) {
|
||||||
|
return values.find((value: MetadataValue) => Metadata.valueMatches(value, filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Like `first`, but only returns a string value, or `undefined`. */
|
||||||
|
public static firstValue(mdMapOrMaps: MetadataMap | MetadataMap[], keyOrKeys: string | string[],
|
||||||
|
filter?: MetadataValueFilter): string {
|
||||||
|
const value = Metadata.first(mdMapOrMaps, keyOrKeys, filter);
|
||||||
|
return value === undefined ? undefined : value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks the given map for a matching value. */
|
||||||
|
public static has(mdMapOrMaps: MetadataMap | MetadataMap[], keyOrKeys: string | string[],
|
||||||
|
filter?: MetadataValueFilter): boolean {
|
||||||
|
return Metadata.first(mdMapOrMaps, keyOrKeys, filter) !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks if a value matches a filter. */
|
||||||
|
public static valueMatches(mdValue: MetadataValue, filter: MetadataValueFilter) {
|
||||||
|
if (!filter) {
|
||||||
|
return true;
|
||||||
|
} else if (filter.language && filter.language !== mdValue.language) {
|
||||||
|
return false;
|
||||||
|
} else if (filter.value) {
|
||||||
|
let fValue = filter.value;
|
||||||
|
let mValue = mdValue.value;
|
||||||
|
if (filter.ignoreCase) {
|
||||||
|
fValue = filter.value.toLowerCase();
|
||||||
|
mValue = mdValue.value.toLowerCase();
|
||||||
|
}
|
||||||
|
if (filter.substring) {
|
||||||
|
return mValue.includes(fValue);
|
||||||
|
} else {
|
||||||
|
return mValue === fValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the list of keys in the map limited by, and in the order given by `keyOrKeys` */
|
||||||
|
private static resolveKeys(mdMap: MetadataMap, keyOrKeys: string | string[]): string[] {
|
||||||
|
const inputKeys: string[] = keyOrKeys instanceof Array ? keyOrKeys : [ keyOrKeys ];
|
||||||
|
const outputKeys: string[] = [];
|
||||||
|
for (const inputKey of inputKeys) {
|
||||||
|
if (inputKey.includes('*')) {
|
||||||
|
const inputKeyRegex = new RegExp('^' + inputKey.replace('.', '\.').replace('*', '.*') + '$');
|
||||||
|
for (const mapKey of Object.keys(mdMap)) {
|
||||||
|
if (!outputKeys.includes(mapKey) && inputKeyRegex.test(mapKey)) {
|
||||||
|
outputKeys.push(mapKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mdMap.hasOwnProperty(inputKey) && !outputKeys.includes(inputKey)) {
|
||||||
|
outputKeys.push(inputKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputKeys;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,23 +0,0 @@
|
|||||||
import { autoserialize } from 'cerialize';
|
|
||||||
|
|
||||||
export class Metadatum {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The metadata field of this Metadatum
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
key: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The language of this Metadatum
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
language: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value of this Metadatum
|
|
||||||
*/
|
|
||||||
@autoserialize
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
}
|
|
@@ -8,6 +8,7 @@ import { FormGroup } from '@angular/forms';
|
|||||||
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
import { DynamicFormControlModel } from '@ng-dynamic-forms/core/src/model/dynamic-form-control.model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
|
import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.interfaces';
|
||||||
import { isNotEmpty } from '../../empty.util';
|
import { isNotEmpty } from '../../empty.util';
|
||||||
import { ResourceType } from '../../../core/shared/resource-type';
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.formModel.forEach(
|
this.formModel.forEach(
|
||||||
(fieldModel: DynamicInputModel) => {
|
(fieldModel: DynamicInputModel) => {
|
||||||
fieldModel.value = this.dso.findMetadata(fieldModel.name);
|
fieldModel.value = this.dso.firstMetadataValue(fieldModel.name);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.formGroup = this.formService.createFormGroup(this.formModel);
|
this.formGroup = this.formService.createFormGroup(this.formModel);
|
||||||
@@ -77,20 +78,24 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks which new fields where added and sends the updated version of the DSO to the parent component
|
* Checks which new fields were added and sends the updated version of the DSO to the parent component
|
||||||
*/
|
*/
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
const metadata = this.formModel.map(
|
const formMetadata = new Object() as MetadataMap;
|
||||||
(fieldModel: DynamicInputModel) => {
|
this.formModel.forEach((fieldModel: DynamicInputModel) => {
|
||||||
return { key: fieldModel.name, value: fieldModel.value }
|
const value: MetadataValue = { value: fieldModel.value as string, language: null };
|
||||||
|
if (formMetadata.hasOwnProperty(fieldModel.name)) {
|
||||||
|
formMetadata[fieldModel.name].push(value);
|
||||||
|
} else {
|
||||||
|
formMetadata[fieldModel.name] = [ value ];
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
const filteredOldMetadata = this.dso.metadata.filter((filter) => !metadata.map((md) => md.key).includes(filter.key));
|
|
||||||
const filteredNewMetadata = metadata.filter((md) => isNotEmpty(md.value));
|
|
||||||
|
|
||||||
const newMetadata = [...filteredOldMetadata, ...filteredNewMetadata];
|
|
||||||
const updatedDSO = Object.assign({}, this.dso, {
|
const updatedDSO = Object.assign({}, this.dso, {
|
||||||
metadata: newMetadata,
|
metadata: {
|
||||||
|
...this.dso.metadata,
|
||||||
|
...formMetadata
|
||||||
|
},
|
||||||
type: ResourceType.Community
|
type: ResourceType.Community
|
||||||
});
|
});
|
||||||
this.submitForm.emit(updatedDSO);
|
this.submitForm.emit(updatedDSO);
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { ListableObject } from '../listable-object.model';
|
import { ListableObject } from '../listable-object.model';
|
||||||
import { hasValue } from '../../../empty.util';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-abstract-object-element',
|
selector: 'ds-abstract-object-element',
|
||||||
@@ -11,8 +10,4 @@ export class AbstractListableElementComponent <T extends ListableObject> {
|
|||||||
public constructor(@Inject('objectElementProvider') public listableObject: ListableObject) {
|
public constructor(@Inject('objectElementProvider') public listableObject: ListableObject) {
|
||||||
this.object = listableObject as T;
|
this.object = listableObject as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValue(data) {
|
|
||||||
return hasValue(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -5,19 +5,19 @@
|
|||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</a>
|
</a>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h4 class="card-title">{{object.findMetadata('dc.title')}}</h4>
|
<h4 class="card-title">{{object.firstMetadataValue('dc.title')}}</h4>
|
||||||
|
|
||||||
<ds-truncatable-part [id]="object.id" [minLines]="2">
|
<ds-truncatable-part [id]="object.id" [minLines]="2">
|
||||||
<p *ngIf="object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0" class="item-authors card-text text-muted">
|
<p *ngIf="object.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])" class="item-authors card-text text-muted">
|
||||||
<span *ngFor="let authorMd of object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{authorMd.value}}
|
<span *ngFor="let author of object.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{author}}
|
||||||
<span *ngIf="!last">; </span>
|
<span *ngIf="!last">; </span>
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="hasValue(object.findMetadata('dc.date.issued'))" class="item-date">{{object.findMetadata("dc.date.issued")}}</span>
|
<span *ngIf="object.hasMetadata('dc.date.issued')" class="item-date">{{object.firstMetadataValue("dc.date.issued")}}</span>
|
||||||
</p>
|
</p>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
|
|
||||||
<ds-truncatable-part [id]="object.id" [minLines]="5">
|
<ds-truncatable-part [id]="object.id" [minLines]="5">
|
||||||
<p *ngIf="object.findMetadata('dc.description.abstract')" class="item-abstract card-text">{{object.findMetadata("dc.description.abstract") }}</p>
|
<p *ngIf="object.hasMetadata('dc.description.abstract')" class="item-abstract card-text">{{object.firstMetadataValue("dc.description.abstract")}}</p>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
|
|
||||||
<div class="text-center pt-2">
|
<div class="text-center pt-2">
|
||||||
|
@@ -8,20 +8,20 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="3" type="h4">
|
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="3" type="h4">
|
||||||
<h4 class="card-title" [innerHTML]="dso.findMetadata('dc.title')"></h4>
|
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
<p *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
|
<p *ngIf="dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
||||||
class="item-authors card-text text-muted">
|
class="item-authors card-text text-muted">
|
||||||
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="1">
|
||||||
<span *ngIf="hasValue(dso.findMetadata('dc.date.issued'))" class="item-date">{{dso.findMetadata("dc.date.issued")}}</span>
|
<span *ngIf="dso.hasMetadata('dc.date.issued')" class="item-date">{{dso.firstMetadataValue('dc.date.issued')}}</span>
|
||||||
<span *ngFor="let authorMd of dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);">,
|
<span *ngFor="let author of dso.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);">,
|
||||||
<span [innerHTML]="authorMd.value"></span>
|
<span [innerHTML]="author"></span>
|
||||||
</span>
|
</span>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
</p>
|
</p>
|
||||||
<p class="item-abstract card-text">
|
<p class="item-abstract card-text">
|
||||||
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="3">
|
<ds-truncatable-part [fixedHeight]="true" [id]="dso.id" [minLines]="3">
|
||||||
<span [innerHTML]="getFirstValue('dc.description.abstract')"></span>
|
<span [innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
</p>
|
</p>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
@@ -2,12 +2,11 @@ import { Component, Inject } from '@angular/core';
|
|||||||
|
|
||||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
|
||||||
import { isEmpty, hasNoValue, hasValue } from '../../empty.util';
|
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
||||||
import { TruncatableService } from '../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { Metadata } from '../../../core/shared/metadata.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-result-grid-element',
|
selector: 'ds-search-result-grid-element',
|
||||||
@@ -22,39 +21,14 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
|||||||
this.dso = this.object.dspaceObject;
|
this.dso = this.object.dspaceObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
getValues(keys: string[]): string[] {
|
/** Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights. */
|
||||||
const results: string[] = new Array<string>();
|
allMetadataValues(keyOrKeys: string | string[]): string[] {
|
||||||
this.object.hitHighlights.forEach(
|
return Metadata.allValues([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
|
||||||
(md: Metadatum) => {
|
|
||||||
if (keys.indexOf(md.key) > -1) {
|
|
||||||
results.push(md.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (isEmpty(results)) {
|
|
||||||
this.dso.filterMetadata(keys).forEach(
|
|
||||||
(md: Metadatum) => {
|
|
||||||
results.push(md.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstValue(key: string): string {
|
/** Gets the first matching metadata string value from hitHighlights or dso metadata, preferring hitHighlights. */
|
||||||
let result: string;
|
firstMetadataValue(keyOrKeys: string | string[]): string {
|
||||||
this.object.hitHighlights.some(
|
return Metadata.firstValue([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
|
||||||
(md: Metadatum) => {
|
|
||||||
if (key === md.key) {
|
|
||||||
result = md.value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (hasNoValue(result)) {
|
|
||||||
result = this.dso.findMetadata(key);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollapsed(): Observable<boolean> {
|
isCollapsed(): Observable<boolean> {
|
||||||
|
@@ -1,23 +1,23 @@
|
|||||||
<ds-truncatable [id]="object.id">
|
<ds-truncatable [id]="object.id">
|
||||||
<a [routerLink]="['/items/' + object.id]" class="lead">
|
<a [routerLink]="['/items/' + object.id]" class="lead">
|
||||||
{{object.findMetadata("dc.title")}}
|
{{object.firstMetadataValue("dc.title")}}
|
||||||
</a>
|
</a>
|
||||||
<div>
|
<div>
|
||||||
<ds-truncatable-part [id]="object.id" [minLines]="1">
|
<ds-truncatable-part [id]="object.id" [minLines]="1">
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<span *ngIf="object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
|
<span *ngIf="object.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
||||||
class="item-list-authors">
|
class="item-list-authors">
|
||||||
<span *ngFor="let authorMd of object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{authorMd.value}}
|
<span *ngFor="let author of object.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{author}}
|
||||||
<span *ngIf="!last">; </span>
|
<span *ngIf="!last">; </span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
(<span *ngIf="hasValue(object.findMetadata('dc.publisher'))" class="item-list-publisher">{{object.findMetadata("dc.publisher")}}, </span><span
|
(<span *ngIf="object.hasMetadata('dc.publisher')" class="item-list-publisher">{{object.firstMetadataValue("dc.publisher")}}, </span><span
|
||||||
*ngIf="hasValue(object.findMetadata('dc.date.issued'))" class="item-list-date">{{object.findMetadata("dc.date.issued")}}</span>)
|
*ngIf="object.hasMetadata('dc.date.issued')" class="item-list-date">{{object.firstMetadataValue("dc.date.issued")}}</span>)
|
||||||
</span>
|
</span>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
<ds-truncatable-part [id]="object.id" [minLines]="3">
|
<ds-truncatable-part [id]="object.id" [minLines]="3">
|
||||||
<div *ngIf="object.findMetadata('dc.description.abstract')" class="item-list-abstract">
|
<div *ngIf="object.hasMetadata('dc.description.abstract')" class="item-list-abstract">
|
||||||
{{object.findMetadata("dc.description.abstract")}}
|
{{object.firstMetadataValue("dc.description.abstract")}}
|
||||||
</div>
|
</div>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
<a [routerLink]="['/collections/' + dso.id]" class="lead" [innerHTML]="getFirstValue('dc.title')"></a>
|
<a [routerLink]="['/collections/' + dso.id]" class="lead" [innerHTML]="firstMetadataValue('dc.title')"></a>
|
||||||
<div *ngIf="dso.shortDescription" class="text-muted abstract-text" [innerHTML]="getFirstValue('dc.description.abstract')"></div>
|
<div *ngIf="dso.shortDescription" class="text-muted abstract-text" [innerHTML]="firstMetadataValue('dc.description.abstract')"></div>
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
<a [routerLink]="['/communities/' + dso.id]" class="lead" [innerHTML]="getFirstValue('dc.title')"></a>
|
<a [routerLink]="['/communities/' + dso.id]" class="lead" [innerHTML]="firstMetadataValue('dc.title')"></a>
|
||||||
<div *ngIf="dso.shortDescription" class="text-muted abstract-text" [innerHTML]="getFirstValue('dc.description.abstract')"></div>
|
<div *ngIf="dso.shortDescription" class="text-muted abstract-text" [innerHTML]="firstMetadataValue('dc.description.abstract')"></div>
|
||||||
|
@@ -1,24 +1,24 @@
|
|||||||
<ds-truncatable [id]="dso.id">
|
<ds-truncatable [id]="dso.id">
|
||||||
<a
|
<a
|
||||||
[routerLink]="['/items/' + dso.id]" class="lead"
|
[routerLink]="['/items/' + dso.id]" class="lead"
|
||||||
[innerHTML]="getFirstValue('dc.title')"></a>
|
[innerHTML]="firstMetadataValue('dc.title')"></a>
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
<ds-truncatable-part [id]="dso.id" [minLines]="1">
|
||||||
(<span *ngIf="dso.findMetadata('dc.publisher')" class="item-list-publisher"
|
(<span *ngIf="dso.hasMetadata('dc.publisher')" class="item-list-publisher"
|
||||||
[innerHTML]="getFirstValue('dc.publisher') + ', '"></span><span
|
[innerHTML]="firstMetadataValue('dc.publisher') + ', '"></span><span
|
||||||
*ngIf="hasValue(dso.findMetadata('dc.date.issued'))" class="item-list-date"
|
*ngIf="dso.hasMetadata('dc.date.issued')" class="item-list-date"
|
||||||
[innerHTML]="getFirstValue('dc.date.issued')"></span>)
|
[innerHTML]="firstMetadataValue('dc.date.issued')"></span>)
|
||||||
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
|
<span *ngIf="dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
|
||||||
class="item-list-authors">
|
class="item-list-authors">
|
||||||
<span *ngFor="let author of getValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
<span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
|
||||||
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
</span>
|
</span>
|
||||||
<div *ngIf="dso.findMetadata('dc.description.abstract')" class="item-list-abstract">
|
<div *ngIf="dso.hasMetadata('dc.description.abstract')" class="item-list-abstract">
|
||||||
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
|
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
|
||||||
[innerHTML]="getFirstValue('dc.description.abstract')"></span>
|
[innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
|
||||||
</ds-truncatable-part>
|
</ds-truncatable-part>
|
||||||
</div>
|
</div>
|
||||||
</ds-truncatable>
|
</ds-truncatable>
|
@@ -3,11 +3,10 @@ import { Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { SearchResult } from '../../../+search-page/search-result.model';
|
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
|
||||||
import { hasNoValue, isEmpty } from '../../empty.util';
|
|
||||||
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
import { ListableObject } from '../../object-collection/shared/listable-object.model';
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { TruncatableService } from '../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||||
|
import { Metadata } from '../../../core/shared/metadata.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-result-list-element',
|
selector: 'ds-search-result-list-element',
|
||||||
@@ -22,39 +21,14 @@ export class SearchResultListElementComponent<T extends SearchResult<K>, K exten
|
|||||||
this.dso = this.object.dspaceObject;
|
this.dso = this.object.dspaceObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
getValues(keys: string[]): string[] {
|
/** Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights. */
|
||||||
const results: string[] = new Array<string>();
|
allMetadataValues(keyOrKeys: string | string[]): string[] {
|
||||||
this.object.hitHighlights.forEach(
|
return Metadata.allValues([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
|
||||||
(md: Metadatum) => {
|
|
||||||
if (keys.indexOf(md.key) > -1) {
|
|
||||||
results.push(md.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (isEmpty(results)) {
|
|
||||||
this.dso.filterMetadata(keys).forEach(
|
|
||||||
(md: Metadatum) => {
|
|
||||||
results.push(md.value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstValue(key: string): string {
|
/** Gets the first matching metadata string value from hitHighlights or dso metadata, preferring hitHighlights. */
|
||||||
let result: string;
|
firstMetadataValue(keyOrKeys: string | string[]): string {
|
||||||
this.object.hitHighlights.some(
|
return Metadata.firstValue([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
|
||||||
(md: Metadatum) => {
|
|
||||||
if (key === md.key) {
|
|
||||||
result = md.value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (hasNoValue(result)) {
|
|
||||||
result = this.dso.findMetadata(key);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollapsed(): Observable<boolean> {
|
isCollapsed(): Observable<boolean> {
|
||||||
|
Reference in New Issue
Block a user