Starting update for iiif using non-entity metadata.

This commit is contained in:
Michael Spalti
2021-09-28 13:22:09 -07:00
parent ded5e29f10
commit 3bf9c5f21c
49 changed files with 66 additions and 766 deletions

View File

@@ -1,50 +0,0 @@
import {IIIFSearchableComponent} from './item-pages/iiif-searchable/iiif-searchable.component';
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {SharedModule} from '../../shared/shared.module';
import {IIIFSearchableGridElementComponent} from './item-grid-elements/iiif-searchable/iiif-searchable-grid-element.component';
import {IIIFSearchableSearchResultListElementComponent} from './item-list-elements/search-result-list-elements/iiif-searchable/iiif-searchable-search-result-list-element.component';
import {IIIFSearchableSearchResultGridElementComponent} from './item-grid-elements/search-result-grid-elements/iiif-searchable/iiif-searchable-search-result-grid-element.component';
import {MiradorViewerComponent} from './mirador-viewer/mirador-viewer.component';
import {IIIFSearchableListElementComponent} from './item-list-elements/iiif-searchable/iiif-searchable-list-element.component';
import {IIIFComponent} from './item-pages/iiif/iiif.component';
import {IIIFListElementComponent} from './item-list-elements/iiif/iiif-list-element.component';
import {IIIFGridElementComponent} from './item-grid-elements/iiif/iiif-grid-element.component';
import {IIIFSearchResultListElementComponent} from './item-list-elements/search-result-list-elements/iiif/iiif-search-result-list-element.component';
import {IIIFSearchResultGridElementComponent} from './item-grid-elements/search-result-grid-elements/iiif/iiif-search-result-grid-element.component';
const ENTRY_COMPONENTS = [
IIIFComponent,
IIIFSearchableComponent,
IIIFListElementComponent,
IIIFSearchableListElementComponent,
IIIFGridElementComponent,
IIIFSearchableGridElementComponent,
IIIFSearchResultListElementComponent,
IIIFSearchableSearchResultListElementComponent,
IIIFSearchResultGridElementComponent,
IIIFSearchableSearchResultGridElementComponent,
MiradorViewerComponent
];
@NgModule({
imports: [
CommonModule,
SharedModule,
],
declarations: [
...ENTRY_COMPONENTS
],
entryComponents: [
...ENTRY_COMPONENTS
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class IIIFEntitiesModule {
static withEntryComponents() {
return {
ngModule: IIIFEntitiesModule,
providers: ENTRY_COMPONENTS.map((component) => ({provide: component}))
};
}
}

View File

@@ -1 +0,0 @@
<ds-iiif-searchable-result-grid-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-searchable-result-grid-element>

View File

@@ -1,17 +0,0 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIFSearchable', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-searchable-grid-element',
styleUrls: ['./iiif-searchable-grid-element.component.scss'],
templateUrl: './iiif-searchable-grid-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIFSearchable.
*/
export class IIIFSearchableGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1 +0,0 @@
<ds-iiif-result-grid-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-result-grid-element>

View File

@@ -1,17 +0,0 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIF', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-grid-element',
styleUrls: ['./iiif-grid-element.component.scss'],
templateUrl: './iiif-grid-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIF.
*/
export class IIIFGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,19 +0,0 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchableSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-searchable-result-grid-element',
styleUrls: ['./iiif-searchable-search-result-grid-element.component.scss'],
templateUrl: './iiif-searchable-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIFSearchable.
*/
export class IIIFSearchableSearchResultGridElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,19 +0,0 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-iiif-result-grid-element',
styleUrls: ['./iiif-search-result-grid-element.component.scss'],
templateUrl: './iiif-search-result-grid-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIF
*/
export class IIIFSearchResultGridElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1 +0,0 @@
<ds-iiif-searchable-search-result-list-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-searchable-search-result-list-element>

View File

@@ -1,17 +0,0 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIFSearchable', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-searchable-list-element',
styleUrls: ['./iiif-searchable-list-element.component.scss'],
templateUrl: './iiif-searchable-list-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIFSearchable
*/
export class IIIFSearchableListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1 +0,0 @@
<ds-iiif-search-result-list-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-iiif-search-result-list-element>

View File

@@ -1,17 +0,0 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@listableObjectComponent('IIIF', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-list-element',
styleUrls: ['./iiif-list-element.component.scss'],
templateUrl: './iiif-list-element.component.html'
})
/**
* The component for displaying a list element for an item of the type IIIF
*/
export class IIIFListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,19 +0,0 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<!-- <ds-truncatable-part [id]="dso.id" [minLines]="1">-->
<!-- <span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"-->
<!-- class="item-list-journals">-->
<!-- <span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">-->
<!-- <span [innerHTML]="value"><span [innerHTML]="value"></span></span>-->
<!-- </span>-->
<!-- </span>-->
<!-- </ds-truncatable-part>-->
</span>
</ds-truncatable>

View File

@@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchableSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-searchable-search-result-list-element',
styleUrls: ['./iiif-searchable-search-result-list-element.component.scss'],
templateUrl: './iiif-searchable-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIFSearchable
*/
export class IIIFSearchableSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,19 +0,0 @@
<ds-item-type-badge *ngIf="showLabel" [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<!-- <ds-truncatable-part [id]="dso.id" [minLines]="1">-->
<!-- <span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"-->
<!-- class="item-list-journals">-->
<!-- <span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">-->
<!-- <span [innerHTML]="value"><span [innerHTML]="value"></span></span>-->
<!-- </span>-->
<!-- </span>-->
<!-- </ds-truncatable-part>-->
</span>
</ds-truncatable>

View File

@@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('IIIFSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-iiif-search-result-list-element',
styleUrls: ['./iiif-search-result-list-element.component.scss'],
templateUrl: './iiif-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type IIIF
*/
export class IIIFSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,44 +0,0 @@
<ds-mirador-viewer id="iiif-viewer" [item]="object" [searchable]="searchable" [query]="query | async"></ds-mirador-viewer>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
</div>
<div class="col-xs-12 col-md-6">
<div class="item-page-title-field">
<h5>{{'iiifsearchable.page.titleprefix' | translate}}</h5><h3><ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values></h3>
</div>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.description.abstract']"
[label]="'item.preview.dc.description.abstract'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.citation']"
[label]="'iiifsearchable.page.issue'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.uri']"
[label]="'item.page.uri'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.other']"
[label]="'iiifsearchable.page.doi'">
</ds-generic-item-page-field>
<ds-metadata-field-wrapper>
<ds-metadata-representation-list
[parentItem]="object"
[itemType]="'Person'"
[metadataField]="'dc.contributor.author'"
[label]="'relationships.isAuthorOf' | translate">
</ds-metadata-representation-list>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</ds-metadata-field-wrapper>
</div>
</div>

View File

@@ -1,3 +0,0 @@
#iiif-viewer {
margin-bottom: 12px;
}

View File

@@ -1,199 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { of as observableOf } from 'rxjs';
import { GenericItemPageFieldComponent } from '../../../../item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { Item } from '../../../../core/shared/item.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { isNotEmpty } from '../../../../shared/empty.util';
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { IIIFSearchableComponent } from './iiif-searchable.component';
import { By } from '@angular/platform-browser';
import { RelationshipService } from '../../../../core/data/relationship.service';
import { RouteService } from '../../../../core/services/route.service';
let comp: IIIFSearchableComponent;
let fixture: ComponentFixture<IIIFSearchableComponent>;
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
metadata: {
'dc.identifier.citation': [
{
language: 'en_US',
value: 'issue'
}
],
'dc.identifier.uri': [
{
language: 'en_US',
value: 'uri'
}
]
,
'dc.identifier.other': [
{
language: 'en_US',
value: 'other'
},
]
}
});
describe('IIIFSearchableComponent', () => {
const routeServiceStub = jasmine.createSpyObj('routeService', {
getHistory: observableOf(['/browse',''])
});
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFSearchableComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
{ provide: RouteService, useValue: routeServiceStub }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFSearchableComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFSearchableComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
// TODO: fix test
// it(`should set searchable attribute to true`, () => {
// comp.ngOnInit();
// fixture.detectChanges();
// const miradorEl = fixture.debugElement.query(By.css('ds-mirador-viewer'));
// expect(miradorEl.nativeElement.getAttribute('searchable')).toBeTruthy();
// });
for (const key of Object.keys(mockItem.metadata)) {
it(`should be calling a component with metadata field ${key}`, () => {
const fields = fixture.debugElement.queryAll(By.css('.item-page-fields'));
expect(containsFieldInput(fields, key)).toBeTruthy();
});
}
});
describe('IIIFSearchableComponent with query', () => {
const routeServiceStub = jasmine.createSpyObj('routeService', {
getHistory: observableOf(['/search?page=1&query=test',''])
});
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFSearchableComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
{ provide: RouteService, useValue: routeServiceStub }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFSearchableComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFSearchableComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
// TODO: fix test
// it(`should set query search value`, () => {
// comp.ngOnInit();
// fixture.detectChanges();
// const miradorEl = fixture.debugElement.query(By.css('#iiif-viewer'));
// expect(miradorEl.nativeElement.getAttribute('query')).toMatch('test');
// });
});
function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
for (const field of fields) {
const fieldComp = field.componentInstance;
if (isNotEmpty(fieldComp.fields)) {
if (fieldComp.fields.indexOf(metadataKey) > -1) {
return true;
}
}
}
return false;
}

View File

@@ -1,48 +0,0 @@
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { Component, OnInit } from '@angular/core';
import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component';
import { RouteService } from '../../../../core/services/route.service';
import { Observable } from 'rxjs/internal/Observable';
import { filter, map, take } from 'rxjs/operators';
@listableObjectComponent('IIIFSearchable', ViewMode.StandalonePage)
@Component({
selector: 'ds-iiif-searchable',
styleUrls: ['./iiif-searchable.component.scss'],
templateUrl: './iiif-searchable.component.html'
})
export class IIIFSearchableComponent extends ItemComponent implements OnInit {
searchable: boolean;
query: Observable<string>;
constructor(protected routeService: RouteService) {
super();
}
ngOnInit(): void {
// Load iiif viewer in searchable configuration.
this.searchable = true;
// Use the route history to get the query from a
// previous search and use this value to initialize the
// viewer with search results.
// TODO: is there is a better way to look the previous search
this.query = this.routeService.getHistory().pipe(
take(1),
map(routes => routes[routes.length - 2 ]),
filter(r => {
return r.includes('/search');
}),
map(r => {
const arr = r.split('&');
const q = arr[1];
const v = q.split('=');
return v[1];
})
);
}
}

View File

@@ -1,40 +0,0 @@
<ds-mirador-viewer id="iiif-viewer" [item]="object" [searchable]="searchable"></ds-mirador-viewer>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
</div>
<div class="col-xs-12 col-md-6">
<div class="item-page-title-field">
<h5>{{'iiifsearchable.page.titleprefix' | translate}}</h5><h3><ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values></h3>
</div>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.citation']"
[label]="'iiifsearchable.page.issue'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.uri']"
[label]="'item.page.uri'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.identifier.other']"
[label]="'iiifsearchable.page.doi'">
</ds-generic-item-page-field>
<ds-metadata-field-wrapper>
<ds-metadata-representation-list
[parentItem]="object"
[itemType]="'Person'"
[metadataField]="'dc.contributor.author'"
[label]="'relationships.isAuthorOf' | translate">
</ds-metadata-representation-list>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</ds-metadata-field-wrapper>
</div>
</div>

View File

@@ -1,127 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { GenericItemPageFieldComponent } from '../../../../item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { CommunityDataService } from '../../../../core/data/community-data.service';
import { DefaultChangeAnalyzer } from '../../../../core/data/default-change-analyzer.service';
import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.service';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { buildPaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service';
import { Item } from '../../../../core/shared/item.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { UUIDService } from '../../../../core/shared/uuid.service';
import { isNotEmpty } from '../../../../shared/empty.util';
import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { IIIFComponent } from './iiif.component';
import { By } from '@angular/platform-browser';
import { RelationshipService } from '../../../../core/data/relationship.service';
let comp: IIIFComponent;
let fixture: ComponentFixture<IIIFComponent>;
const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
metadata: {
'dc.identifier.citation': [
{
language: 'en_US',
value: 'issue'
}
],
'dc.identifier.uri': [
{
language: 'en_US',
value: 'uri'
}
]
,
'dc.identifier.other': [
{
language: 'en_US',
value: 'other'
}
]
}
});
describe('IIIFComponent', () => {
const mockBitstreamDataService = {
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
return createSuccessfulRemoteDataObject$(new Bitstream());
}
};
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
})],
declarations: [IIIFComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{ provide: ItemDataService, useValue: {} },
{ provide: TruncatableService, useValue: {} },
{ provide: RelationshipService, useValue: {} },
{ provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} },
{ provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} },
{ provide: CommunityDataService, useValue: {} },
{ provide: HALEndpointService, useValue: {} },
{ provide: HttpClient, useValue: {} },
{ provide: DSOChangeAnalyzer, useValue: {} },
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(IIIFComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(waitForAsync(() => {
fixture = TestBed.createComponent(IIIFComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));
it(`should set searchable attribute to false`, () => {
const miradorEl = fixture.debugElement.query(By.css('ds-mirador-viewer'));
expect(miradorEl.nativeElement.getAttribute('searchable')).toBeFalsy();
});
for (const key of Object.keys(mockItem.metadata)) {
it(`should be calling a component with metadata field ${key}`, () => {
const fields = fixture.debugElement.queryAll(By.css('.item-page-fields'));
expect(containsFieldInput(fields, key)).toBeTruthy();
});
}
});
function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
for (const field of fields) {
const fieldComp = field.componentInstance;
if (isNotEmpty(fieldComp.fields)) {
if (fieldComp.fields.indexOf(metadataKey) > -1) {
return true;
}
}
}
return false;
}

View File

@@ -1,24 +0,0 @@
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { Component, OnInit } from '@angular/core';
import {ItemComponent} from '../../../../item-page/simple/item-types/shared/item.component';
@listableObjectComponent('IIIF', ViewMode.StandalonePage)
@Component({
selector: 'ds-iiif',
styleUrls: ['./iiif.component.scss'],
templateUrl: './iiif.component.html'
})
export class IIIFComponent extends ItemComponent implements OnInit {
searchable;
/**
* Load iiif viewer in no search configuration.
*/
ngOnInit(): void {
this.searchable = false;
}
}

View File

@@ -26,11 +26,11 @@ import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal
import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
import { ThemedItemPageComponent } from './simple/themed-item-page.component';
import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component';
import { IIIFEntitiesModule } from '../entity-groups/iiif-entities/iiif-entities.module';
import { MediaViewerComponent } from './media-viewer/media-viewer.component';
import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';
const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
@@ -59,7 +59,8 @@ const DECLARATIONS = [
AbstractIncrementalListComponent,
MediaViewerComponent,
MediaViewerVideoComponent,
MediaViewerImageComponent
MediaViewerImageComponent,
MiradorViewerComponent
];
@NgModule({
@@ -71,7 +72,6 @@ const DECLARATIONS = [
StatisticsModule.forRoot(),
JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents(),
IIIFEntitiesModule.withEntryComponents(),
NgxGalleryModule,
],
declarations: [

View File

@@ -1,13 +1,13 @@
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, PLATFORM_ID } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Item } from '../../../core/shared/item.model';
import { environment } from '../../../../environments/environment';
import { BitstreamDataService } from '../../../core/data/bitstream-data.service';
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { Bitstream } from '../../../core/shared/bitstream.model';
import { hasValue } from '../../../shared/empty.util';
import { Item } from '../../core/shared/item.model';
import { environment } from '../../../environments/environment';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { Bitstream } from '../../core/shared/bitstream.model';
import { hasValue } from '../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable';
import { map } from 'rxjs/operators';
import { of } from 'rxjs';

View File

@@ -13,6 +13,13 @@
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
</ng-container>
<ng-container *ngIf="iiifEnabled">
<ds-mirador-viewer id="iiif-viewer"
[item]="object"
[searchable]="iiifSearchEnabled"
[query]="iiifQuery$ | async">
</ds-mirador-viewer>
</ng-container>
<ng-container *ngIf="mediaViewer.image">
<ds-media-viewer [item]="object" [videoOptions]="mediaViewer.video"></ds-media-viewer>
</ng-container>

View File

@@ -0,0 +1,30 @@
import { Item } from '../../../../core/shared/item.model';
import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import { RouteService } from '../../../../core/services/route.service';
export const isIiifEnabled = (item: Item) => {
return !!item.firstMetadataValue('dspace.iiif.enabled');
};
export const isIiifSearchEnabled = (item: Item) => {
return !!item.firstMetadataValue('iiif.search.enabled');
};
export const getDSpaceQuery = (item: Item, routeService: RouteService): Observable<string> => {
return routeService.getHistory().pipe(
take(1),
map(routes => routes[routes.length - 2 ]),
filter(r => {
return r.includes('/search');
}),
map(r => {
const arr = r.split('&');
const q = arr[1];
const v = q.split('=');
return v[1];
})
);
};

View File

@@ -2,6 +2,9 @@ import { Component, Input, OnInit } from '@angular/core';
import { environment } from '../../../../../environments/environment';
import { Item } from '../../../../core/shared/item.model';
import { getItemPageRoute } from '../../../item-page-routing-paths';
import {RouteService} from '../../../../core/services/route.service';
import {Observable} from 'rxjs';
import {getDSpaceQuery, isIiifEnabled, isIiifSearchEnabled} from './item-iiif-utils';
@Component({
selector: 'ds-item',
@@ -18,9 +21,24 @@ export class ItemComponent implements OnInit {
*/
itemPageRoute: string;
iiifEnabled: boolean;
iiifSearchEnabled: boolean;
iiifQuery$: Observable<string>;
mediaViewer = environment.mediaViewer;
constructor(protected routeService: RouteService) {
}
ngOnInit(): void {
this.itemPageRoute = getItemPageRoute(this.object);
// Should iiif metadata be evaluated in the base component?
this.iiifEnabled = isIiifEnabled(this.object);
this.iiifSearchEnabled = isIiifSearchEnabled(this.object);
if (this.iiifSearchEnabled) {
this.iiifQuery$ = getDSpaceQuery(this.object, this.routeService);
}
}
}