mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-11 03:53:02 +00:00
refactored items, bundles and bitstreams, test builders
This commit is contained in:
@@ -1,12 +1,18 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { getRemoteDataPayload, getSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators';
|
import {
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getRemoteDataPayload,
|
||||||
|
getSucceededRemoteData,
|
||||||
|
toDSpaceObjectListRD
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
@@ -81,6 +87,7 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private itemDataService: ItemDataService,
|
private itemDataService: ItemDataService,
|
||||||
|
private collectionDataService: CollectionDataService,
|
||||||
private translateService: TranslateService) {
|
private translateService: TranslateService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +113,8 @@ export class ItemCollectionMapperComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const owningCollectionRD$ = this.itemRD$.pipe(
|
const owningCollectionRD$ = this.itemRD$.pipe(
|
||||||
switchMap((itemRD: RemoteData<Item>) => itemRD.payload.owningCollection)
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
switchMap((item: Item) => this.collectionDataService.findOwningCollectionFor(item))
|
||||||
);
|
);
|
||||||
const itemCollectionsAndOptions$ = observableCombineLatest(
|
const itemCollectionsAndOptions$ = observableCombineLatest(
|
||||||
this.itemCollectionsRD$,
|
this.itemCollectionsRD$,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<ds-metadata-field-wrapper *ngIf="hasSucceeded() | async" [label]="label | translate">
|
<ds-metadata-field-wrapper *ngIf="(this.collectionsRD$ | async)?.hasSucceeded" [label]="label | translate">
|
||||||
<div class="collections">
|
<div class="collections">
|
||||||
<a *ngFor="let collection of (collections | async); let last=last;" [routerLink]="['/collections', collection.id]">
|
<a *ngFor="let collection of (this.collectionsRD$ | async)?.payload?.page; let last=last;" [routerLink]="['/collections', collection.id]">
|
||||||
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
|
|
||||||
import {map} from 'rxjs/operators';
|
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { CollectionDataService } from '../../../core/data/collection-data.service';
|
||||||
|
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
|
||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { Item } from '../../../core/shared/item.model';
|
import { Item } from '../../../core/shared/item.model';
|
||||||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the parent collections section of the item
|
* This component renders the parent collections section of the item
|
||||||
@@ -25,9 +26,9 @@ export class CollectionsComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
collections: Observable<Collection[]>;
|
collectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||||
|
|
||||||
constructor(private rdbs: RemoteDataBuildService) {
|
constructor(private cds: CollectionDataService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,11 +38,25 @@ export class CollectionsComponent implements OnInit {
|
|||||||
// TODO: this should use parents, but the collections
|
// TODO: this should use parents, but the collections
|
||||||
// for an Item aren't returned by the REST API yet,
|
// for an Item aren't returned by the REST API yet,
|
||||||
// only the owning collection
|
// only the owning collection
|
||||||
this.collections = this.item.owner.pipe(map((rd: RemoteData<Collection>) => [rd.payload]));
|
this.collectionsRD$ = this.cds.findOwningCollectionFor(this.item).pipe(
|
||||||
|
map((rd: RemoteData<Collection>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
new PaginatedList({
|
||||||
|
elementsPerPage: 10,
|
||||||
|
totalPages: 1,
|
||||||
|
currentPage: 1,
|
||||||
|
totalElements: 1
|
||||||
|
} as PageInfo, [rd.payload])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return rd as any;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSucceeded() {
|
|
||||||
return this.item.owner.pipe(map((rd: RemoteData<Collection>) => rd.hasSucceeded));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ds-metadata-field-wrapper [label]="label | translate">
|
<ds-metadata-field-wrapper [label]="label | translate">
|
||||||
<div class="file-section row" *ngFor="let file of (bitstreamsObs | async); let last=last;">
|
<div class="file-section row" *ngFor="let file of (bitstreams$ | async); let last=last;">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<ds-thumbnail [thumbnail]="thumbnails.get(file.id) | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="(file.thumbnail | async)?.payload"></ds-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<a [href]="file.content" [download]="file.name">
|
<a [href]="file._links.content.href" [download]="file.name">
|
||||||
{{"item.page.filesection.download" | translate}}
|
{{"item.page.filesection.download" | translate}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
|
import { Component, Injector, Input, OnInit } from '@angular/core';
|
||||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
|
import { getBitstreamBuilder } from '../../../../core/cache/builders/bitstream-builder';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||||
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -22,27 +25,42 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
|||||||
|
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
bitstreamsObs: Observable<Bitstream[]>;
|
bitstreams$: Observable<Bitstream[]>;
|
||||||
|
|
||||||
thumbnails: Map<string, Observable<Bitstream>> = new Map();
|
constructor(
|
||||||
|
bitstreamDataService: BitstreamDataService,
|
||||||
|
private parentInjector: Injector
|
||||||
|
) {
|
||||||
|
super(bitstreamDataService);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
const originals = this.item.getFiles();
|
// TODO pagination
|
||||||
const licenses = this.item.getBitstreamsByBundleName('LICENSE');
|
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||||
this.bitstreamsObs = observableCombineLatest(originals, licenses).pipe(map(([o, l]) => [...o, ...l]));
|
getFirstSucceededRemoteListPayload(),
|
||||||
this.bitstreamsObs.subscribe(
|
startWith([])
|
||||||
(files) =>
|
);
|
||||||
files.forEach(
|
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'LICENSE', { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||||
|
getFirstSucceededRemoteListPayload(),
|
||||||
|
startWith([])
|
||||||
|
);
|
||||||
|
this.bitstreams$ = observableCombineLatest(originals$, licenses$).pipe(
|
||||||
|
map(([o, l]) => [...o, ...l]),
|
||||||
|
map((files: Bitstream[]) =>
|
||||||
|
files.map(
|
||||||
(original) => {
|
(original) => {
|
||||||
const thumbnail: Observable<Bitstream> = this.item.getThumbnailForOriginal(original);
|
return getBitstreamBuilder(this.parentInjector, original)
|
||||||
this.thumbnails.set(original.id, thumbnail);
|
.loadThumbnail(this.item)
|
||||||
|
.loadBitstreamFormat()
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<ng-container *ngVar="(bitstreamsObs | async) as bitstreams">
|
<ng-container *ngVar="(bitstreams$ | async) as bitstreams">
|
||||||
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
|
<ds-metadata-field-wrapper *ngIf="bitstreams?.length > 0" [label]="label | translate">
|
||||||
<div class="file-section">
|
<div class="file-section">
|
||||||
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" [download]="file?.name">
|
<a *ngFor="let file of bitstreams; let last=last;" [href]="file?._links.content.href" [download]="file?.name">
|
||||||
<span>{{file?.name}}</span>
|
<span>{{file?.name}}</span>
|
||||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the file section of the item
|
* This component renders the file section of the item
|
||||||
@@ -20,14 +22,21 @@ export class FileSectionComponent implements OnInit {
|
|||||||
|
|
||||||
separator = '<br/>';
|
separator = '<br/>';
|
||||||
|
|
||||||
bitstreamsObs: Observable<Bitstream[]>;
|
bitstreams$: Observable<Bitstream[]>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(): void {
|
initialize(): void {
|
||||||
this.bitstreamsObs = this.item.getFiles();
|
this.bitstreams$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL').pipe(
|
||||||
|
getFirstSucceededRemoteListPayload()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
|
||||||
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item',
|
selector: 'ds-item',
|
||||||
@@ -10,4 +14,14 @@ import { Item } from '../../../../core/shared/item.model';
|
|||||||
*/
|
*/
|
||||||
export class ItemComponent {
|
export class ItemComponent {
|
||||||
@Input() object: Item;
|
@Input() object: Item;
|
||||||
|
|
||||||
|
constructor(protected bitstreamDataService: BitstreamDataService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||||
|
getThumbnail(): Observable<Bitstream> {
|
||||||
|
return this.bitstreamDataService.getThumbnailFor(this.object).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
60
src/app/core/cache/builders/bitstream-builder.ts
vendored
Normal file
60
src/app/core/cache/builders/bitstream-builder.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Injector } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { BitstreamDataService } from '../../data/bitstream-data.service';
|
||||||
|
import { BitstreamFormatDataService } from '../../data/bitstream-format-data.service';
|
||||||
|
import { RemoteData } from '../../data/remote-data';
|
||||||
|
import { BitstreamFormat } from '../../shared/bitstream-format.model';
|
||||||
|
import { Bitstream } from '../../shared/bitstream.model';
|
||||||
|
import { Item } from '../../shared/item.model';
|
||||||
|
|
||||||
|
export const getBitstreamBuilder = (parentInjector: Injector, bitstream: Bitstream) => {
|
||||||
|
const injector = Injector.create({
|
||||||
|
providers:[
|
||||||
|
{
|
||||||
|
provide: BitstreamBuilder,
|
||||||
|
useClass: BitstreamBuilder,
|
||||||
|
deps:[
|
||||||
|
BitstreamDataService,
|
||||||
|
BitstreamFormatDataService,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
parent: parentInjector
|
||||||
|
});
|
||||||
|
return injector.get(BitstreamBuilder).initWithBitstream(bitstream);
|
||||||
|
};
|
||||||
|
|
||||||
|
export class BitstreamBuilder {
|
||||||
|
private bitstream: Bitstream;
|
||||||
|
private thumbnail: Observable<RemoteData<Bitstream>>;
|
||||||
|
private format: Observable<RemoteData<BitstreamFormat>>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected bitstreamDataService: BitstreamDataService,
|
||||||
|
protected bitstreamFormatDataService: BitstreamFormatDataService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
initWithBitstream(bitstream: Bitstream): BitstreamBuilder {
|
||||||
|
this.bitstream = bitstream;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadThumbnail(item: Item): BitstreamBuilder {
|
||||||
|
this.thumbnail = this.bitstreamDataService.getMatchingThumbnail(item, this.bitstream);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadBitstreamFormat(): BitstreamBuilder {
|
||||||
|
this.format = this.bitstreamFormatDataService.findByBitstream(this.bitstream);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): Bitstream {
|
||||||
|
const bitstream = this.bitstream;
|
||||||
|
bitstream.thumbnail = this.thumbnail;
|
||||||
|
bitstream.format = this.format;
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -53,7 +53,7 @@ export function getMapsToType(type: string | ResourceType) {
|
|||||||
return typeMap.get(type);
|
return typeMap.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function relationship<T extends CacheableObject>(value: GenericConstructor<T>, isList: boolean = false): any {
|
export function relationship<T extends CacheableObject>(value: GenericConstructor<T>, isList: boolean = false, shouldAutoResolve: boolean = true): any {
|
||||||
return function r(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
return function r(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||||
if (!target || !propertyKey) {
|
if (!target || !propertyKey) {
|
||||||
return;
|
return;
|
||||||
@@ -66,7 +66,8 @@ export function relationship<T extends CacheableObject>(value: GenericConstructo
|
|||||||
relationshipMap.set(target.constructor, metaDataList);
|
relationshipMap.set(target.constructor, metaDataList);
|
||||||
return Reflect.metadata(relationshipKey, {
|
return Reflect.metadata(relationshipKey, {
|
||||||
resourceType: (value as any).type.value,
|
resourceType: (value as any).type.value,
|
||||||
isList
|
isList,
|
||||||
|
shouldAutoResolve
|
||||||
}).apply(this, arguments);
|
}).apply(this, arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,21 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, race as observableRace } from 'rxjs';
|
import {
|
||||||
|
combineLatest as observableCombineLatest,
|
||||||
|
Observable,
|
||||||
|
of as observableOf,
|
||||||
|
race as observableRace
|
||||||
|
} from 'rxjs';
|
||||||
import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { hasValue, hasValueOperator, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
|
import {
|
||||||
|
hasNoValue,
|
||||||
|
hasValue,
|
||||||
|
hasValueOperator,
|
||||||
|
isEmpty,
|
||||||
|
isNotEmpty,
|
||||||
|
isNotUndefined
|
||||||
|
} from '../../../shared/empty.util';
|
||||||
import { PaginatedList } from '../../data/paginated-list';
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
import { RemoteDataError } from '../../data/remote-data-error';
|
import { RemoteDataError } from '../../data/remote-data-error';
|
||||||
@@ -157,46 +169,55 @@ export class RemoteDataBuildService {
|
|||||||
relationships.forEach((relationship: string) => {
|
relationships.forEach((relationship: string) => {
|
||||||
let result;
|
let result;
|
||||||
if (hasValue(normalized[relationship])) {
|
if (hasValue(normalized[relationship])) {
|
||||||
const { resourceType, isList } = getRelationMetadata(normalized, relationship);
|
const { resourceType, isList, shouldAutoResolve } = getRelationMetadata(normalized, relationship);
|
||||||
const objectList = normalized[relationship].page || normalized[relationship];
|
const objectList = normalized[relationship].page || normalized[relationship];
|
||||||
if (typeof objectList !== 'string') {
|
if (shouldAutoResolve) {
|
||||||
objectList.forEach((href: string) => {
|
if (typeof objectList !== 'string') {
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), href);
|
objectList.forEach((href: string) => {
|
||||||
|
const request = new GetRequest(this.requestService.generateRequestId(), href);
|
||||||
|
if (!this.requestService.isCachedOrPending(request)) {
|
||||||
|
this.requestService.configure(request)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const rdArr = [];
|
||||||
|
objectList.forEach((href: string) => {
|
||||||
|
rdArr.push(this.buildSingle(href));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isList) {
|
||||||
|
result = this.aggregate(rdArr);
|
||||||
|
} else if (rdArr.length === 1) {
|
||||||
|
result = rdArr[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const request = new GetRequest(this.requestService.generateRequestId(), objectList);
|
||||||
if (!this.requestService.isCachedOrPending(request)) {
|
if (!this.requestService.isCachedOrPending(request)) {
|
||||||
this.requestService.configure(request)
|
this.requestService.configure(request)
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const rdArr = [];
|
// The rest API can return a single URL to represent a list of resources (e.g. /items/:id/bitstreams)
|
||||||
objectList.forEach((href: string) => {
|
// in that case only 1 href will be stored in the normalized obj (so the isArray above fails),
|
||||||
rdArr.push(this.buildSingle(href));
|
// but it should still be built as a list
|
||||||
});
|
if (isList) {
|
||||||
|
result = this.buildList(objectList);
|
||||||
if (isList) {
|
} else {
|
||||||
result = this.aggregate(rdArr);
|
result = this.buildSingle(objectList);
|
||||||
} else if (rdArr.length === 1) {
|
}
|
||||||
result = rdArr[0];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), objectList);
|
|
||||||
if (!this.requestService.isCachedOrPending(request)) {
|
|
||||||
this.requestService.configure(request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rest API can return a single URL to represent a list of resources (e.g. /items/:id/bitstreams)
|
if (hasValue(normalized[relationship].page)) {
|
||||||
// in that case only 1 href will be stored in the normalized obj (so the isArray above fails),
|
links[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo);
|
||||||
// but it should still be built as a list
|
|
||||||
if (isList) {
|
|
||||||
result = this.buildList(objectList);
|
|
||||||
} else {
|
} else {
|
||||||
result = this.buildSingle(objectList);
|
links[relationship] = result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (hasValue(normalized[relationship].page)) {
|
|
||||||
links[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo);
|
|
||||||
} else {
|
} else {
|
||||||
links[relationship] = result;
|
if (hasNoValue(links._links)) {
|
||||||
|
links._links = {};
|
||||||
|
}
|
||||||
|
links._links[relationship] = {
|
||||||
|
href: objectList
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -22,13 +22,14 @@ export class NormalizedBitstream extends NormalizedDSpaceObject<Bitstream> {
|
|||||||
* The relative path to this Bitstream's file
|
* The relative path to this Bitstream's file
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
|
@relationship(Bitstream, false, false)
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The format of this Bitstream
|
* The format of this Bitstream
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(BitstreamFormat, false)
|
@relationship(BitstreamFormat, false, false)
|
||||||
format: string;
|
format: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,14 +42,14 @@ export class NormalizedBitstream extends NormalizedDSpaceObject<Bitstream> {
|
|||||||
* An array of Bundles that are direct parents of this Bitstream
|
* An array of Bundles that are direct parents of this Bitstream
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(Item, true)
|
@relationship(Item, true, false)
|
||||||
parents: string[];
|
parents: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bundle that owns this Bitstream
|
* The Bundle that owns this Bitstream
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(Item, false)
|
@relationship(Item, false, false)
|
||||||
owner: string;
|
owner: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -22,7 +22,7 @@ export class NormalizedBundle extends NormalizedDSpaceObject<Bundle> {
|
|||||||
* The primary bitstream of this Bundle
|
* The primary bitstream of this Bundle
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(Bitstream, false)
|
@relationship(Bitstream, false, false)
|
||||||
primaryBitstream: string;
|
primaryBitstream: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +39,7 @@ export class NormalizedBundle extends NormalizedDSpaceObject<Bundle> {
|
|||||||
* List of Bitstreams that are part of this Bundle
|
* List of Bitstreams that are part of this Bundle
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(Bitstream, true)
|
@relationship(Bitstream, true, false)
|
||||||
bitstreams: string[];
|
bitstreams: string[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,42 +30,41 @@ export class NormalizedCollection extends NormalizedDSpaceObject<Collection> {
|
|||||||
* The Bitstream that represents the license of this Collection
|
* The Bitstream that represents the license of this Collection
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(License, false)
|
|
||||||
license: string;
|
license: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bitstream that represents the default Access Conditions of this Collection
|
* The Bitstream that represents the default Access Conditions of this Collection
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
@relationship(ResourcePolicy, false)
|
@relationship(ResourcePolicy, false, false)
|
||||||
defaultAccessConditions: string;
|
defaultAccessConditions: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bitstream that represents the logo of this Collection
|
* The Bitstream that represents the logo of this Collection
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Bitstream, false)
|
@relationship(Bitstream, false, false)
|
||||||
logo: string;
|
logo: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of Communities that are direct parents of this Collection
|
* An array of Communities that are direct parents of this Collection
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Community, true)
|
@relationship(Community, true, false)
|
||||||
parents: string[];
|
parents: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Community that owns this Collection
|
* The Community that owns this Collection
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Community, false)
|
@relationship(Community, false, false)
|
||||||
owner: string;
|
owner: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of Items that are part of (not necessarily owned by) this Collection
|
* List of Items that are part of (not necessarily owned by) this Collection
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Item, true)
|
@relationship(Item, true, false)
|
||||||
items: string[];
|
items: string[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -48,25 +48,25 @@ export class NormalizedItem extends NormalizedDSpaceObject<Item> {
|
|||||||
* An array of Collections that are direct parents of this Item
|
* An array of Collections that are direct parents of this Item
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Collection, true)
|
@relationship(Collection, true, false)
|
||||||
parents: string[];
|
parents: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Collection that owns this Item
|
* The Collection that owns this Item
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Collection, false)
|
@relationship(Collection, false, false)
|
||||||
owningCollection: string;
|
owningCollection: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of Bitstreams that are owned by this Item
|
* List of Bitstreams that are owned by this Item
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Bundle, true)
|
@relationship(Bundle, true, false)
|
||||||
bundles: string[];
|
bundles: string[];
|
||||||
|
|
||||||
@deserialize
|
@deserialize
|
||||||
@relationship(Relationship, true)
|
@relationship(Relationship, true, false)
|
||||||
relationships: string[];
|
relationships: string[];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,24 +27,26 @@ export abstract class BaseResponseParsingService {
|
|||||||
return this.processArray(data, request);
|
return this.processArray(data, request);
|
||||||
} else if (isRestDataObject(data)) {
|
} else if (isRestDataObject(data)) {
|
||||||
const object = this.deserialize(data);
|
const object = this.deserialize(data);
|
||||||
if (isNotEmpty(data._embedded)) {
|
|
||||||
Object
|
// TODO remove
|
||||||
.keys(data._embedded)
|
// if (isNotEmpty(data._embedded)) {
|
||||||
.filter((property) => data._embedded.hasOwnProperty(property))
|
// Object
|
||||||
.forEach((property) => {
|
// .keys(data._embedded)
|
||||||
const parsedObj = this.process<ObjectDomain>(data._embedded[property], request);
|
// .filter((property) => data._embedded.hasOwnProperty(property))
|
||||||
if (isNotEmpty(parsedObj)) {
|
// .forEach((property) => {
|
||||||
if (isRestPaginatedList(data._embedded[property])) {
|
// const parsedObj = this.process<ObjectDomain>(data._embedded[property], request);
|
||||||
object[property] = parsedObj;
|
// if (isNotEmpty(parsedObj)) {
|
||||||
object[property].page = parsedObj.page.map((obj) => this.retrieveObjectOrUrl(obj));
|
// if (isRestPaginatedList(data._embedded[property])) {
|
||||||
} else if (isRestDataObject(data._embedded[property])) {
|
// object[property] = parsedObj;
|
||||||
object[property] = this.retrieveObjectOrUrl(parsedObj);
|
// object[property].page = parsedObj.page.map((obj) => this.retrieveObjectOrUrl(obj));
|
||||||
} else if (Array.isArray(parsedObj)) {
|
// } else if (isRestDataObject(data._embedded[property])) {
|
||||||
object[property] = parsedObj.map((obj) => this.retrieveObjectOrUrl(obj))
|
// object[property] = this.retrieveObjectOrUrl(parsedObj);
|
||||||
}
|
// } else if (Array.isArray(parsedObj)) {
|
||||||
}
|
// object[property] = parsedObj.map((obj) => this.retrieveObjectOrUrl(obj))
|
||||||
});
|
// }
|
||||||
}
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
this.cache(object, request);
|
this.cache(object, request);
|
||||||
return object;
|
return object;
|
||||||
|
147
src/app/core/data/bitstream-data.service.ts
Normal file
147
src/app/core/data/bitstream-data.service.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
|
import { Bundle } from '../shared/bundle.model';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { Item } from '../shared/item.model';
|
||||||
|
import { BundleDataService } from './bundle-data.service';
|
||||||
|
import { CommunityDataService } from './community-data.service';
|
||||||
|
import { DataService } from './data.service';
|
||||||
|
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
||||||
|
import { PaginatedList } from './paginated-list';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
|
import { RemoteDataError } from './remote-data-error';
|
||||||
|
import { FindListOptions } from './request.models';
|
||||||
|
import { RequestService } from './request.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class BitstreamDataService extends DataService<Bitstream> {
|
||||||
|
|
||||||
|
protected linkPath = 'bitstreams';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected dataBuildService: NormalizedObjectBuildService,
|
||||||
|
protected store: Store<CoreState>,
|
||||||
|
protected cds: CommunityDataService,
|
||||||
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected halService: HALEndpointService,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
protected http: HttpClient,
|
||||||
|
protected comparator: DSOChangeAnalyzer<Bitstream>,
|
||||||
|
protected bundleService: BundleDataService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
|
||||||
|
// TODO needed? if not, perhaps remove it from datasevice?
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the bitstreams in a given bundle
|
||||||
|
*
|
||||||
|
* @param bundle the bundle to retrieve bitstreams from
|
||||||
|
* @param options options for the find all request
|
||||||
|
*/
|
||||||
|
findAllByBundle(bundle: Bundle, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||||
|
return this.findAllByHref(bundle._links.bitstreams.href, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the thumbnail for the given item
|
||||||
|
* @returns {Observable<RemoteData<Bitstream>>} the first bitstream in the THUMBNAIL bundle
|
||||||
|
*/
|
||||||
|
// TODO should be implemented rest side. Item should get a thumbnail link
|
||||||
|
public getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
|
||||||
|
return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe(
|
||||||
|
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||||
|
if (hasValue(bundleRD.payload)) {
|
||||||
|
return this.findAllByBundle(bundleRD.payload, { elementsPerPage: 1 }).pipe(
|
||||||
|
map((bitstreamRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||||
|
if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) {
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
bitstreamRD.payload.page[0]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return bitstreamRD as any;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [bundleRD as any];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO should be implemented rest side
|
||||||
|
public getMatchingThumbnail(item: Item, bitstreamInOriginal: Bitstream): Observable<RemoteData<Bitstream>> {
|
||||||
|
return this.bundleService.findByItemAndName(item, 'THUMBNAIL').pipe(
|
||||||
|
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||||
|
if (hasValue(bundleRD.payload)) {
|
||||||
|
return this.findAllByBundle(bundleRD.payload, { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||||
|
map((bitstreamRD: RemoteData<PaginatedList<Bitstream>>) => {
|
||||||
|
if (hasValue(bitstreamRD.payload) && hasValue(bitstreamRD.payload.page)) {
|
||||||
|
const matchingThumbnail = bitstreamRD.payload.page.find((thumbnail: Bitstream) =>
|
||||||
|
thumbnail.name.startsWith(bitstreamInOriginal.name)
|
||||||
|
);
|
||||||
|
if (hasValue(matchingThumbnail)) {
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
matchingThumbnail
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new RemoteDataError(404, '404', 'No matching thumbnail found'),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return bitstreamRD as any;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [bundleRD as any];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public findAllByItemAndBundleName(item: Item, bundleName: string, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||||
|
return this.bundleService.findByItemAndName(item, bundleName).pipe(
|
||||||
|
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||||
|
if (hasValue(bundleRD.payload)) {
|
||||||
|
return this.findAllByBundle(bundleRD.payload, options);
|
||||||
|
} else {
|
||||||
|
return [bundleRD as any];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,6 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { BitstreamFormat } from '../shared/bitstream-format.model';
|
import { BitstreamFormat } from '../shared/bitstream-format.model';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
@@ -183,4 +185,8 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
|
|||||||
map((request: RequestEntry) => request.response.isSuccessful)
|
map((request: RequestEntry) => request.response.isSuccessful)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findByBitstream(bitstream: Bitstream): Observable<RemoteData<BitstreamFormat>> {
|
||||||
|
return this.findByHref(bitstream._links.format.href);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,31 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { DataService } from './data.service';
|
|
||||||
import { Bundle } from '../shared/bundle.model';
|
|
||||||
import { RequestService } from './request.service';
|
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
|
||||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { CoreState } from '../core.reducers';
|
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
import { Injectable } from '@angular/core';
|
||||||
import { FindListOptions } from './request.models';
|
import { Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
|
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { CoreState } from '../core.reducers';
|
||||||
|
import { Bundle } from '../shared/bundle.model';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { Item } from '../shared/item.model';
|
||||||
|
import { BitstreamDataService } from './bitstream-data.service';
|
||||||
|
import { DataService } from './data.service';
|
||||||
|
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||||
|
import { PaginatedList } from './paginated-list';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
|
import { FindListOptions } from './request.models';
|
||||||
|
import { RequestService } from './request.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service responsible for fetching/sending data from/to the REST API on the bundles endpoint
|
* A service responsible for fetching/sending data from/to the REST API on the bundles endpoint
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable(
|
||||||
|
{providedIn: 'root'}
|
||||||
|
)
|
||||||
export class BundleDataService extends DataService<Bundle> {
|
export class BundleDataService extends DataService<Bundle> {
|
||||||
protected linkPath = 'bundles';
|
protected linkPath = 'bundles';
|
||||||
protected forceBypassCache = false;
|
protected forceBypassCache = false;
|
||||||
@@ -43,4 +51,29 @@ export class BundleDataService extends DataService<Bundle> {
|
|||||||
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
||||||
return this.halService.getEndpoint(this.linkPath);
|
return this.halService.getEndpoint(this.linkPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findAllByItem(item: Item, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bundle>>> {
|
||||||
|
return this.findAllByHref(item._links.bundles.href, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO should be implemented rest side
|
||||||
|
findByItemAndName(item: Item, bundleName: string): Observable<RemoteData<Bundle>> {
|
||||||
|
return this.findAllByItem(item, { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||||
|
map((rd: RemoteData<PaginatedList<Bundle>>) => {
|
||||||
|
if (hasValue(rd.payload) && hasValue(rd.payload.page)) {
|
||||||
|
const matchingBundle = rd.payload.page.find((bundle: Bundle) =>
|
||||||
|
bundle.name === bundleName);
|
||||||
|
return new RemoteData(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
undefined,
|
||||||
|
matchingBundle
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return rd as any;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
|
|||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { Collection } from '../shared/collection.model';
|
import { Collection } from '../shared/collection.model';
|
||||||
|
import { Item } from '../shared/item.model';
|
||||||
import { ComColDataService } from './comcol-data.service';
|
import { ComColDataService } from './comcol-data.service';
|
||||||
import { CommunityDataService } from './community-data.service';
|
import { CommunityDataService } from './community-data.service';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
@@ -240,4 +241,9 @@ export class CollectionDataService extends ComColDataService<Collection> {
|
|||||||
this.halService.getEndpoint('collections', `${communityEndpointHref}/${parentUUID}`)),
|
this.halService.getEndpoint('collections', `${communityEndpointHref}/${parentUUID}`)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findOwningCollectionFor(item: Item): Observable<RemoteData<Collection>> {
|
||||||
|
return this.findByHref(item._links.owningCollection.href);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -81,7 +81,9 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
|
|||||||
protected abstract getFindByParentHref(parentUUID: string): Observable<string>;
|
protected abstract getFindByParentHref(parentUUID: string): Observable<string>;
|
||||||
|
|
||||||
public findByParent(parentUUID: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
public findByParent(parentUUID: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
const href$ = this.buildHrefFromFindOptions(this.getFindByParentHref(parentUUID), [], options);
|
const href$ = this.getFindByParentHref(parentUUID).pipe(
|
||||||
|
map((href: string) => this.buildHrefFromFindOptions(href, options))
|
||||||
|
);
|
||||||
return this.findList(href$, options);
|
return this.findList(href$, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -76,12 +76,12 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
* Return an observable that emits created HREF
|
* Return an observable that emits created HREF
|
||||||
*/
|
*/
|
||||||
protected getFindAllHref(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
protected getFindAllHref(options: FindListOptions = {}, linkPath?: string): Observable<string> {
|
||||||
let result: Observable<string>;
|
let result$: Observable<string>;
|
||||||
const args = [];
|
const args = [];
|
||||||
|
|
||||||
result = this.getBrowseEndpoint(options, linkPath).pipe(distinctUntilChanged());
|
result$ = this.getBrowseEndpoint(options, linkPath).pipe(distinctUntilChanged());
|
||||||
|
|
||||||
return this.buildHrefFromFindOptions(result, args, options);
|
return result$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,10 +93,10 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
* Return an observable that emits created HREF
|
* Return an observable that emits created HREF
|
||||||
*/
|
*/
|
||||||
protected getSearchByHref(searchMethod: string, options: FindListOptions = {}): Observable<string> {
|
protected getSearchByHref(searchMethod: string, options: FindListOptions = {}): Observable<string> {
|
||||||
let result: Observable<string>;
|
let result$: Observable<string>;
|
||||||
const args = [];
|
const args = [];
|
||||||
|
|
||||||
result = this.getSearchEndpoint(searchMethod);
|
result$ = this.getSearchEndpoint(searchMethod);
|
||||||
|
|
||||||
if (hasValue(options.searchParams)) {
|
if (hasValue(options.searchParams)) {
|
||||||
options.searchParams.forEach((param: SearchParam) => {
|
options.searchParams.forEach((param: SearchParam) => {
|
||||||
@@ -104,37 +104,39 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.buildHrefFromFindOptions(result, args, options);
|
return result$.pipe(map((result: string) => this.buildHrefFromFindOptions(result, options, args)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn an options object into a query string and combine it with the given HREF
|
* Turn an options object into a query string and combine it with the given HREF
|
||||||
*
|
*
|
||||||
* @param href$ The HREF to which the query string should be appended
|
* @param href The HREF to which the query string should be appended
|
||||||
* @param args Array with additional params to combine with query string
|
|
||||||
* @param options The [[FindListOptions]] object
|
* @param options The [[FindListOptions]] object
|
||||||
|
* @param extraArgs Array with additional params to combine with query string
|
||||||
* @return {Observable<string>}
|
* @return {Observable<string>}
|
||||||
* Return an observable that emits created HREF
|
* Return an observable that emits created HREF
|
||||||
*/
|
*/
|
||||||
protected buildHrefFromFindOptions(href$: Observable<string>, args: string[], options: FindListOptions): Observable<string> {
|
protected buildHrefFromFindOptions(href: string, options: FindListOptions, extraArgs: string[] = []): string {
|
||||||
|
|
||||||
|
let args = [...extraArgs];
|
||||||
|
|
||||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
||||||
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
|
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
|
||||||
args.push(`page=${options.currentPage - 1}`);
|
args = [...args, `page=${options.currentPage - 1}`];
|
||||||
}
|
}
|
||||||
if (hasValue(options.elementsPerPage)) {
|
if (hasValue(options.elementsPerPage)) {
|
||||||
args.push(`size=${options.elementsPerPage}`);
|
args = [...args, `size=${options.elementsPerPage}`];
|
||||||
}
|
}
|
||||||
if (hasValue(options.sort)) {
|
if (hasValue(options.sort)) {
|
||||||
args.push(`sort=${options.sort.field},${options.sort.direction}`);
|
args = [...args, `sort=${options.sort.field},${options.sort.direction}`];
|
||||||
}
|
}
|
||||||
if (hasValue(options.startsWith)) {
|
if (hasValue(options.startsWith)) {
|
||||||
args.push(`startsWith=${options.startsWith}`);
|
args = [...args, `startsWith=${options.startsWith}`];
|
||||||
}
|
}
|
||||||
if (isNotEmpty(args)) {
|
if (isNotEmpty(args)) {
|
||||||
return href$.pipe(map((href: string) => new URLCombiner(href, `?${args.join('&')}`).toString()));
|
return new URLCombiner(href, `?${args.join('&')}`).toString();
|
||||||
} else {
|
} else {
|
||||||
return href$;
|
return href;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +185,9 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
return this.rdbService.buildSingle<T>(hrefObs);
|
return this.rdbService.buildSingle<T>(hrefObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
findByHref(href: string, options?: HttpOptions): Observable<RemoteData<T>> {
|
findByHref(href: string, findListOptions: FindListOptions = {}, httpOptions?: HttpOptions): Observable<RemoteData<T>> {
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), href, null, options);
|
const requestHref = this.buildHrefFromFindOptions(href, findListOptions, []);
|
||||||
|
const request = new GetRequest(this.requestService.generateRequestId(), requestHref, null, httpOptions);
|
||||||
if (hasValue(this.responseMsToLive)) {
|
if (hasValue(this.responseMsToLive)) {
|
||||||
request.responseMsToLive = this.responseMsToLive;
|
request.responseMsToLive = this.responseMsToLive;
|
||||||
}
|
}
|
||||||
@@ -192,6 +195,16 @@ export abstract class DataService<T extends CacheableObject> {
|
|||||||
return this.rdbService.buildSingle<T>(href);
|
return this.rdbService.buildSingle<T>(href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findAllByHref(href: string, findListOptions: FindListOptions = {}, httpOptions?: HttpOptions): Observable<RemoteData<PaginatedList<T>>> {
|
||||||
|
const requestHref = this.buildHrefFromFindOptions(href, findListOptions, []);
|
||||||
|
const request = new GetRequest(this.requestService.generateRequestId(), requestHref, null, httpOptions);
|
||||||
|
if (hasValue(this.responseMsToLive)) {
|
||||||
|
request.responseMsToLive = this.responseMsToLive;
|
||||||
|
}
|
||||||
|
this.requestService.configure(request);
|
||||||
|
return this.rdbService.buildList<T>(requestHref);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return object search endpoint by given search method
|
* Return object search endpoint by given search method
|
||||||
*
|
*
|
||||||
|
@@ -29,7 +29,7 @@ import {
|
|||||||
configureRequest,
|
configureRequest,
|
||||||
filterSuccessfulResponses,
|
filterSuccessfulResponses,
|
||||||
getRequestFromRequestHref,
|
getRequestFromRequestHref,
|
||||||
getResponseFromEntry
|
getResponseFromEntry, getSucceededRemoteData
|
||||||
} from '../shared/operators';
|
} from '../shared/operators';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { GenericSuccessResponse, RestResponse } from '../cache/response.models';
|
import { GenericSuccessResponse, RestResponse } from '../cache/response.models';
|
||||||
@@ -53,7 +53,8 @@ export class ItemDataService extends DataService<Item> {
|
|||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
protected notificationsService: NotificationsService,
|
protected notificationsService: NotificationsService,
|
||||||
protected http: HttpClient,
|
protected http: HttpClient,
|
||||||
protected comparator: DSOChangeAnalyzer<Item>) {
|
protected comparator: DSOChangeAnalyzer<Item>,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -178,11 +178,11 @@ export class RelationshipService extends DataService<Relationship> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an item its relationships in the form of an array
|
* Get an item's relationships in the form of an array
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
getItemRelationshipsArray(item: Item): Observable<Relationship[]> {
|
getItemRelationshipsArray(item: Item): Observable<Relationship[]> {
|
||||||
return item.relationships.pipe(
|
return this.findAllByHref(item._links.relationships.href).pipe(
|
||||||
getSucceededRemoteData(),
|
getSucceededRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
map((rels: PaginatedList<Relationship>) => rels.page),
|
map((rels: PaginatedList<Relationship>) => rels.page),
|
||||||
|
@@ -61,6 +61,6 @@ export class ResourcePolicyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findByHref(href: string, options?: HttpOptions): Observable<RemoteData<ResourcePolicy>> {
|
findByHref(href: string, options?: HttpOptions): Observable<RemoteData<ResourcePolicy>> {
|
||||||
return this.dataService.findByHref(href, options);
|
return this.dataService.findByHref(href, {}, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +1,28 @@
|
|||||||
import {
|
|
||||||
catchError,
|
|
||||||
distinctUntilKeyChanged,
|
|
||||||
filter,
|
|
||||||
first,
|
|
||||||
map,
|
|
||||||
take
|
|
||||||
} from 'rxjs/operators';
|
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { Meta, MetaDefinition, Title } from '@angular/platform-browser';
|
import { Meta, MetaDefinition, Title } from '@angular/platform-browser';
|
||||||
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { catchError, distinctUntilKeyChanged, filter, first, map, take } from 'rxjs/operators';
|
||||||
import { RemoteData } from '../data/remote-data';
|
|
||||||
import { Bitstream } from '../shared/bitstream.model';
|
|
||||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
|
||||||
import { Item } from '../shared/item.model';
|
|
||||||
|
|
||||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||||
import { BitstreamFormat } from '../shared/bitstream-format.model';
|
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
|
import { BitstreamDataService } from '../data/bitstream-data.service';
|
||||||
|
import { BitstreamFormatDataService } from '../data/bitstream-format-data.service';
|
||||||
|
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { BitstreamFormat } from '../shared/bitstream-format.model';
|
||||||
|
import { Bitstream } from '../shared/bitstream.model';
|
||||||
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
|
import { Item } from '../shared/item.model';
|
||||||
|
import {
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getFirstSucceededRemoteListPayload
|
||||||
|
} from '../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetadataService {
|
export class MetadataService {
|
||||||
@@ -39,6 +38,8 @@ export class MetadataService {
|
|||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private meta: Meta,
|
private meta: Meta,
|
||||||
private title: Title,
|
private title: Title,
|
||||||
|
private bitstreamDataService: BitstreamDataService,
|
||||||
|
private bitstreamFormatDataService: BitstreamFormatDataService,
|
||||||
@Inject(GLOBAL_CONFIG) private envConfig: GlobalConfig
|
@Inject(GLOBAL_CONFIG) private envConfig: GlobalConfig
|
||||||
) {
|
) {
|
||||||
// TODO: determine what open graph meta tags are needed and whether
|
// TODO: determine what open graph meta tags are needed and whether
|
||||||
@@ -266,8 +267,9 @@ export class MetadataService {
|
|||||||
private setCitationPdfUrlTag(): void {
|
private setCitationPdfUrlTag(): void {
|
||||||
if (this.currentObject.value instanceof Item) {
|
if (this.currentObject.value instanceof Item) {
|
||||||
const item = this.currentObject.value as Item;
|
const item = this.currentObject.value as Item;
|
||||||
item.getFiles()
|
this.bitstreamDataService.findAllByItemAndBundleName(item, 'ORIGINAL')
|
||||||
.pipe(
|
.pipe(
|
||||||
|
getFirstSucceededRemoteListPayload(),
|
||||||
first((files) => isNotEmpty(files)),
|
first((files) => isNotEmpty(files)),
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
console.debug(error.message);
|
console.debug(error.message);
|
||||||
@@ -275,17 +277,11 @@ export class MetadataService {
|
|||||||
}))
|
}))
|
||||||
.subscribe((bitstreams: Bitstream[]) => {
|
.subscribe((bitstreams: Bitstream[]) => {
|
||||||
for (const bitstream of bitstreams) {
|
for (const bitstream of bitstreams) {
|
||||||
bitstream.format.pipe(
|
this.bitstreamFormatDataService.findByBitstream(bitstream).pipe(
|
||||||
first(),
|
getFirstSucceededRemoteDataPayload()
|
||||||
catchError((error: Error) => {
|
).subscribe((format: BitstreamFormat) => {
|
||||||
console.debug(error.message);
|
|
||||||
return []
|
|
||||||
}),
|
|
||||||
map((rd: RemoteData<BitstreamFormat>) => rd.payload),
|
|
||||||
filter((format: BitstreamFormat) => hasValue(format)))
|
|
||||||
.subscribe((format: BitstreamFormat) => {
|
|
||||||
if (format.mimetype === 'application/pdf') {
|
if (format.mimetype === 'application/pdf') {
|
||||||
this.addMetaTag('citation_pdf_url', bitstream.content);
|
this.addMetaTag('citation_pdf_url', bitstream._links.content.href);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
5
src/app/core/shared/HALLink.model.ts
Normal file
5
src/app/core/shared/HALLink.model.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export class HALLink {
|
||||||
|
href: string;
|
||||||
|
name?: string;
|
||||||
|
templated?: boolean
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
import { DSpaceObject } from './dspace-object.model';
|
|
||||||
import { RemoteData } from '../data/remote-data';
|
|
||||||
import { Item } from './item.model';
|
|
||||||
import { BitstreamFormat } from './bitstream-format.model';
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { RemoteData } from '../data/remote-data';
|
||||||
|
import { BitstreamFormat } from './bitstream-format.model';
|
||||||
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
|
import { HALLink } from './HALLink.model';
|
||||||
import { ResourceType } from './resource-type';
|
import { ResourceType } from './resource-type';
|
||||||
|
|
||||||
export class Bitstream extends DSpaceObject {
|
export class Bitstream extends DSpaceObject {
|
||||||
@@ -24,22 +24,24 @@ export class Bitstream extends DSpaceObject {
|
|||||||
bundleName: string;
|
bundleName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of Bitstream Format of this Bitstream
|
* The Thumbnail for this Bitstream
|
||||||
*/
|
*/
|
||||||
format: Observable<RemoteData<BitstreamFormat>>;
|
thumbnail?: Observable<RemoteData<Bitstream>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of Items that are direct parents of this Bitstream
|
* The Bitstream Format for this Bitstream
|
||||||
*/
|
*/
|
||||||
parents: Observable<RemoteData<Item[]>>;
|
format?: Observable<RemoteData<BitstreamFormat>>;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Bundle that owns this Bitstream
|
|
||||||
*/
|
|
||||||
owner: Observable<RemoteData<Item>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL to retrieve this Bitstream's file
|
* The URL to retrieve this Bitstream's file
|
||||||
*/
|
*/
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
bundle: HALLink;
|
||||||
|
content: HALLink;
|
||||||
|
format: HALLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
import { DSpaceObject } from './dspace-object.model';
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
import { Bitstream } from './bitstream.model';
|
import { HALLink } from './HALLink.model';
|
||||||
import { Item } from './item.model';
|
|
||||||
import { RemoteData } from '../data/remote-data';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ResourceType } from './resource-type';
|
import { ResourceType } from './resource-type';
|
||||||
import { PaginatedList } from '../data/paginated-list';
|
|
||||||
|
|
||||||
export class Bundle extends DSpaceObject {
|
export class Bundle extends DSpaceObject {
|
||||||
static type = new ResourceType('bundle');
|
static type = new ResourceType('bundle');
|
||||||
@@ -14,24 +10,11 @@ export class Bundle extends DSpaceObject {
|
|||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
/**
|
_links: {
|
||||||
* The primary bitstream of this Bundle
|
self: HALLink;
|
||||||
*/
|
primaryBitstream: HALLink;
|
||||||
primaryBitstream: Observable<RemoteData<Bitstream>>;
|
parents: HALLink;
|
||||||
|
owner: HALLink;
|
||||||
/**
|
bitstreams: HALLink;
|
||||||
* An array of Items that are direct parents of this Bundle
|
}
|
||||||
*/
|
|
||||||
parents: Observable<RemoteData<Item[]>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Item that owns this Bundle
|
|
||||||
*/
|
|
||||||
owner: Observable<RemoteData<Item>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Bitstreams that are part of this Bundle
|
|
||||||
*/
|
|
||||||
bitstreams: Observable<RemoteData<PaginatedList<Bitstream>>>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { DSpaceObject } from './dspace-object.model';
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
import { Bitstream } from './bitstream.model';
|
import { Bitstream } from './bitstream.model';
|
||||||
|
import { HALLink } from './HALLink.model';
|
||||||
import { Item } from './item.model';
|
import { Item } from './item.model';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -82,4 +83,13 @@ export class Collection extends DSpaceObject {
|
|||||||
owner: Observable<RemoteData<Collection>>;
|
owner: Observable<RemoteData<Collection>>;
|
||||||
|
|
||||||
items: Observable<RemoteData<Item[]>>;
|
items: Observable<RemoteData<Item[]>>;
|
||||||
|
|
||||||
|
_links: {
|
||||||
|
license: HALLink;
|
||||||
|
harvester: HALLink;
|
||||||
|
mappedItems: HALLink;
|
||||||
|
defaultAccessConditions: HALLink;
|
||||||
|
logo: HALLink;
|
||||||
|
self: HALLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,11 @@
|
|||||||
import { map, startWith, filter, switchMap } from 'rxjs/operators';
|
import { isEmpty } from '../../shared/empty.util';
|
||||||
import { Observable } from 'rxjs';
|
import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator';
|
||||||
|
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||||
|
|
||||||
import { DSpaceObject } from './dspace-object.model';
|
import { DSpaceObject } from './dspace-object.model';
|
||||||
import { Collection } from './collection.model';
|
|
||||||
import { RemoteData } from '../data/remote-data';
|
|
||||||
import { Bitstream } from './bitstream.model';
|
|
||||||
import { hasValueOperator, isNotEmpty, isEmpty } from '../../shared/empty.util';
|
|
||||||
import { PaginatedList } from '../data/paginated-list';
|
|
||||||
import { Relationship } from './item-relationships/relationship.model';
|
|
||||||
import { ResourceType } from './resource-type';
|
|
||||||
import { getAllSucceededRemoteData, getSucceededRemoteData } from './operators';
|
|
||||||
import { Bundle } from './bundle.model';
|
|
||||||
import { GenericConstructor } from './generic-constructor';
|
import { GenericConstructor } from './generic-constructor';
|
||||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
import { HALLink } from './HALLink.model';
|
||||||
import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator';
|
import { ResourceType } from './resource-type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing a DSpace Item
|
* Class representing a DSpace Item
|
||||||
@@ -46,77 +38,13 @@ export class Item extends DSpaceObject {
|
|||||||
*/
|
*/
|
||||||
isWithdrawn: boolean;
|
isWithdrawn: boolean;
|
||||||
|
|
||||||
/**
|
_links: {
|
||||||
* An array of Collections that are direct parents of this Item
|
self: HALLink;
|
||||||
*/
|
parents: HALLink;
|
||||||
parents: Observable<RemoteData<Collection[]>>;
|
owningCollection: HALLink;
|
||||||
|
bundles: HALLink;
|
||||||
/**
|
relationships: HALLink;
|
||||||
* The Collection that owns this Item
|
};
|
||||||
*/
|
|
||||||
owningCollection: Observable<RemoteData<Collection>>;
|
|
||||||
|
|
||||||
get owner(): Observable<RemoteData<Collection>> {
|
|
||||||
return this.owningCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bitstream bundles within this item
|
|
||||||
*/
|
|
||||||
bundles: Observable<RemoteData<PaginatedList<Bundle>>>;
|
|
||||||
|
|
||||||
relationships: Observable<RemoteData<PaginatedList<Relationship>>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the thumbnail of this item
|
|
||||||
* @returns {Observable<Bitstream>} the primaryBitstream of the 'THUMBNAIL' bundle
|
|
||||||
*/
|
|
||||||
getThumbnail(): Observable<Bitstream> {
|
|
||||||
// TODO: currently this just picks the first thumbnail
|
|
||||||
// should be adjusted when we have a way to determine
|
|
||||||
// the primary thumbnail from rest
|
|
||||||
return this.getBitstreamsByBundleName('THUMBNAIL').pipe(
|
|
||||||
filter((thumbnails) => isNotEmpty(thumbnails)),
|
|
||||||
map((thumbnails) => thumbnails[0]),)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the thumbnail for the given original of this item
|
|
||||||
* @returns {Observable<Bitstream>} the primaryBitstream of the 'THUMBNAIL' bundle
|
|
||||||
*/
|
|
||||||
getThumbnailForOriginal(original: Bitstream): Observable<Bitstream> {
|
|
||||||
return this.getBitstreamsByBundleName('THUMBNAIL').pipe(
|
|
||||||
map((files) => {
|
|
||||||
return files.find((thumbnail) => thumbnail.name.startsWith(original.name))
|
|
||||||
}),startWith(undefined),);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all files that should be displayed on the item page of this item
|
|
||||||
* @returns {Observable<Array<Observable<Bitstream>>>} an array of all Bitstreams in the 'ORIGINAL' bundle
|
|
||||||
*/
|
|
||||||
getFiles(): Observable<Bitstream[]> {
|
|
||||||
return this.getBitstreamsByBundleName('ORIGINAL');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves bitstreams by bundle name
|
|
||||||
* @param bundleName The name of the Bundle that should be returned
|
|
||||||
* @returns {Observable<Bitstream[]>} the bitstreams with the given bundleName
|
|
||||||
* TODO now that bitstreams can be paginated this should move to the server
|
|
||||||
* see https://github.com/DSpace/dspace-angular/issues/332
|
|
||||||
*/
|
|
||||||
getBitstreamsByBundleName(bundleName: string): Observable<Bitstream[]> {
|
|
||||||
return this.bundles.pipe(
|
|
||||||
getSucceededRemoteData(),
|
|
||||||
map((rd: RemoteData<PaginatedList<Bundle>>) => rd.payload.page.find((bundle: Bundle) => bundle.name === bundleName)),
|
|
||||||
hasValueOperator(),
|
|
||||||
switchMap((bundle: Bundle) => bundle.bitstreams),
|
|
||||||
getAllSucceededRemoteData(),
|
|
||||||
map((rd: RemoteData<PaginatedList<Bitstream>>) => rd.payload.page),
|
|
||||||
startWith([])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that returns as which type of object this object should be rendered
|
* Method that returns as which type of object this object should be rendered
|
||||||
|
@@ -59,10 +59,92 @@ export const getRemoteDataPayload = () =>
|
|||||||
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
||||||
source.pipe(map((remoteData: RemoteData<T>) => remoteData.payload));
|
source.pipe(map((remoteData: RemoteData<T>) => remoteData.payload));
|
||||||
|
|
||||||
|
export const getPaginatedListPayload = () =>
|
||||||
|
<T>(source: Observable<PaginatedList<T>>): Observable<T[]> =>
|
||||||
|
source.pipe(map((list: PaginatedList<T>) => list.page));
|
||||||
|
|
||||||
export const getSucceededRemoteData = () =>
|
export const getSucceededRemoteData = () =>
|
||||||
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
<T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
|
||||||
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded));
|
source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first successful remotely retrieved object
|
||||||
|
*
|
||||||
|
* You usually don't want to use this, it is a code smell.
|
||||||
|
* Work with the RemoteData object instead, that way you can
|
||||||
|
* handle loading and errors correctly.
|
||||||
|
*
|
||||||
|
* These operators were created as a first step in refactoring
|
||||||
|
* out all the instances where this is used incorrectly.
|
||||||
|
*/
|
||||||
|
export const getFirstSucceededRemoteDataPayload = () =>
|
||||||
|
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
||||||
|
source.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the all successful remotely retrieved objects
|
||||||
|
*
|
||||||
|
* You usually don't want to use this, it is a code smell.
|
||||||
|
* Work with the RemoteData object instead, that way you can
|
||||||
|
* handle loading and errors correctly.
|
||||||
|
*
|
||||||
|
* These operators were created as a first step in refactoring
|
||||||
|
* out all the instances where this is used incorrectly.
|
||||||
|
*/
|
||||||
|
export const getAllSucceededRemoteDataPayload = () =>
|
||||||
|
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
||||||
|
source.pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first successful remotely retrieved paginated list
|
||||||
|
* as an array
|
||||||
|
*
|
||||||
|
* You usually don't want to use this, it is a code smell.
|
||||||
|
* Work with the RemoteData object instead, that way you can
|
||||||
|
* handle loading and errors correctly.
|
||||||
|
*
|
||||||
|
* You also don't want to ignore pagination and simply use the
|
||||||
|
* page as an array.
|
||||||
|
*
|
||||||
|
* These operators were created as a first step in refactoring
|
||||||
|
* out all the instances where this is used incorrectly.
|
||||||
|
*/
|
||||||
|
export const getFirstSucceededRemoteListPayload = () =>
|
||||||
|
<T>(source: Observable<RemoteData<PaginatedList<T>>>): Observable<T[]> =>
|
||||||
|
source.pipe(
|
||||||
|
getSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
getPaginatedListPayload()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all successful remotely retrieved paginated lists
|
||||||
|
* as arrays
|
||||||
|
*
|
||||||
|
* You usually don't want to use this, it is a code smell.
|
||||||
|
* Work with the RemoteData object instead, that way you can
|
||||||
|
* handle loading and errors correctly.
|
||||||
|
*
|
||||||
|
* You also don't want to ignore pagination and simply use the
|
||||||
|
* page as an array.
|
||||||
|
*
|
||||||
|
* These operators were created as a first step in refactoring
|
||||||
|
* out all the instances where this is used incorrectly.
|
||||||
|
*/
|
||||||
|
export const getAllSucceededRemoteListPayload = () =>
|
||||||
|
<T>(source: Observable<RemoteData<PaginatedList<T>>>): Observable<T[]> =>
|
||||||
|
source.pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
getPaginatedListPayload()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operator that checks if a remote data object contains a page not found error
|
* Operator that checks if a remote data object contains a page not found error
|
||||||
* When it does contain such an error, it will redirect the user to a page not found, without altering the current URL
|
* When it does contain such an error, it will redirect the user to a page not found, without altering the current URL
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-generic-item-page-field [item]="object"
|
<ds-generic-item-page-field [item]="object"
|
||||||
[fields]="['publicationvolume.volumeNumber']"
|
[fields]="['publicationvolume.volumeNumber']"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-generic-item-page-field [item]="object"
|
<ds-generic-item-page-field [item]="object"
|
||||||
[fields]="['publicationvolume.volumeNumber']"
|
[fields]="['publicationvolume.volumeNumber']"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
<ds-generic-item-page-field class="item-page-fields" [item]="object"
|
||||||
[fields]="['creativeworkseries.issn']"
|
[fields]="['creativeworkseries.issn']"
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -3,13 +3,13 @@
|
|||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
|
||||||
class="card-img-top full-width">
|
class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
|
||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-generic-item-page-field [item]="object"
|
<ds-generic-item-page-field [item]="object"
|
||||||
[fields]="['organization.foundingDate']"
|
[fields]="['organization.foundingDate']"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="this.object.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<ds-generic-item-page-field [item]="object"
|
<ds-generic-item-page-field [item]="object"
|
||||||
[fields]="['person.email']"
|
[fields]="['person.email']"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async" [defaultImage]="'assets/images/project-placeholder.svg'"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/project-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<!--<ds-generic-item-page-field [item]="object"-->
|
<!--<ds-generic-item-page-field [item]="object"-->
|
||||||
<!--[fields]="['project.identifier.status']"-->
|
<!--[fields]="['project.identifier.status']"-->
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<div *ngIf="logo" class="dso-logo mb-3">
|
<div *ngIf="logo" class="dso-logo mb-3">
|
||||||
<img [src]="logo.content" class="img-fluid" [attr.alt]="alternateText ? alternateText : null" (error)="errorHandler($event)"/>
|
<img [src]="logo._links.content.href" class="img-fluid" [attr.alt]="alternateText ? alternateText : null" (error)="errorHandler($event)"/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,9 +1,18 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
|
|
||||||
import { Item } from '../../../../core/shared/item.model';
|
import { Item } from '../../../../core/shared/item.model';
|
||||||
|
import {
|
||||||
|
getAllSucceededRemoteListPayload,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
getFirstSucceededRemoteListPayload,
|
||||||
|
getRemoteDataPayload,
|
||||||
|
getSucceededRemoteData
|
||||||
|
} from '../../../../core/shared/operators';
|
||||||
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
|
||||||
import { fadeInOut } from '../../../animations/fade';
|
import { fadeInOut } from '../../../animations/fade';
|
||||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
@@ -64,15 +73,16 @@ export class ItemDetailPreviewComponent {
|
|||||||
* @param {HALEndpointService} halService
|
* @param {HALEndpointService} halService
|
||||||
*/
|
*/
|
||||||
constructor(private fileService: FileService,
|
constructor(private fileService: FileService,
|
||||||
private halService: HALEndpointService) {
|
private halService: HALEndpointService,
|
||||||
|
private bitstreamDataService: BitstreamDataService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all instance variables
|
* Initialize all instance variables
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.thumbnail$ = this.item.getThumbnail();
|
this.thumbnail$ = this.getThumbnail();
|
||||||
this.bitstreams$ = this.item.getFiles();
|
this.bitstreams$ = this.getFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,4 +96,20 @@ export class ItemDetailPreviewComponent {
|
|||||||
this.fileService.downloadFile(fileUrl);
|
this.fileService.downloadFile(fileUrl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO refactor this method to return RemoteData, and the template to deal with loading and errors
|
||||||
|
getThumbnail(): Observable<Bitstream> {
|
||||||
|
return this.bitstreamDataService.getThumbnailFor(this.item).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO refactor this method to return RemoteData, and the template to deal with loading and errors
|
||||||
|
getFiles(): Observable<Bitstream[]> {
|
||||||
|
return this.bitstreamDataService
|
||||||
|
.findAllByItemAndBundleName(this.item, 'ORIGINAL', { elementsPerPage: Number.MAX_SAFE_INTEGER })
|
||||||
|
.pipe(
|
||||||
|
getFirstSucceededRemoteListPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,8 +30,8 @@ export class GridThumbnailComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (hasValue(this.thumbnail) && this.thumbnail.content) {
|
if (hasValue(this.thumbnail) && this.thumbnail._links.content.href) {
|
||||||
this.src = this.thumbnail.content;
|
this.src = this.thumbnail._links.content.href;
|
||||||
} else {
|
} else {
|
||||||
this.src = this.defaultImage
|
this.src = this.defaultImage
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,13 @@
|
|||||||
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
|
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
|
||||||
class="card-img-top full-width">
|
class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="this.dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
|
||||||
<div>
|
<div>
|
||||||
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
|
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
|
||||||
</ds-grid-thumbnail>
|
</ds-grid-thumbnail>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { SearchResult } from '../../search/search-result.model';
|
import { SearchResult } from '../../search/search-result.model';
|
||||||
|
import { BitstreamDataService } from '../../../core/data/bitstream-data.service';
|
||||||
|
import { Bitstream } from '../../../core/shared/bitstream.model';
|
||||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
|
import { Metadata } from '../../../core/shared/metadata.utils';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
import { 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 { TruncatableService } from '../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../truncatable/truncatable.service';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Metadata } from '../../../core/shared/metadata.utils';
|
|
||||||
import { hasValue } from '../../empty.util';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-result-grid-element',
|
selector: 'ds-search-result-grid-element',
|
||||||
@@ -24,7 +27,10 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
|||||||
*/
|
*/
|
||||||
isCollapsed$: Observable<boolean>;
|
isCollapsed$: Observable<boolean>;
|
||||||
|
|
||||||
public constructor(protected truncatableService: TruncatableService) {
|
public constructor(
|
||||||
|
protected truncatableService: TruncatableService,
|
||||||
|
protected bitstreamDataService: BitstreamDataService
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
if (hasValue(this.object)) {
|
if (hasValue(this.object)) {
|
||||||
this.isCollapsed$ = this.isCollapsed();
|
this.isCollapsed$ = this.isCollapsed();
|
||||||
@@ -63,4 +69,11 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
|
|||||||
private isCollapsed(): Observable<boolean> {
|
private isCollapsed(): Observable<boolean> {
|
||||||
return this.truncatableService.isCollapsed(this.dso.id);
|
return this.truncatableService.isCollapsed(this.dso.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||||
|
getThumbnail(): Observable<Bitstream> {
|
||||||
|
return this.bitstreamDataService.getThumbnailFor(this.dso as any).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,22 @@ import { hasValue } from '../shared/empty.util';
|
|||||||
styleUrls: ['./thumbnail.component.scss'],
|
styleUrls: ['./thumbnail.component.scss'],
|
||||||
templateUrl: './thumbnail.component.html'
|
templateUrl: './thumbnail.component.html'
|
||||||
})
|
})
|
||||||
export class ThumbnailComponent implements OnInit {
|
export class ThumbnailComponent {
|
||||||
|
|
||||||
@Input() thumbnail: Bitstream;
|
private _thumbnail: Bitstream;
|
||||||
|
|
||||||
|
get thumbnail(): Bitstream {
|
||||||
|
return this._thumbnail;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Input() set thumbnail(t: Bitstream) {
|
||||||
|
this._thumbnail = t;
|
||||||
|
if (hasValue(this.thumbnail) && hasValue(this.thumbnail._links.content) && this.thumbnail._links.content.href) {
|
||||||
|
this.src = this.thumbnail._links.content.href;
|
||||||
|
} else {
|
||||||
|
this.src = this.defaultImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default 'holder.js' image
|
* The default 'holder.js' image
|
||||||
@@ -23,16 +36,8 @@ export class ThumbnailComponent implements OnInit {
|
|||||||
@Input() defaultImage? = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23FFFFFF%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
|
@Input() defaultImage? = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23FFFFFF%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
|
||||||
|
|
||||||
src: string;
|
src: string;
|
||||||
|
|
||||||
errorHandler(event) {
|
errorHandler(event) {
|
||||||
event.currentTarget.src = this.defaultImage;
|
event.currentTarget.src = this.defaultImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
if (hasValue(this.thumbnail) && this.thumbnail.content) {
|
|
||||||
this.src = this.thumbnail.content;
|
|
||||||
} else {
|
|
||||||
this.src = this.defaultImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ a<div class="top-item-page">
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-secondary"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-secondary"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-secondary"
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-secondary"
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"
|
||||||
[defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
[defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"
|
||||||
[defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
[defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
<div class="col-12 col-md-2 d-flex flex-md-column justify-content-between">
|
||||||
<ds-metadata-field-wrapper>
|
<ds-metadata-field-wrapper>
|
||||||
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"
|
<ds-thumbnail [thumbnail]="getThumbnail() | async"
|
||||||
[defaultImage]="'assets/images/project-placeholder.svg'"></ds-thumbnail>
|
[defaultImage]="'assets/images/project-placeholder.svg'"></ds-thumbnail>
|
||||||
</ds-metadata-field-wrapper>
|
</ds-metadata-field-wrapper>
|
||||||
<div>
|
<div>
|
||||||
|
Reference in New Issue
Block a user