mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'live-rest-backend' into w2p-41914_DSO-list-component
This commit is contained in:
@@ -10,6 +10,15 @@
|
|||||||
"license": "License"
|
"license": "License"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"community": {
|
||||||
|
"page": {
|
||||||
|
"news": "News",
|
||||||
|
"license": "License"
|
||||||
|
},
|
||||||
|
"sub-collection-list": {
|
||||||
|
"head": "Collections of this Community"
|
||||||
|
}
|
||||||
|
},
|
||||||
"item": {
|
"item": {
|
||||||
"page": {
|
"page": {
|
||||||
"author": "Author",
|
"author": "Author",
|
||||||
|
@@ -10,6 +10,7 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeaderComponent } from './header/header.component';
|
import { HeaderComponent } from './header/header.component';
|
||||||
import { CollectionPageModule } from './collection-page/collection-page.module';
|
import { CollectionPageModule } from './collection-page/collection-page.module';
|
||||||
|
import { CommunityPageModule } from './community-page/community-page.module';
|
||||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||||
|
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
|||||||
HomeModule,
|
HomeModule,
|
||||||
ItemPageModule,
|
ItemPageModule,
|
||||||
CollectionPageModule,
|
CollectionPageModule,
|
||||||
|
CommunityPageModule,
|
||||||
CoreModule.forRoot(),
|
CoreModule.forRoot(),
|
||||||
AppRoutingModule
|
AppRoutingModule
|
||||||
],
|
],
|
||||||
|
@@ -1,16 +1,32 @@
|
|||||||
<div class="collection-page" *ngIf="collectionData.hasSucceeded | async">
|
<div class="collection-page" *ngIf="collectionData.hasSucceeded | async">
|
||||||
<ds-collection-page-name [name]="(collectionData.payload | async)?.name"></ds-collection-page-name>
|
<!-- Collection Name -->
|
||||||
<ds-collection-page-logo *ngIf="logoData" [logo]="logoData.payload | async"></ds-collection-page-logo>
|
<ds-comcol-page-header
|
||||||
<ds-collection-page-introductory-text
|
[name]="(collectionData.payload | async)?.name">
|
||||||
[introductoryText]="(collectionData.payload | async)?.introductoryText">
|
</ds-comcol-page-header>
|
||||||
</ds-collection-page-introductory-text>
|
<!-- Collection logo -->
|
||||||
<ds-collection-page-news
|
<ds-comcol-page-logo *ngIf="logoData"
|
||||||
[sidebarText]="(collectionData.payload | async)?.sidebarText">
|
[logo]="logoData.payload | async"
|
||||||
</ds-collection-page-news>
|
[alternateText]="'Collection Logo'">
|
||||||
<ds-collection-page-copyright
|
</ds-comcol-page-logo>
|
||||||
[copyrightText]="(collectionData.payload | async)?.copyrightText">
|
<!-- Introductionary text -->
|
||||||
</ds-collection-page-copyright>
|
<ds-comcol-page-content
|
||||||
<ds-collection-page-license
|
[content]="(collectionData.payload | async)?.introductoryText"
|
||||||
[license]="(collectionData.payload | async)?.license">
|
[hasInnerHtml]="true">
|
||||||
</ds-collection-page-license>
|
</ds-comcol-page-content>
|
||||||
|
<!-- News -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(collectionData.payload | async)?.sidebarText"
|
||||||
|
[hasInnerHtml]="true"
|
||||||
|
[title]="'community.page.news'">
|
||||||
|
</ds-comcol-page-content>
|
||||||
|
<!-- Copyright -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(collectionData.payload | async)?.copyrightText"
|
||||||
|
[hasInnerHtml]="true">
|
||||||
|
</ds-comcol-page-content>
|
||||||
|
<!-- Licence -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(collectionData.payload | async)?.license"
|
||||||
|
[title]="'collection.page.license'">
|
||||||
|
</ds-comcol-page-content>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,19 +1,21 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
|
||||||
import { Collection } from "../core/shared/collection.model";
|
import { Collection } from "../core/shared/collection.model";
|
||||||
import { Bitstream } from "../core/shared/bitstream.model";
|
import { Bitstream } from "../core/shared/bitstream.model";
|
||||||
import { RemoteData } from "../core/data/remote-data";
|
import { RemoteData } from "../core/data/remote-data";
|
||||||
import { CollectionDataService } from "../core/data/collection-data.service";
|
import { CollectionDataService } from "../core/data/collection-data.service";
|
||||||
|
import { Subscription } from "rxjs/Subscription";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-page',
|
selector: 'ds-collection-page',
|
||||||
styleUrls: ['./collection-page.component.css'],
|
styleUrls: ['./collection-page.component.css'],
|
||||||
templateUrl: './collection-page.component.html',
|
templateUrl: './collection-page.component.html',
|
||||||
})
|
})
|
||||||
export class CollectionPageComponent implements OnInit {
|
export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||||
collectionData: RemoteData<Collection>;
|
collectionData: RemoteData<Collection>;
|
||||||
logoData: RemoteData<Bitstream>;
|
logoData: RemoteData<Bitstream>;
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private collectionDataService: CollectionDataService,
|
private collectionDataService: CollectionDataService,
|
||||||
@@ -24,12 +26,16 @@ export class CollectionPageComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.subscribe((params: Params) => {
|
this.route.params.subscribe((params: Params) => {
|
||||||
this.collectionData = this.collectionDataService.findById(params['id'])
|
this.collectionData = this.collectionDataService.findById(params['id']);
|
||||||
this.collectionData.payload
|
this.subs.push(this.collectionData.payload
|
||||||
.subscribe(collection => this.logoData = collection.logo);
|
.subscribe(collection => this.logoData = collection.logo));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
universalInit() {
|
universalInit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,8 @@ import { CommonModule } from '@angular/common';
|
|||||||
|
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { CollectionPageComponent } from './collection-page.component';
|
import { CollectionPageComponent } from './collection-page.component';
|
||||||
import { FieldWrapperComponent } from './field-wrapper/field-wrapper.component';
|
|
||||||
import { CollectionPageNameComponent } from './name/collection-page-name.component';
|
|
||||||
import { CollectionPageLogoComponent } from './logo/collection-page-logo.component';
|
|
||||||
import { CollectionPageIntroductoryTextComponent } from './introductory-text/collection-page-introductory-text.component';
|
|
||||||
import { CollectionPageNewsComponent } from './news/collection-page-news.component';
|
|
||||||
import { CollectionPageCopyrightComponent } from './copyright/collection-page-copyright.component';
|
|
||||||
import { CollectionPageLicenseComponent } from './license/collection-page-license.component';
|
|
||||||
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -18,16 +12,10 @@ import { CollectionPageRoutingModule } from './collection-page-routing.module';
|
|||||||
CollectionPageRoutingModule,
|
CollectionPageRoutingModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
SharedModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
CollectionPageComponent,
|
CollectionPageComponent,
|
||||||
FieldWrapperComponent,
|
|
||||||
CollectionPageNameComponent,
|
|
||||||
CollectionPageLogoComponent,
|
|
||||||
CollectionPageIntroductoryTextComponent,
|
|
||||||
CollectionPageNewsComponent,
|
|
||||||
CollectionPageCopyrightComponent,
|
|
||||||
CollectionPageLicenseComponent,
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CollectionPageModule { }
|
export class CollectionPageModule { }
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
<ds-field-wrapper *ngIf="copyrightText" class="collection-page-copyright">
|
|
||||||
<p [innerHtml]="copyrightText"></p>
|
|
||||||
</ds-field-wrapper>
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-copyright',
|
|
||||||
styleUrls: ['./collection-page-copyright.component.css'],
|
|
||||||
templateUrl: './collection-page-copyright.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageCopyrightComponent {
|
|
||||||
@Input() copyrightText: String;
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
<div class="collection-page-field-wrapper">
|
|
||||||
<ng-content></ng-content>
|
|
||||||
</div>
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-field-wrapper',
|
|
||||||
styleUrls: ['./field-wrapper.component.css'],
|
|
||||||
templateUrl: './field-wrapper.component.html',
|
|
||||||
})
|
|
||||||
export class FieldWrapperComponent {
|
|
||||||
@Input() name: String;
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
<ds-field-wrapper *ngIf="introductoryText" class="collection-page-introductory-text">
|
|
||||||
<p [innerHtml]="introductoryText"></p>
|
|
||||||
</ds-field-wrapper>
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-introductory-text',
|
|
||||||
styleUrls: ['./collection-page-introductory-text.component.css'],
|
|
||||||
templateUrl: './collection-page-introductory-text.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageIntroductoryTextComponent {
|
|
||||||
@Input() introductoryText: String;
|
|
||||||
}
|
|
@@ -1,4 +0,0 @@
|
|||||||
<ds-field-wrapper *ngIf="license" class="collection-page-license">
|
|
||||||
<h2>{{ 'collection.page.license' | translate }}</h2>
|
|
||||||
<p>{{ license }}</p>
|
|
||||||
</ds-field-wrapper>
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-license',
|
|
||||||
styleUrls: ['./collection-page-license.component.css'],
|
|
||||||
templateUrl: './collection-page-license.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageLicenseComponent {
|
|
||||||
@Input() license: String;
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
<ds-field-wrapper *ngIf="logo" class="collection-page-logo">
|
|
||||||
<img [src]="logo.url" class="img-responsive" alt="Collection logo" />
|
|
||||||
</ds-field-wrapper>
|
|
@@ -1 +0,0 @@
|
|||||||
@import '../../../styles/variables.scss';
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
import { Bitstream } from "../../core/shared/bitstream.model";
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-logo',
|
|
||||||
styleUrls: ['./collection-page-logo.component.css'],
|
|
||||||
templateUrl: './collection-page-logo.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageLogoComponent {
|
|
||||||
@Input() logo: Bitstream;
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
@import '../../../styles/variables.scss';
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-name',
|
|
||||||
styleUrls: ['./collection-page-name.component.css'],
|
|
||||||
templateUrl: './collection-page-name.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageNameComponent {
|
|
||||||
@Input() name: String;
|
|
||||||
}
|
|
@@ -1,4 +0,0 @@
|
|||||||
<ds-field-wrapper *ngIf="sidebarText" class="collection-page-news">
|
|
||||||
<h2>{{ 'collection.page.news' | translate }}</h2>
|
|
||||||
<p [innerHtml]="sidebarText"></p>
|
|
||||||
</ds-field-wrapper>
|
|
@@ -1 +0,0 @@
|
|||||||
@import '../../../styles/variables.scss';
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-collection-page-news',
|
|
||||||
styleUrls: ['./collection-page-news.component.css'],
|
|
||||||
templateUrl: './collection-page-news.component.html',
|
|
||||||
})
|
|
||||||
export class CollectionPageNewsComponent {
|
|
||||||
@Input() sidebarText: String;
|
|
||||||
}
|
|
13
src/app/community-page/community-page-routing.module.ts
Normal file
13
src/app/community-page/community-page-routing.module.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { CommunityPageComponent } from './community-page.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{ path: 'communities/:id', component: CommunityPageComponent }
|
||||||
|
])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CommunityPageRoutingModule { }
|
26
src/app/community-page/community-page.component.html
Normal file
26
src/app/community-page/community-page.component.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="community-page" *ngIf="communityData.hasSucceeded | async">
|
||||||
|
<!-- Community name -->
|
||||||
|
<ds-comcol-page-header [name]="(communityData.payload | async)?.name"></ds-comcol-page-header>
|
||||||
|
<!-- Community logo -->
|
||||||
|
<ds-comcol-page-logo *ngIf="logoData"
|
||||||
|
[logo]="logoData.payload | async"
|
||||||
|
[alternateText]="'Community Logo'">
|
||||||
|
</ds-comcol-page-logo>
|
||||||
|
<!-- Introductionary text -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(communityData.payload | async)?.introductoryText"
|
||||||
|
[hasInnerHtml]="true">
|
||||||
|
</ds-comcol-page-content>
|
||||||
|
<!-- News -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(communityData.payload | async)?.sidebarText"
|
||||||
|
[hasInnerHtml]="true"
|
||||||
|
[title]="'community.page.news'">
|
||||||
|
</ds-comcol-page-content>
|
||||||
|
<!-- Copyright -->
|
||||||
|
<ds-comcol-page-content
|
||||||
|
[content]="(communityData.payload | async)?.copyrightText"
|
||||||
|
[hasInnerHtml]="true">
|
||||||
|
</ds-comcol-page-content>
|
||||||
|
<ds-community-page-sub-collection-list></ds-community-page-sub-collection-list>
|
||||||
|
</div>
|
1
src/app/community-page/community-page.component.scss
Normal file
1
src/app/community-page/community-page.component.scss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
41
src/app/community-page/community-page.component.ts
Normal file
41
src/app/community-page/community-page.component.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
|
||||||
|
import { Community } from "../core/shared/community.model";
|
||||||
|
import { Bitstream } from "../core/shared/bitstream.model";
|
||||||
|
import { RemoteData } from "../core/data/remote-data";
|
||||||
|
import { CommunityDataService } from "../core/data/community-data.service";
|
||||||
|
import { Subscription } from "rxjs/Subscription";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-page',
|
||||||
|
styleUrls: ['./community-page.component.css'],
|
||||||
|
templateUrl: './community-page.component.html',
|
||||||
|
})
|
||||||
|
export class CommunityPageComponent implements OnInit, OnDestroy {
|
||||||
|
communityData: RemoteData<Community>;
|
||||||
|
logoData: RemoteData<Bitstream>;
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private communityDataService: CommunityDataService,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {
|
||||||
|
this.universalInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.route.params.subscribe((params: Params) => {
|
||||||
|
this.communityData = this.communityDataService.findById(params['id']);
|
||||||
|
this.subs.push(this.communityData.payload
|
||||||
|
.subscribe(community => this.logoData = community.logo));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
universalInit() {
|
||||||
|
}
|
||||||
|
}
|
25
src/app/community-page/community-page.module.ts
Normal file
25
src/app/community-page/community-page.module.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from "@angular/router";
|
||||||
|
|
||||||
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
|
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { CommunityPageComponent } from './community-page.component';
|
||||||
|
import { CommunityPageSubCollectionListComponent } from './sub-collection-list/community-page-sub-collection-list.component';
|
||||||
|
import { CommunityPageRoutingModule } from './community-page-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommunityPageRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
TranslateModule,
|
||||||
|
RouterModule,
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
CommunityPageComponent,
|
||||||
|
CommunityPageSubCollectionListComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CommunityPageModule { }
|
@@ -0,0 +1,11 @@
|
|||||||
|
<div *ngIf="subCollections.hasSucceeded | async">
|
||||||
|
<h2>{{'community.sub-collection-list.head' | translate}}</h2>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let collection of (subCollections.payload | async)">
|
||||||
|
<p>
|
||||||
|
<span class="lead"><a [routerLink]="['/collections', collection.id]">{{collection.name}}</a></span><br>
|
||||||
|
<span class="text-muted">{{collection.shortDescription}}</span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
@@ -0,0 +1,28 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { CollectionDataService } from "../../core/data/collection-data.service";
|
||||||
|
import { RemoteData } from "../../core/data/remote-data";
|
||||||
|
import { Collection } from "../../core/shared/collection.model";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-page-sub-collection-list',
|
||||||
|
styleUrls: ['./community-page-sub-collection-list.component.css'],
|
||||||
|
templateUrl: './community-page-sub-collection-list.component.html',
|
||||||
|
})
|
||||||
|
export class CommunityPageSubCollectionListComponent implements OnInit {
|
||||||
|
subCollections: RemoteData<Collection[]>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cds: CollectionDataService
|
||||||
|
) {
|
||||||
|
this.universalInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
universalInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.subCollections = this.cds.findAll();
|
||||||
|
}
|
||||||
|
}
|
@@ -26,53 +26,46 @@ export class RemoteDataBuildService {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO refactor, nearly identical to buildList, only payload differs
|
|
||||||
buildSingle<TNormalized extends CacheableObject, TDomain>(
|
buildSingle<TNormalized extends CacheableObject, TDomain>(
|
||||||
href: string,
|
href: string,
|
||||||
normalizedType: GenericConstructor<TNormalized>
|
normalizedType: GenericConstructor<TNormalized>
|
||||||
): RemoteData<TDomain> {
|
): RemoteData<TDomain> {
|
||||||
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href);
|
const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href);
|
||||||
const responseCacheObs = this.responseCache.get(href);
|
|
||||||
|
|
||||||
const requestPending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.requestPending).distinctUntilChanged();
|
const requestObs = Observable.race(
|
||||||
|
this.store.select<RequestEntry>('core', 'data', 'request', href).filter(entry => hasValue(entry)),
|
||||||
|
requestHrefObs.flatMap(requestHref =>
|
||||||
|
this.store.select<RequestEntry>('core', 'data', 'request', requestHref)).filter(entry => hasValue(entry))
|
||||||
|
);
|
||||||
|
|
||||||
const responsePending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.responsePending).distinctUntilChanged();
|
const responseCacheObs = Observable.race(
|
||||||
|
this.responseCache.get(href).filter(entry => hasValue(entry)),
|
||||||
|
requestHrefObs.flatMap(requestHref => this.responseCache.get(requestHref)).filter(entry => hasValue(entry))
|
||||||
|
);
|
||||||
|
|
||||||
|
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
|
||||||
|
|
||||||
|
const responsePending = requestObs.map((entry: RequestEntry) => entry.responsePending).distinctUntilChanged();
|
||||||
|
|
||||||
const isSuccessFul = responseCacheObs
|
const isSuccessFul = responseCacheObs
|
||||||
.map((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful).distinctUntilChanged();
|
.map((entry: ResponseCacheEntry) => entry.response.isSuccessful).distinctUntilChanged();
|
||||||
|
|
||||||
const errorMessage = responseCacheObs
|
const errorMessage = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && !entry.response.isSuccessful)
|
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
|
||||||
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const statusCode = responseCacheObs
|
const statusCode = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry))
|
|
||||||
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
|
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const pageInfo = responseCacheObs
|
const pageInfo = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry)
|
.filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo']))
|
||||||
&& hasValue(entry.response) && hasValue(entry.response['pageInfo']))
|
|
||||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).pageInfo)
|
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).pageInfo)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const payload =
|
const payload = this.objectCache.getBySelfLink<TNormalized>(href, normalizedType)
|
||||||
Observable.race(
|
.map((normalized: TNormalized) => {
|
||||||
this.objectCache.getBySelfLink<TNormalized>(href, normalizedType),
|
|
||||||
responseCacheObs
|
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful)
|
|
||||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
|
||||||
.flatMap((resourceUUIDs: Array<string>) => {
|
|
||||||
if (isNotEmpty(resourceUUIDs)) {
|
|
||||||
return this.objectCache.get(resourceUUIDs[0], normalizedType);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Observable.of(undefined);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.distinctUntilChanged()
|
|
||||||
).map((normalized: TNormalized) => {
|
|
||||||
return this.build<TNormalized, TDomain>(normalized);
|
return this.build<TNormalized, TDomain>(normalized);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,39 +81,37 @@ export class RemoteDataBuildService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO refactor, nearly identical to buildSingle, only payload differs
|
|
||||||
buildList<TNormalized extends CacheableObject, TDomain>(
|
buildList<TNormalized extends CacheableObject, TDomain>(
|
||||||
href: string,
|
href: string,
|
||||||
normalizedType: GenericConstructor<TNormalized>
|
normalizedType: GenericConstructor<TNormalized>
|
||||||
): RemoteData<TDomain[]> {
|
): RemoteData<TDomain[]> {
|
||||||
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href);
|
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href)
|
||||||
const responseCacheObs = this.responseCache.get(href);
|
.filter(entry => hasValue(entry));
|
||||||
|
const responseCacheObs = this.responseCache.get(href).filter(entry => hasValue(entry));
|
||||||
|
|
||||||
const requestPending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.requestPending).distinctUntilChanged();
|
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
|
||||||
|
|
||||||
const responsePending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.responsePending).distinctUntilChanged();
|
const responsePending = requestObs.map((entry: RequestEntry) => entry.responsePending).distinctUntilChanged();
|
||||||
|
|
||||||
const isSuccessFul = responseCacheObs
|
const isSuccessFul = responseCacheObs
|
||||||
.map((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful).distinctUntilChanged();
|
.map((entry: ResponseCacheEntry) => entry.response.isSuccessful).distinctUntilChanged();
|
||||||
|
|
||||||
const errorMessage = responseCacheObs
|
const errorMessage = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && !entry.response.isSuccessful)
|
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
|
||||||
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const statusCode = responseCacheObs
|
const statusCode = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry))
|
|
||||||
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
|
.map((entry: ResponseCacheEntry) => entry.response.statusCode)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const pageInfo = responseCacheObs
|
const pageInfo = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry)
|
.filter((entry: ResponseCacheEntry) => hasValue(entry.response) && hasValue(entry.response['pageInfo']))
|
||||||
&& hasValue(entry.response) && hasValue(entry.response['pageInfo']))
|
|
||||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).pageInfo)
|
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).pageInfo)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
|
|
||||||
const payload = responseCacheObs
|
const payload = responseCacheObs
|
||||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful)
|
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
||||||
.flatMap((resourceUUIDs: Array<string>) => {
|
.flatMap((resourceUUIDs: Array<string>) => {
|
||||||
return this.objectCache.getList(resourceUUIDs, normalizedType)
|
return this.objectCache.getList(resourceUUIDs, normalizedType)
|
||||||
|
@@ -18,7 +18,7 @@ export class NormalizedBitstream extends NormalizedDSpaceObject {
|
|||||||
* The relative path to this Bitstream's file
|
* The relative path to this Bitstream's file
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
url: string;
|
retrieve: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mime type of this Bitstream
|
* The mime type of this Bitstream
|
||||||
|
@@ -17,6 +17,8 @@ export class NormalizedCommunity extends NormalizedDSpaceObject {
|
|||||||
/**
|
/**
|
||||||
* The Bitstream that represents the logo of this Community
|
* The Bitstream that represents the logo of this Community
|
||||||
*/
|
*/
|
||||||
|
@autoserialize
|
||||||
|
@relationship(ResourceType.Bitstream)
|
||||||
logo: string;
|
logo: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
9
src/app/core/cache/object-cache.actions.ts
vendored
9
src/app/core/cache/object-cache.actions.ts
vendored
@@ -20,6 +20,7 @@ export class AddToObjectCacheAction implements Action {
|
|||||||
objectToCache: CacheableObject;
|
objectToCache: CacheableObject;
|
||||||
timeAdded: number;
|
timeAdded: number;
|
||||||
msToLive: number;
|
msToLive: number;
|
||||||
|
requestHref: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,9 +32,13 @@ export class AddToObjectCacheAction implements Action {
|
|||||||
* the time it was added
|
* the time it was added
|
||||||
* @param msToLive
|
* @param msToLive
|
||||||
* the amount of milliseconds before it should expire
|
* the amount of milliseconds before it should expire
|
||||||
|
* @param requestHref
|
||||||
|
* The href of the request that resulted in this object
|
||||||
|
* This isn't necessarily the same as the object's self
|
||||||
|
* link, it could have been part of a list for example
|
||||||
*/
|
*/
|
||||||
constructor(objectToCache: CacheableObject, timeAdded: number, msToLive: number) {
|
constructor(objectToCache: CacheableObject, timeAdded: number, msToLive: number, requestHref: string) {
|
||||||
this.payload = { objectToCache, timeAdded, msToLive };
|
this.payload = { objectToCache, timeAdded, msToLive, requestHref };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
src/app/core/cache/object-cache.reducer.spec.ts
vendored
15
src/app/core/cache/object-cache.reducer.spec.ts
vendored
@@ -24,7 +24,8 @@ describe("objectCacheReducer", () => {
|
|||||||
foo: "bar"
|
foo: "bar"
|
||||||
},
|
},
|
||||||
timeAdded: new Date().getTime(),
|
timeAdded: new Date().getTime(),
|
||||||
msToLive: 900000
|
msToLive: 900000,
|
||||||
|
requestHref: "https://rest.api/endpoint/uuid1"
|
||||||
},
|
},
|
||||||
[uuid2]: {
|
[uuid2]: {
|
||||||
data: {
|
data: {
|
||||||
@@ -32,7 +33,8 @@ describe("objectCacheReducer", () => {
|
|||||||
foo: "baz"
|
foo: "baz"
|
||||||
},
|
},
|
||||||
timeAdded: new Date().getTime(),
|
timeAdded: new Date().getTime(),
|
||||||
msToLive: 900000
|
msToLive: 900000,
|
||||||
|
requestHref: "https://rest.api/endpoint/uuid2"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
deepFreeze(testState);
|
deepFreeze(testState);
|
||||||
@@ -56,7 +58,8 @@ describe("objectCacheReducer", () => {
|
|||||||
const objectToCache = {uuid: uuid1};
|
const objectToCache = {uuid: uuid1};
|
||||||
const timeAdded = new Date().getTime();
|
const timeAdded = new Date().getTime();
|
||||||
const msToLive = 900000;
|
const msToLive = 900000;
|
||||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||||
|
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||||
const newState = objectCacheReducer(state, action);
|
const newState = objectCacheReducer(state, action);
|
||||||
|
|
||||||
expect(newState[uuid1].data).toEqual(objectToCache);
|
expect(newState[uuid1].data).toEqual(objectToCache);
|
||||||
@@ -68,7 +71,8 @@ describe("objectCacheReducer", () => {
|
|||||||
const objectToCache = {uuid: uuid1, foo: "baz", somethingElse: true};
|
const objectToCache = {uuid: uuid1, foo: "baz", somethingElse: true};
|
||||||
const timeAdded = new Date().getTime();
|
const timeAdded = new Date().getTime();
|
||||||
const msToLive = 900000;
|
const msToLive = 900000;
|
||||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||||
|
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||||
const newState = objectCacheReducer(testState, action);
|
const newState = objectCacheReducer(testState, action);
|
||||||
|
|
||||||
expect(newState[uuid1].data['foo']).toBe("baz");
|
expect(newState[uuid1].data['foo']).toBe("baz");
|
||||||
@@ -80,7 +84,8 @@ describe("objectCacheReducer", () => {
|
|||||||
const objectToCache = {uuid: uuid1};
|
const objectToCache = {uuid: uuid1};
|
||||||
const timeAdded = new Date().getTime();
|
const timeAdded = new Date().getTime();
|
||||||
const msToLive = 900000;
|
const msToLive = 900000;
|
||||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||||
|
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||||
deepFreeze(state);
|
deepFreeze(state);
|
||||||
|
|
||||||
objectCacheReducer(state, action);
|
objectCacheReducer(state, action);
|
||||||
|
4
src/app/core/cache/object-cache.reducer.ts
vendored
4
src/app/core/cache/object-cache.reducer.ts
vendored
@@ -22,6 +22,7 @@ export class ObjectCacheEntry implements CacheEntry {
|
|||||||
data: CacheableObject;
|
data: CacheableObject;
|
||||||
timeAdded: number;
|
timeAdded: number;
|
||||||
msToLive: number;
|
msToLive: number;
|
||||||
|
requestHref: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +84,8 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio
|
|||||||
[action.payload.objectToCache.uuid]: {
|
[action.payload.objectToCache.uuid]: {
|
||||||
data: action.payload.objectToCache,
|
data: action.payload.objectToCache,
|
||||||
timeAdded: action.payload.timeAdded,
|
timeAdded: action.payload.timeAdded,
|
||||||
msToLive: action.payload.msToLive
|
msToLive: action.payload.msToLive,
|
||||||
|
requestHref: action.payload.requestHref
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ describe("ObjectCacheService", () => {
|
|||||||
let store: Store<ObjectCacheState>;
|
let store: Store<ObjectCacheState>;
|
||||||
|
|
||||||
const uuid = '1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
|
const uuid = '1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
|
||||||
|
const requestHref = 'https://rest.api/endpoint/1698f1d3-be98-4c51-9fd8-6bfedcbd59b7';
|
||||||
const timestamp = new Date().getTime();
|
const timestamp = new Date().getTime();
|
||||||
const msToLive = 900000;
|
const msToLive = 900000;
|
||||||
const objectToCache = {
|
const objectToCache = {
|
||||||
@@ -44,8 +45,8 @@ describe("ObjectCacheService", () => {
|
|||||||
describe("add", () => {
|
describe("add", () => {
|
||||||
it("should dispatch an ADD action with the object to add, the time to live, and the current timestamp", () => {
|
it("should dispatch an ADD action with the object to add, the time to live, and the current timestamp", () => {
|
||||||
|
|
||||||
service.add(objectToCache, msToLive);
|
service.add(objectToCache, msToLive, requestHref);
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new AddToObjectCacheAction(objectToCache, timestamp, msToLive));
|
expect(store.dispatch).toHaveBeenCalledWith(new AddToObjectCacheAction(objectToCache, timestamp, msToLive, requestHref));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
29
src/app/core/cache/object-cache.service.ts
vendored
29
src/app/core/cache/object-cache.service.ts
vendored
@@ -22,9 +22,13 @@ export class ObjectCacheService {
|
|||||||
* The object to add
|
* The object to add
|
||||||
* @param msToLive
|
* @param msToLive
|
||||||
* The number of milliseconds it should be cached for
|
* The number of milliseconds it should be cached for
|
||||||
|
* @param requestHref
|
||||||
|
* The href of the request that resulted in this object
|
||||||
|
* This isn't necessarily the same as the object's self
|
||||||
|
* link, it could have been part of a list for example
|
||||||
*/
|
*/
|
||||||
add(objectToCache: CacheableObject, msToLive: number): void {
|
add(objectToCache: CacheableObject, msToLive: number, requestHref: string): void {
|
||||||
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive));
|
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive, requestHref));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,9 +58,7 @@ export class ObjectCacheService {
|
|||||||
* An observable of the requested object
|
* An observable of the requested object
|
||||||
*/
|
*/
|
||||||
get<T extends CacheableObject>(uuid: string, type: GenericConstructor<T>): Observable<T> {
|
get<T extends CacheableObject>(uuid: string, type: GenericConstructor<T>): Observable<T> {
|
||||||
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
|
return this.getEntry(uuid)
|
||||||
.filter(entry => this.isValid(entry))
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.map((entry: ObjectCacheEntry) => <T> Object.assign(new type(), entry.data));
|
.map((entry: ObjectCacheEntry) => <T> Object.assign(new type(), entry.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +67,23 @@ export class ObjectCacheService {
|
|||||||
.flatMap((uuid: string) => this.get(uuid, type))
|
.flatMap((uuid: string) => this.get(uuid, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getEntry(uuid: string): Observable<ObjectCacheEntry> {
|
||||||
|
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
|
||||||
|
.filter(entry => this.isValid(entry))
|
||||||
|
.distinctUntilChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequestHref(uuid: string): Observable<string> {
|
||||||
|
return this.getEntry(uuid)
|
||||||
|
.map((entry: ObjectCacheEntry) => entry.requestHref)
|
||||||
|
.distinctUntilChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
getRequestHrefBySelfLink(self: string): Observable<string> {
|
||||||
|
return this.store.select<string>('core', 'index', 'href', self)
|
||||||
|
.flatMap((uuid: string) => this.getRequestHref(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an observable for an array of objects of the same type
|
* Get an observable for an array of objects of the same type
|
||||||
* with the specified UUIDs
|
* with the specified UUIDs
|
||||||
|
@@ -49,7 +49,7 @@ export class RequestEffects {
|
|||||||
})
|
})
|
||||||
.flatMap((entry: RequestEntry) => {
|
.flatMap((entry: RequestEntry) => {
|
||||||
return this.restApi.get(entry.request.href)
|
return this.restApi.get(entry.request.href)
|
||||||
.map((data: DSpaceRESTV2Response) => new SuccessResponse(this.process(data.payload), data.statusCode, this.processPageInfo(data.payload.page)))
|
.map((data: DSpaceRESTV2Response) => new SuccessResponse(this.process(data.payload, entry.request.href), data.statusCode, this.processPageInfo(data.payload.page)))
|
||||||
.do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive))
|
.do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive))
|
||||||
.map((response: Response) => new RequestCompleteAction(entry.request.href))
|
.map((response: Response) => new RequestCompleteAction(entry.request.href))
|
||||||
.catch((error: RequestError) => Observable.of(new ErrorResponse(error))
|
.catch((error: RequestError) => Observable.of(new ErrorResponse(error))
|
||||||
@@ -57,14 +57,14 @@ export class RequestEffects {
|
|||||||
.map((response: Response) => new RequestCompleteAction(entry.request.href)));
|
.map((response: Response) => new RequestCompleteAction(entry.request.href)));
|
||||||
});
|
});
|
||||||
|
|
||||||
protected process(data: any): Array<string> {
|
protected process(data: any, requestHref: string): Array<string> {
|
||||||
|
|
||||||
if (isNotEmpty(data)) {
|
if (isNotEmpty(data)) {
|
||||||
if (isPaginatedResponse(data)) {
|
if (isPaginatedResponse(data)) {
|
||||||
return this.process(data._embedded);
|
return this.process(data._embedded, requestHref);
|
||||||
}
|
}
|
||||||
else if (isObjectLevel(data)) {
|
else if (isObjectLevel(data)) {
|
||||||
return this.deserializeAndCache(data);
|
return this.deserializeAndCache(data, requestHref);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let uuids = [];
|
let uuids = [];
|
||||||
@@ -72,14 +72,14 @@ export class RequestEffects {
|
|||||||
.filter(property => data.hasOwnProperty(property))
|
.filter(property => data.hasOwnProperty(property))
|
||||||
.filter(property => hasValue(data[property]))
|
.filter(property => hasValue(data[property]))
|
||||||
.forEach(property => {
|
.forEach(property => {
|
||||||
uuids = [...uuids, ...this.deserializeAndCache(data[property])];
|
uuids = [...uuids, ...this.deserializeAndCache(data[property], requestHref)];
|
||||||
});
|
});
|
||||||
return uuids;
|
return uuids;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected deserializeAndCache(obj): Array<string> {
|
protected deserializeAndCache(obj, requestHref: string): Array<string> {
|
||||||
let type: ResourceType;
|
let type: ResourceType;
|
||||||
const isArray = Array.isArray(obj);
|
const isArray = Array.isArray(obj);
|
||||||
|
|
||||||
@@ -103,19 +103,19 @@ export class RequestEffects {
|
|||||||
if (isArray) {
|
if (isArray) {
|
||||||
obj.forEach(o => {
|
obj.forEach(o => {
|
||||||
if (isNotEmpty(o._embedded)) {
|
if (isNotEmpty(o._embedded)) {
|
||||||
this.process(o._embedded);
|
this.process(o._embedded, requestHref);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const normalizedObjArr = serializer.deserializeArray(obj);
|
const normalizedObjArr = serializer.deserializeArray(obj);
|
||||||
normalizedObjArr.forEach(t => this.addToObjectCache(t));
|
normalizedObjArr.forEach(t => this.addToObjectCache(t, requestHref));
|
||||||
return normalizedObjArr.map(t => t.uuid);
|
return normalizedObjArr.map(t => t.uuid);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (isNotEmpty(obj._embedded)) {
|
if (isNotEmpty(obj._embedded)) {
|
||||||
this.process(obj._embedded);
|
this.process(obj._embedded, requestHref);
|
||||||
}
|
}
|
||||||
const normalizedObj = serializer.deserialize(obj);
|
const normalizedObj = serializer.deserialize(obj);
|
||||||
this.addToObjectCache(normalizedObj);
|
this.addToObjectCache(normalizedObj, requestHref);
|
||||||
return [normalizedObj.uuid];
|
return [normalizedObj.uuid];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,11 +132,11 @@ export class RequestEffects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected addToObjectCache(co: CacheableObject): void {
|
protected addToObjectCache(co: CacheableObject, requestHref: string): void {
|
||||||
if (hasNoValue(co) || hasNoValue(co.uuid)) {
|
if (hasNoValue(co) || hasNoValue(co.uuid)) {
|
||||||
throw new Error('The server returned an invalid object');
|
throw new Error('The server returned an invalid object');
|
||||||
}
|
}
|
||||||
this.objectCache.add(co, this.EnvConfig.cache.msToLive);
|
this.objectCache.add(co, this.EnvConfig.cache.msToLive, requestHref);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected processPageInfo(pageObj: any): PageInfo {
|
protected processPageInfo(pageObj: any): PageInfo {
|
||||||
|
@@ -8,6 +8,9 @@ import { RequestConfigureAction, RequestExecuteAction } from "./request.actions"
|
|||||||
import { ResponseCacheService } from "../cache/response-cache.service";
|
import { ResponseCacheService } from "../cache/response-cache.service";
|
||||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||||
import { CacheableObject } from "../cache/object-cache.reducer";
|
import { CacheableObject } from "../cache/object-cache.reducer";
|
||||||
|
import { ResponseCacheEntry } from "../cache/response-cache.reducer";
|
||||||
|
import { request } from "http";
|
||||||
|
import { SuccessResponse } from "../cache/response-cache.models";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestService {
|
export class RequestService {
|
||||||
@@ -35,7 +38,19 @@ export class RequestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configure<T extends CacheableObject>(request: Request<T>): void {
|
configure<T extends CacheableObject>(request: Request<T>): void {
|
||||||
const isCached = this.objectCache.hasBySelfLink(request.href);
|
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||||
|
|
||||||
|
if (!isCached && this.responseCache.has(request.href)) {
|
||||||
|
//if it isn't cached it may be a list endpoint, if so verify
|
||||||
|
//every object included in the response is still cached
|
||||||
|
this.responseCache.get(request.href)
|
||||||
|
.take(1)
|
||||||
|
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||||
|
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
||||||
|
.map((resourceUUIDs: Array<string>) => resourceUUIDs.every(uuid => this.objectCache.has(uuid)))
|
||||||
|
.subscribe(c => isCached = c);
|
||||||
|
}
|
||||||
|
|
||||||
const isPending = this.isPending(request.href);
|
const isPending = this.isPending(request.href);
|
||||||
|
|
||||||
if (!(isCached || isPending)) {
|
if (!(isCached || isPending)) {
|
||||||
|
@@ -60,8 +60,8 @@ describe("DSpaceRESTv2Serializer", () => {
|
|||||||
it("should turn a model in to a valid document", () => {
|
it("should turn a model in to a valid document", () => {
|
||||||
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
const doc = serializer.serialize(testModels[0]);
|
const doc = serializer.serialize(testModels[0]);
|
||||||
expect(testModels[0].id).toBe(doc._embedded.id);
|
expect(testModels[0].id).toBe(doc.id);
|
||||||
expect(testModels[0].name).toBe(doc._embedded.name);
|
expect(testModels[0].name).toBe(doc.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -72,10 +72,10 @@ describe("DSpaceRESTv2Serializer", () => {
|
|||||||
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
const doc = serializer.serializeArray(testModels);
|
const doc = serializer.serializeArray(testModels);
|
||||||
|
|
||||||
expect(testModels[0].id).toBe(doc._embedded[0].id);
|
expect(testModels[0].id).toBe(doc[0].id);
|
||||||
expect(testModels[0].name).toBe(doc._embedded[0].name);
|
expect(testModels[0].name).toBe(doc[0].name);
|
||||||
expect(testModels[1].id).toBe(doc._embedded[1].id);
|
expect(testModels[1].id).toBe(doc[1].id);
|
||||||
expect(testModels[1].name).toBe(doc._embedded[1].name);
|
expect(testModels[1].name).toBe(doc[1].name);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -9,11 +9,6 @@ export class Bitstream extends DSpaceObject {
|
|||||||
*/
|
*/
|
||||||
size: number;
|
size: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* The relative path to this Bitstream's file
|
|
||||||
*/
|
|
||||||
url: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mime type of this Bitstream
|
* The mime type of this Bitstream
|
||||||
*/
|
*/
|
||||||
@@ -40,7 +35,7 @@ export class Bitstream extends DSpaceObject {
|
|||||||
owner: RemoteData<Item>;
|
owner: RemoteData<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bundle that owns this Bitstream
|
* The URL to retrieve this Bitstream's file
|
||||||
*/
|
*/
|
||||||
retrieve: string;
|
retrieve: string;
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { TestBed, async } from '@angular/core/testing';
|
|
||||||
import { Item } from "./item.model";
|
import { Item } from "./item.model";
|
||||||
import { Bundle } from "./bundle.model";
|
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { RemoteData } from "../data/remote-data";
|
import { RemoteData } from "../data/remote-data";
|
||||||
import { Bitstream } from "./bitstream.model";
|
import { Bitstream } from "./bitstream.model";
|
||||||
|
import { isEmpty } from "../../shared/empty.util";
|
||||||
|
import { PageInfo } from "./page-info.model";
|
||||||
|
|
||||||
|
|
||||||
describe('Item', () => {
|
describe('Item', () => {
|
||||||
@@ -17,23 +17,25 @@ describe('Item', () => {
|
|||||||
const bitstream2Path = "otherfile.doc";
|
const bitstream2Path = "otherfile.doc";
|
||||||
|
|
||||||
const nonExistingBundleName = "c1e568f7-d14e-496b-bdd7-07026998cc00";
|
const nonExistingBundleName = "c1e568f7-d14e-496b-bdd7-07026998cc00";
|
||||||
let remoteBundles;
|
let bitstreams;
|
||||||
let thumbnailBundle;
|
let remoteDataThumbnail;
|
||||||
let originalBundle;
|
let remoteDataFiles;
|
||||||
|
let remoteDataAll;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const thumbnail = {
|
const thumbnail = {
|
||||||
retrieve: thumbnailPath
|
retrieve: thumbnailPath
|
||||||
};
|
};
|
||||||
|
|
||||||
const bitstreams = [{
|
bitstreams = [{
|
||||||
retrieve: bitstream1Path
|
retrieve: bitstream1Path
|
||||||
}, {
|
}, {
|
||||||
retrieve: bitstream2Path
|
retrieve: bitstream2Path
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const remoteDataThumbnail = createRemoteDataObject(thumbnail);
|
remoteDataThumbnail = createRemoteDataObject(thumbnail);
|
||||||
const remoteDataFiles = createRemoteDataObject(bitstreams);
|
remoteDataFiles = createRemoteDataObject(bitstreams);
|
||||||
|
remoteDataAll = createRemoteDataObject([...bitstreams, thumbnail]);
|
||||||
|
|
||||||
|
|
||||||
// Create Bundles
|
// Create Bundles
|
||||||
@@ -50,32 +52,30 @@ describe('Item', () => {
|
|||||||
bitstreams: remoteDataFiles
|
bitstreams: remoteDataFiles
|
||||||
}];
|
}];
|
||||||
|
|
||||||
remoteBundles = createRemoteDataObject(bundles);
|
|
||||||
|
|
||||||
item = Object.assign(new Item(), { bundles: remoteBundles });
|
item = Object.assign(new Item(), { bitstreams: remoteDataAll});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return the bundle with the given name of this item when the bundle exists', () => {
|
it('should return the bitstreams related to this item with the specified bundle name', () => {
|
||||||
let name: string = thumbnailBundleName;
|
const bitObs: Observable<Bitstream[]> = item.getBitstreamsByBundleName(thumbnailBundleName);
|
||||||
let bundle: Observable<Bundle> = item.getBundle(name);
|
bitObs.take(1).subscribe(bs =>
|
||||||
bundle.map(b => expect(b.name).toBe(name));
|
expect(bs.every(b => b.name === thumbnailBundleName)).toBeTruthy());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null when no bundle with this name exists for this item', () => {
|
it('should return an empty array when no bitstreams with this bundleName exist for this item', () => {
|
||||||
let name: string = nonExistingBundleName;
|
const bitstreams: Observable<Bitstream[]> = item.getBitstreamsByBundleName(nonExistingBundleName);
|
||||||
let bundle: Observable<Bundle> = item.getBundle(name);
|
bitstreams.take(1).subscribe(bs => expect(isEmpty(bs)).toBeTruthy());
|
||||||
bundle.map(b => expect(b).toBeUndefined());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("get thumbnail", () => {
|
describe("get thumbnail", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(item, 'getBundle').and.returnValue(Observable.of(thumbnailBundle));
|
spyOn(item, 'getBitstreamsByBundleName').and.returnValue(Observable.of([remoteDataThumbnail]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the thumbnail (the primaryBitstream in the bundle "THUMBNAIL") of this item', () => {
|
it('should return the thumbnail of this item', () => {
|
||||||
let path: string = thumbnailPath;
|
let path: string = thumbnailPath;
|
||||||
let bitstream: Observable<Bitstream> = item.getThumbnail();
|
let bitstream: Observable<Bitstream> = item.getThumbnail();
|
||||||
bitstream.map(b => expect(b.retrieve).toBe(path));
|
bitstream.map(b => expect(b.retrieve).toBe(path));
|
||||||
@@ -85,10 +85,10 @@ describe('Item', () => {
|
|||||||
|
|
||||||
describe("get files", () => {
|
describe("get files", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(item, 'getBundle').and.returnValue(Observable.of(originalBundle));
|
spyOn(item, 'getBitstreamsByBundleName').and.returnValue(Observable.of(bitstreams));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all files in the ORIGINAL bundle', () => {
|
it('should return all bitstreams with "ORIGINAL" as bundleName', () => {
|
||||||
let paths = [bitstream1Path, bitstream2Path];
|
let paths = [bitstream1Path, bitstream2Path];
|
||||||
|
|
||||||
let files: Observable<Bitstream[]> = item.getFiles();
|
let files: Observable<Bitstream[]> = item.getFiles();
|
||||||
@@ -110,6 +110,23 @@ describe('Item', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function createRemoteDataObject(object: Object) {
|
function createRemoteDataObject(object: Object) {
|
||||||
return new RemoteData("", Observable.of(false), Observable.of(false), Observable.of(true), Observable.of(undefined), Observable.of(object));
|
const self = "";
|
||||||
|
const requestPending = Observable.of(false);
|
||||||
|
const responsePending = Observable.of(false);
|
||||||
|
const isSuccessful = Observable.of(true);
|
||||||
|
const errorMessage = Observable.of(undefined);
|
||||||
|
const statusCode = Observable.of("200");
|
||||||
|
const pageInfo = Observable.of(new PageInfo());
|
||||||
|
const payload = Observable.of(object);
|
||||||
|
return new RemoteData(
|
||||||
|
self,
|
||||||
|
requestPending,
|
||||||
|
responsePending,
|
||||||
|
isSuccessful,
|
||||||
|
errorMessage,
|
||||||
|
statusCode,
|
||||||
|
pageInfo,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
<div *ngIf="content" class="content-with-optional-title">
|
||||||
|
<h2 *ngIf="title">{{ title | translate }}</h2>
|
||||||
|
<div *ngIf="hasInnerHtml" [innerHtml]="content"></div>
|
||||||
|
<div *ngIf="!hasInnerHtml">{{content}}</div>
|
||||||
|
</div>
|
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component renders any content inside of this component.
|
||||||
|
* If there is a title set it will render the title.
|
||||||
|
* If hasInnerHtml is true the content will be handled as html.
|
||||||
|
* To see how it is used see collection-page or community-page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-comcol-page-content',
|
||||||
|
styleUrls: ['./comcol-page-content.component.css'],
|
||||||
|
templateUrl: './comcol-page-content.component.html'
|
||||||
|
})
|
||||||
|
export class ComcolPageContentComponent {
|
||||||
|
|
||||||
|
// Optional title
|
||||||
|
@Input() title: string;
|
||||||
|
|
||||||
|
// The content to render. Might be html
|
||||||
|
@Input() content: string;
|
||||||
|
|
||||||
|
// flag whether the content contains html syntax or not
|
||||||
|
@Input() hasInnerHtml: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.universalInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
universalInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-comcol-page-header',
|
||||||
|
styleUrls: ['./comcol-page-header.component.css'],
|
||||||
|
templateUrl: './comcol-page-header.component.html',
|
||||||
|
})
|
||||||
|
export class ComcolPageHeaderComponent {
|
||||||
|
@Input() name: String;
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
<div *ngIf="logo" class="dso-logo">
|
||||||
|
<img [src]="logo.retrieve" class="img-responsive" [attr.alt]="alternateText ? alternateText : null" />
|
||||||
|
</div>
|
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
import { Bitstream } from "../../core/shared/bitstream.model";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-comcol-page-logo',
|
||||||
|
styleUrls: ['./comcol-page-logo.component.css'],
|
||||||
|
templateUrl: './comcol-page-logo.component.html',
|
||||||
|
})
|
||||||
|
export class ComcolPageLogoComponent {
|
||||||
|
@Input() logo: Bitstream;
|
||||||
|
|
||||||
|
@Input() alternateText: string;
|
||||||
|
}
|
@@ -14,6 +14,9 @@ import { ThumbnailComponent } from "../thumbnail/thumbnail.component";
|
|||||||
import { SafeUrlPipe } from "./utils/safe-url-pipe";
|
import { SafeUrlPipe } from "./utils/safe-url-pipe";
|
||||||
import { HostWindowService } from "./host-window.service";
|
import { HostWindowService } from "./host-window.service";
|
||||||
import { NativeWindowFactory, NativeWindowService } from "./window.service";
|
import { NativeWindowFactory, NativeWindowService } from "./window.service";
|
||||||
|
import { ComcolPageContentComponent } from "./comcol-page-content/comcol-page-content.component";
|
||||||
|
import { ComcolPageHeaderComponent } from "./comcol-page-header/comcol-page-header.component";
|
||||||
|
import { ComcolPageLogoComponent } from "./comcol-page-logo/comcol-page-logo.component";
|
||||||
import { TRUNCATE_PIPES } from "ng2-truncate";
|
import { TRUNCATE_PIPES } from "ng2-truncate";
|
||||||
import { EnumKeysPipe } from "./utils/enum-keys-pipe";
|
import { EnumKeysPipe } from "./utils/enum-keys-pipe";
|
||||||
|
|
||||||
@@ -39,7 +42,10 @@ const PIPES = [
|
|||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
// put shared components here
|
// put shared components here
|
||||||
PaginationComponent,
|
PaginationComponent,
|
||||||
ThumbnailComponent
|
ThumbnailComponent,
|
||||||
|
ComcolPageContentComponent,
|
||||||
|
ComcolPageHeaderComponent,
|
||||||
|
ComcolPageLogoComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
|
@@ -35,11 +35,12 @@ export const COMMUNITIES = {
|
|||||||
],
|
],
|
||||||
"_links": {
|
"_links": {
|
||||||
"self": {
|
"self": {
|
||||||
"href": "http://dspace7.4science.it/dspace-spring-rest/api/core/community/9076bd16-e69a-48d6-9e41-0238cb40d863"
|
"href": "/communities/6631"
|
||||||
},
|
},
|
||||||
"collections": [
|
"collections": [
|
||||||
{ "href": "/collections/5179" }
|
{ "href": "/collections/5179" }
|
||||||
]
|
],
|
||||||
|
"logo": { "href": "/bitstreams/4688" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,7 +78,7 @@ export const COMMUNITIES = {
|
|||||||
],
|
],
|
||||||
"_links": {
|
"_links": {
|
||||||
"self": {
|
"self": {
|
||||||
"href": "http://dspace7.4science.it/dspace-spring-rest/api/core/community/9076bd16-e69a-48d6-9e41-0238cb40d863"
|
"href": "/communities/2365"
|
||||||
},
|
},
|
||||||
"collections": [
|
"collections": [
|
||||||
{ "href": "/collections/6547" }
|
{ "href": "/collections/6547" }
|
||||||
|
@@ -94,7 +94,6 @@ export const ITEMS = {
|
|||||||
],
|
],
|
||||||
"_embedded": {
|
"_embedded": {
|
||||||
"parents": [
|
"parents": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"_links": {
|
"_links": {
|
||||||
"self": { "href": "/collections/6547" },
|
"self": { "href": "/collections/6547" },
|
||||||
@@ -108,6 +107,28 @@ export const ITEMS = {
|
|||||||
"type": "collection",
|
"type": "collection",
|
||||||
"name": "Another Test Collection",
|
"name": "Another Test Collection",
|
||||||
"handle": "123456789/6547",
|
"handle": "123456789/6547",
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"key": "dc.rights",
|
||||||
|
"value": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description",
|
||||||
|
"value": "<p class='lead'>Another introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.abstract",
|
||||||
|
"value": "Another collection for testing purposes",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.tableofcontents",
|
||||||
|
"value": "<p>Some more news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>",
|
||||||
|
"language": null
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bundles": [
|
"bundles": [
|
||||||
@@ -239,13 +260,36 @@ export const ITEMS = {
|
|||||||
"items": [
|
"items": [
|
||||||
{ "href": "/items/8871" },
|
{ "href": "/items/8871" },
|
||||||
{ "href": "/items/9978" }
|
{ "href": "/items/9978" }
|
||||||
]
|
],
|
||||||
|
"logo": { "href": "/bitstreams/4688" }
|
||||||
},
|
},
|
||||||
"id": "5179",
|
"id": "5179",
|
||||||
"uuid": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
"uuid": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
||||||
"type": "collection",
|
"type": "collection",
|
||||||
"name": "A Test Collection",
|
"name": "A Test Collection",
|
||||||
"handle": "123456789/5179",
|
"handle": "123456789/5179",
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"key": "dc.rights",
|
||||||
|
"value": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description",
|
||||||
|
"value": "<p class='lead'>An introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.abstract",
|
||||||
|
"value": "A collection for testing purposes",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.tableofcontents",
|
||||||
|
"value": "<p>Some news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>",
|
||||||
|
"language": null
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_links": {
|
"_links": {
|
||||||
@@ -260,6 +304,28 @@ export const ITEMS = {
|
|||||||
"type": "collection",
|
"type": "collection",
|
||||||
"name": "Another Test Collection",
|
"name": "Another Test Collection",
|
||||||
"handle": "123456789/6547",
|
"handle": "123456789/6547",
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"key": "dc.rights",
|
||||||
|
"value": "<p>© 2005-2016 JOHN DOE SOME RIGHTS RESERVED</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description",
|
||||||
|
"value": "<p class='lead'>Another introductory text dolor sit amet, consectetur adipiscing elit. Duis laoreet lorem erat, eget auctor est ultrices quis. Nullam ac tincidunt quam. In nec nisl odio. In egestas aliquam tincidunt.</p>\r\n<p>Integer vitae diam id dolor pharetra dignissim in sed enim. Vivamus pulvinar tristique sem a iaculis. Aenean ultricies dui vel facilisis laoreet. Integer porta erat eu ultrices rhoncus. Sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum.</p>",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.abstract",
|
||||||
|
"value": "Another collection for testing purposes",
|
||||||
|
"language": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "dc.description.tableofcontents",
|
||||||
|
"value": "<p>Some more news sed condimentum malesuada ex sit amet ullamcorper. Morbi a ipsum dolor. Vivamus interdum eget lacus ut fermentum. Donec sed ultricies erat, nec sollicitudin mauris. Duis varius nulla quis quam vulputate, at hendrerit turpis rutrum. Integer nec facilisis sapien. Fusce fringilla malesuada lectus id pulvinar. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae</p>",
|
||||||
|
"language": null
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -10,5 +10,5 @@
|
|||||||
* ];
|
* ];
|
||||||
**/
|
**/
|
||||||
export const routes: string[] = [
|
export const routes: string[] = [
|
||||||
'home', 'items/:id' , 'collections/:id', '**'
|
'home', 'items/:id' , 'collections/:id', 'communities/:id', '**'
|
||||||
];
|
];
|
||||||
|
@@ -5330,7 +5330,7 @@ rxjs@5.0.0-beta.12, rxjs@^5.0.0-beta.12:
|
|||||||
dependencies:
|
dependencies:
|
||||||
symbol-observable "^1.0.1"
|
symbol-observable "^1.0.1"
|
||||||
|
|
||||||
rxjs@^5.0.0-beta.12, rxjs@^5.0.1:
|
rxjs@^5.0.1:
|
||||||
version "5.4.0"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.0.tgz#a7db14ab157f9d7aac6a56e655e7a3860d39bf26"
|
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.0.tgz#a7db14ab157f9d7aac6a56e655e7a3860d39bf26"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Reference in New Issue
Block a user