Merge branch 'pr/100' into live-rest-backend

This commit is contained in:
Art Lowel
2017-06-02 15:14:02 +02:00
48 changed files with 393 additions and 160 deletions

View File

@@ -4,7 +4,6 @@
"link.dspace": "DSpace software", "link.dspace": "DSpace software",
"link.duraspace": "DuraSpace" "link.duraspace": "DuraSpace"
}, },
"item": { "item": {
"page": { "page": {
"author": "Author", "author": "Author",
@@ -12,24 +11,31 @@
"date": "Date", "date": "Date",
"uri": "URI", "uri": "URI",
"files": "Files", "files": "Files",
"collections": "Collections" "collections": "Collections",
"filesection": {
"download": "Download",
"name": "Name:",
"format": "Format:",
"size": "Size:",
"description": "Description:"
},
"link": {
"simple": "Simple item page",
"full": "Full item page"
}
} }
}, },
"nav": { "nav": {
"home": "Home" "home": "Home"
}, },
"pagination": { "pagination": {
"results-per-page": "Results Per Page", "results-per-page": "Results Per Page",
"showing": { "showing": {
"label" : "Now showing items ", "label": "Now showing items ",
"detail": "{{ range }} of {{ total }}" "detail": "{{ range }} of {{ total }}"
} }
}, },
"title": "DSpace", "title": "DSpace",
"404": { "404": {
"help": "We can't find the page you're looking for. The page may have been moved or deleted. You can use the button below to get back to the home page. ", "help": "We can't find the page you're looking for. The page may have been moved or deleted. You can use the button below to get back to the home page. ",
"page-not-found": "page not found", "page-not-found": "page not found",
@@ -37,7 +43,6 @@
"home-page": "Take me to the home page" "home-page": "Take me to the home page"
} }
}, },
"home": { "home": {
"top-level-communities": { "top-level-communities": {
"head": "Communities in DSpace", "head": "Communities in DSpace",

View File

@@ -135,9 +135,12 @@ export class RemoteDataBuildService {
}); });
}, 0); }, 0);
links[relationship] = normalized[relationship].map((href: string) => { let rdArr = [];
return this.buildSingle(href, resourceConstructor); normalized[relationship].forEach((href: string) => {
rdArr.push(this.buildSingle(href, resourceConstructor));
}); });
links[relationship] = this.aggregate(rdArr);
} }
else { else {
// without the setTimeout, the actions inside requestService.configure // without the setTimeout, the actions inside requestService.configure
@@ -154,4 +157,49 @@ export class RemoteDataBuildService {
const domainModel = getMapsTo(normalized.constructor); const domainModel = getMapsTo(normalized.constructor);
return Object.assign(new domainModel(), normalized, links); return Object.assign(new domainModel(), normalized, links);
} }
aggregate<T>(input: RemoteData<T>[]): RemoteData<T[]> {
const requestPending = Observable.combineLatest(
...input.map(rd => rd.isRequestPending),
).map((...pendingArray) => pendingArray.every(e => e === true))
.distinctUntilChanged();
const responsePending = Observable.combineLatest(
...input.map(rd => rd.isResponsePending),
).map((...pendingArray) => pendingArray.every(e => e === true))
.distinctUntilChanged();
const isSuccessFul = Observable.combineLatest(
...input.map(rd => rd.hasSucceeded),
).map((...successArray) => successArray.every(e => e === true))
.distinctUntilChanged();
const errorMessage = Observable.combineLatest(
...input.map(rd => rd.errorMessage),
).map((...errors) => errors
.map((e, idx) => {
if (hasValue(e)) {
return `[${idx}]: ${e}`;
}
})
.filter(e => hasValue(e))
.join(", ")
);
const payload = <Observable<T[]>> Observable.combineLatest(
...input.map(rd => rd.payload)
);
return new RemoteData(
// This is an aggregated object, it doesn't necessarily correspond
// to a single REST endpoint, so instead of a self link, use the
// current time in ms for a somewhat unique id
`${new Date().getTime()}`,
requestPending,
responsePending,
isSuccessFul,
errorMessage,
payload
);
}
} }

View File

@@ -21,8 +21,14 @@ export class NormalizedBitstream extends NormalizedDSpaceObject {
/** /**
* The mime type of this Bitstream * The mime type of this Bitstream
*/ */
@autoserialize
mimetype: string; mimetype: string;
/**
* The format of this Bitstream
*/
format: string;
/** /**
* The description of this Bitstream * The description of this Bitstream
*/ */

View File

@@ -27,7 +27,7 @@ export class Bitstream extends DSpaceObject {
/** /**
* An array of Bundles that are direct parents of this Bitstream * An array of Bundles that are direct parents of this Bitstream
*/ */
parents: Array<RemoteData<Bundle>>; parents: RemoteData<Bundle[]>;
/** /**
* The Bundle that owns this Bitstream * The Bundle that owns this Bitstream

View File

@@ -12,13 +12,13 @@ export class Bundle extends DSpaceObject {
/** /**
* An array of Items that are direct parents of this Bundle * An array of Items that are direct parents of this Bundle
*/ */
parents: Array<RemoteData<Item>>; parents: RemoteData<Item[]>;
/** /**
* The Item that owns this Bundle * The Item that owns this Bundle
*/ */
owner: Item; owner: Item;
bitstreams: Array<RemoteData<Bitstream>> bitstreams: RemoteData<Bitstream[]>
} }

View File

@@ -58,13 +58,13 @@ export class Collection extends DSpaceObject {
/** /**
* An array of Collections that are direct parents of this Collection * An array of Collections that are direct parents of this Collection
*/ */
parents: Array<RemoteData<Collection>>; parents: RemoteData<Collection[]>;
/** /**
* The Collection that owns this Collection * The Collection that owns this Collection
*/ */
owner: Collection; owner: Collection;
items: Array<RemoteData<Item>>; items: RemoteData<Item[]>;
} }

View File

@@ -50,13 +50,13 @@ export class Community extends DSpaceObject {
/** /**
* An array of Communities that are direct parents of this Community * An array of Communities that are direct parents of this Community
*/ */
parents: Array<RemoteData<DSpaceObject>>; parents: RemoteData<DSpaceObject[]>;
/** /**
* The Community that owns this Community * The Community that owns this Community
*/ */
owner: Community; owner: Community;
collections: Array<RemoteData<Collection>>; collections: RemoteData<Collection[]>;
} }

View File

@@ -39,7 +39,7 @@ export abstract class DSpaceObject implements CacheableObject {
/** /**
* An array of DSpaceObjects that are direct parents of this DSpaceObject * An array of DSpaceObjects that are direct parents of this DSpaceObject
*/ */
parents: Array<RemoteData<DSpaceObject>>; parents: RemoteData<DSpaceObject[]>;
/** /**
* The DSpaceObject that owns this DSpaceObject * The DSpaceObject that owns this DSpaceObject

View File

@@ -17,8 +17,7 @@ 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 remoteThumbnailBundle; let remoteBundles;
let remoteOriginalBundle;
let thumbnailBundle; let thumbnailBundle;
let originalBundle; let originalBundle;
@@ -27,34 +26,33 @@ describe('Item', () => {
retrieve: thumbnailPath retrieve: thumbnailPath
}; };
const bitstream1 = { const bitstreams = [{
retrieve: bitstream1Path retrieve: bitstream1Path
}; }, {
const bitstream2 = {
retrieve: bitstream2Path retrieve: bitstream2Path
}; }];
const remoteDataThumbnail = createRemoteDataObject(thumbnail); const remoteDataThumbnail = createRemoteDataObject(thumbnail);
const remoteDataFile1 = createRemoteDataObject(bitstream1); const remoteDataFiles = createRemoteDataObject(bitstreams);
const remoteDataFile2 = createRemoteDataObject(bitstream2);
// Create Bundles // Create Bundles
thumbnailBundle = { const bundles =
[
{
name: thumbnailBundleName, name: thumbnailBundleName,
primaryBitstream: remoteDataThumbnail primaryBitstream: remoteDataThumbnail
}; },
originalBundle = { {
name: originalBundleName, name: originalBundleName,
bitstreams: [remoteDataFile1, remoteDataFile2] bitstreams: remoteDataFiles
}; }];
remoteThumbnailBundle = createRemoteDataObject(thumbnailBundle); remoteBundles = createRemoteDataObject(bundles);
remoteOriginalBundle = createRemoteDataObject(originalBundle);
item = Object.assign(new Item(), { bundles: [remoteThumbnailBundle, remoteOriginalBundle] }); item = Object.assign(new Item(), { bundles: remoteBundles });
}); });
@@ -68,11 +66,10 @@ describe('Item', () => {
it('should return null when no bundle with this name exists for this item', () => { it('should return null when no bundle with this name exists for this item', () => {
let name: string = nonExistingBundleName; let name: string = nonExistingBundleName;
let bundle: Observable<Bundle> = item.getBundle(name); let bundle: Observable<Bundle> = item.getBundle(name);
bundle.map(b => expect(b).toBeNull()); bundle.map(b => expect(b).toBeUndefined());
}); });
describe("get thumbnail", () => { describe("get thumbnail", () => {
beforeEach(() => { beforeEach(() => {
spyOn(item, 'getBundle').and.returnValue(Observable.of(thumbnailBundle)); spyOn(item, 'getBundle').and.returnValue(Observable.of(thumbnailBundle));
@@ -94,17 +91,15 @@ describe('Item', () => {
it('should return all files in the ORIGINAL bundle', () => { it('should return all files in the ORIGINAL bundle', () => {
let paths = [bitstream1Path, bitstream2Path]; let paths = [bitstream1Path, bitstream2Path];
let files: Observable<Array<Observable<Bitstream>>> = item.getFiles(); let files: Observable<Bitstream[]> = item.getFiles();
let index = 0; let index = 0;
files.map(f => expect(f.length).toBe(2)); files.map(f => expect(f.length).toBe(2));
files.subscribe( files.subscribe(
array => array.forEach( array => array.forEach(
observableFile => observableFile.subscribe( file => {
file => { expect(file.retrieve).toBe(paths[index]);
expect(file.retrieve).toBe(paths[index]); index++;
index++; }
}
)
) )
) )
}); });

View File

@@ -31,14 +31,14 @@ export class Item extends DSpaceObject {
/** /**
* An array of Collections that are direct parents of this Item * An array of Collections that are direct parents of this Item
*/ */
parents: Array<RemoteData<Collection>>; parents: RemoteData<Collection[]>;
/** /**
* The Collection that owns this Item * The Collection that owns this Item
*/ */
owner: Collection; owner: Collection;
bundles: Array<RemoteData<Bundle>>; bundles: RemoteData<Bundle[]>;
/** /**
@@ -47,29 +47,40 @@ export class Item extends DSpaceObject {
*/ */
getThumbnail(): Observable<Bitstream> { getThumbnail(): Observable<Bitstream> {
const bundle: Observable<Bundle> = this.getBundle("THUMBNAIL"); const bundle: Observable<Bundle> = this.getBundle("THUMBNAIL");
return bundle.flatMap( return bundle
bundle => { .filter(bundle => hasValue(bundle))
if (bundle != null) { .flatMap(bundle => bundle.primaryBitstream.payload)
return bundle.primaryBitstream.payload; .startWith(undefined);
} }
else {
return Observable.of(undefined); /**
} * Retrieves the thumbnail for the given original of this item
} * @returns {Observable<Bitstream>} the primaryBitstream of the "THUMBNAIL" bundle
); */
getThumbnailForOriginal(original: Bitstream): Observable<Bitstream> {
const bundle: Observable<Bundle> = this.getBundle("THUMBNAIL");
return bundle
.filter(bundle => hasValue(bundle))
.flatMap(bundle => bundle
.bitstreams.payload.map(files => files
.find(thumbnail => thumbnail
.name.startsWith(original.name)
)
)
)
.startWith(undefined);;
} }
/** /**
* Retrieves all files that should be displayed on the item page of this item * Retrieves all files that should be displayed on the item page of this item
* @returns {Observable<Array<Observable<Bitstream>>>} an array of all Bitstreams in the "ORIGINAL" bundle * @returns {Observable<Array<Observable<Bitstream>>>} an array of all Bitstreams in the "ORIGINAL" bundle
*/ */
getFiles(): Observable<Array<Observable<Bitstream>>> { getFiles(name: String = "ORIGINAL"): Observable<Bitstream[]> {
const bundle: Observable <Bundle> = this.getBundle("ORIGINAL"); const bundle: Observable <Bundle> = this.getBundle(name);
return bundle.map(bundle => { return bundle
if (hasValue(bundle) && Array.isArray(bundle.bitstreams)) { .filter(bundle => hasValue(bundle))
return bundle.bitstreams.map(bitstream => bitstream.payload) .flatMap(bundle => bundle.bitstreams.payload)
} .startWith([]);
});
} }
/** /**
@@ -78,22 +89,14 @@ export class Item extends DSpaceObject {
* @returns {Observable<Bundle>} the Bundle that belongs to this item with the given name * @returns {Observable<Bundle>} the Bundle that belongs to this item with the given name
*/ */
getBundle(name: String): Observable<Bundle> { getBundle(name: String): Observable<Bundle> {
return Observable.combineLatest( return this.bundles.payload
...this.bundles.map(b => b.payload), .filter(bundles => hasValue(bundles))
(...bundles: Array<Bundle>) => bundles)
.map(bundles => { .map(bundles => {
return bundles.find((bundle: Bundle) => { return bundles.find((bundle: Bundle) => {
return bundle.name === name return bundle.name === name
}); });
}); })
} .startWith(undefined);
/**
* Retrieves all direct parent collections of this item
* @returns {Array<Observable<Collection>>} an array of all Collections that contain this item
*/
getCollections(): Array<Observable<Collection>> {
return this.parents.map(collection => collection.payload.map(parent => parent));
} }
} }

View File

@@ -1,7 +0,0 @@
<ds-metadata-field-wrapper [label]="label | translate">
<div class="collections">
<a *ngFor="let collection of collections; let last=last;" [href]="(collection | async)?.self">
<span>{{(collection | async)?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
</a>
</div>
</ds-metadata-field-wrapper>

View File

@@ -0,0 +1,7 @@
<ds-metadata-field-wrapper [label]="label | translate">
<div class="collections">
<a *ngFor="let collection of (collections | async); let last=last;" [href]="collection?.self">
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
</a>
</div>
</ds-metadata-field-wrapper>

View File

@@ -1,7 +1,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Collection } from "../../core/shared/collection.model"; import { Collection } from "../../../core/shared/collection.model";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { Item } from "../../core/shared/item.model"; import { Item } from "../../../core/shared/item.model";
/** /**
* This component renders the parent collections section of the item * This component renders the parent collections section of the item
@@ -20,7 +20,7 @@ export class CollectionsComponent implements OnInit {
separator: string = "<br/>" separator: string = "<br/>"
collections: Array<Observable<Collection>>; collections: Observable<Collection[]>;
constructor() { constructor() {
this.universalInit(); this.universalInit();
@@ -31,7 +31,7 @@ export class CollectionsComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.collections = this.item.getCollections(); this.collections = this.item.parents.payload;
} }

View File

@@ -1,4 +1,4 @@
@import '../../../styles/variables.scss'; @import '../../../../styles/variables.scss';
:host { :host {
.simple-view-element { .simple-view-element {
margin-bottom: 15px; margin-bottom: 15px;

View File

@@ -0,0 +1 @@
@import '../../../../styles/variables.scss';

View File

@@ -0,0 +1 @@
@import '../../../../styles/variables.scss';

View File

@@ -0,0 +1,29 @@
<ds-metadata-field-wrapper [label]="label | translate">
<div class="file-section row" *ngFor="let file of (files | async); let last=last;">
<div class="col-3">
<ds-thumbnail [thumbnail]="thumbnails.get(file.id) | async"></ds-thumbnail>
</div>
<div class="col-7">
<dl class="row">
<dt class="col-md-4">{{"item.page.filesection.name" | translate}}</dt>
<dd class="col-md-8">{{file.name}}</dd>
<dt class="col-md-4">{{"item.page.filesection.size" | translate}}</dt>
<dd class="col-md-8">{{(file.size) | dsFileSize }}</dd>
<dt class="col-md-4">{{"item.page.filesection.format" | translate}}</dt>
<dd class="col-md-8">{{(file.mimetype)}}</dd>
<dt class="col-md-4">{{"item.page.filesection.description" | translate}}</dt>
<dd class="col-md-8">{{file.findMetadata("dc.description")}}</dd>
</dl>
</div>
<div class="col-2">
<a [href]="file.retrieve">
{{"item.page.filesection.download" | translate}}
</a>
</div>
</div>
</ds-metadata-field-wrapper>

View File

@@ -0,0 +1,7 @@
@import '../../../../../styles/variables.scss';
@import '../../../../../../node_modules/bootstrap/scss/_variables.scss';
@media screen and (min-width: map-get($grid-breakpoints, md)) {
dt {
text-align: right;
}
}

View File

@@ -0,0 +1,52 @@
import { Component, Input, OnInit } from '@angular/core';
import { Bitstream } from "../../../../core/shared/bitstream.model";
import { Item } from "../../../../core/shared/item.model";
import { Observable } from "rxjs";
import { FileSectionComponent } from "../../../simple/field-components/file-section/file-section.component";
import { hasValue } from "../../../../shared/empty.util";
/**
* This component renders the file section of the item
* inside a 'ds-metadata-field-wrapper' component.
*/
@Component({
selector: 'ds-item-page-full-file-section',
styleUrls: ['./full-file-section.component.css'],
templateUrl: './full-file-section.component.html'
})
export class FullFileSectionComponent extends FileSectionComponent implements OnInit {
@Input() item: Item;
label : string;
files: Observable<Bitstream[]>;
thumbnails: Map<string, Observable<Bitstream>> = new Map();
universalInit() {
}
ngOnInit(): void {
super.ngOnInit();
}
initialize(): void {
const originals = this.item.getFiles("ORIGINAL");
const licenses = this.item.getFiles("LICENSE");
this.files = Observable.combineLatest(originals, licenses, (originals, licenses) => [...originals, ...licenses]);
this.files.subscribe(
files =>
files.forEach(
original => {
const thumbnail: Observable<Bitstream> = this.item.getThumbnailForOriginal(original);
this.thumbnails.set(original.id, thumbnail);
}
)
)
}
}

View File

@@ -0,0 +1,25 @@
<div class="item-page" *ngIf="item.hasSucceeded | async">
<ds-item-page-title-field [item]="item.payload | async"></ds-item-page-title-field>
<div class="simple-view-link">
<a class="btn btn-secondary col-4" [routerLink]="['/items/' + (item.payload | async)?.id]">
{{"item.page.link.simple" | translate}}
</a>
</div>
<table class="table table-responsive table-striped">
<tbody>
<tr *ngFor="let metadatum of (metadata | async)">
<td>{{metadatum.key}}</td>
<td>{{metadatum.value}}</td>
<td>{{metadatum.language}}</td>
</tr>
</tbody>
</table>
<ds-item-page-full-file-section [item]="item.payload | async"></ds-item-page-full-file-section>
<ds-item-page-collections [item]="item.payload | async"></ds-item-page-collections>
</div>

View File

@@ -0,0 +1,7 @@
@import '../../../styles/variables.scss';
:host {
div.simple-view-link {
text-align: center;
margin: 20px;
}
}

View File

@@ -0,0 +1,45 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from "rxjs";
import { ItemPageComponent } from "../simple/item-page.component";
import { Metadatum } from "../../core/shared/metadatum.model";
import { ItemDataService } from "../../core/data/item-data.service";
import { ActivatedRoute } from "@angular/router";
import { RemoteData } from "../../core/data/remote-data";
import { Item } from "../../core/shared/item.model";
/**
* This component renders a simple item page.
* The route parameter 'id' is used to request the item it represents.
* All fields of the item that should be displayed, are defined in its template.
*/
@Component({
selector: 'ds-full-item-page',
styleUrls: ['./full-item-page.component.css'],
templateUrl: './full-item-page.component.html',
})
export class FullItemPageComponent extends ItemPageComponent implements OnInit {
item: RemoteData<Item>;
metadata: Observable<Array<Metadatum>>;
constructor(route: ActivatedRoute, items: ItemDataService) {
super(route, items);
}
universalInit() {
}
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
ngOnInit(): void {
super.ngOnInit();
}
initialize(params) {
super.initialize(params);
this.metadata = this.item.payload.map(i => i.metadata);
}
}

View File

@@ -1,12 +1,14 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { ItemPageComponent } from './item-page.component'; import { ItemPageComponent } from './simple/item-page.component';
import { FullItemPageComponent } from './full/full-item-page.component';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ path: 'items/:id', pathMatch: 'full', component: ItemPageComponent }, { path: 'items/:id', pathMatch: 'full', component: ItemPageComponent },
{ path: 'items/:id/full', component: FullItemPageComponent },
]) ])
] ]
}) })

View File

@@ -1 +0,0 @@
@import '../../styles/variables.scss';

View File

@@ -1,23 +1,26 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ItemPageComponent } from './item-page.component'; import { ItemPageComponent } from './simple/item-page.component';
import { ItemPageRoutingModule } from './item-page-routing.module'; import { ItemPageRoutingModule } from './item-page-routing.module';
import { MetadataValuesComponent } from './metadata-values/metadata-values.component'; import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
import { MetadataUriValuesComponent } from './metadata-uri-values/metadata-uri-values.component'; import { MetadataUriValuesComponent } from './field-components/metadata-uri-values/metadata-uri-values.component';
import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata-field-wrapper.component'; import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
import { ItemPageAuthorFieldComponent } from './specific-field/author/item-page-author-field.component'; import { ItemPageAuthorFieldComponent } from './simple/field-components/specific-field/author/item-page-author-field.component';
import { ItemPageDateFieldComponent } from './specific-field/date/item-page-date-field.component'; import { ItemPageDateFieldComponent } from './simple/field-components/specific-field/date/item-page-date-field.component';
import { ItemPageAbstractFieldComponent } from './specific-field/abstract/item-page-abstract-field.component'; import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageUriFieldComponent } from './specific-field/uri/item-page-uri-field.component'; import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
import { ItemPageTitleFieldComponent } from './specific-field/title/item-page-title-field.component'; import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
import { ItemPageSpecificFieldComponent } from './specific-field/item-page-specific-field.component'; import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component';
import { SharedModule } from './../shared/shared.module'; import { SharedModule } from './../shared/shared.module';
import { FileSectionComponent } from "./file-section/file-section.component"; import { FileSectionComponent } from "./simple/field-components/file-section/file-section.component";
import { CollectionsComponent } from "./collections/collections.component"; import { CollectionsComponent } from "./field-components/collections/collections.component";
import { FullItemPageComponent } from "./full/full-item-page.component";
import { FullFileSectionComponent } from "./full/field-components/file-section/full-file-section.component";
@NgModule({ @NgModule({
declarations: [ declarations: [
ItemPageComponent, ItemPageComponent,
FullItemPageComponent,
MetadataValuesComponent, MetadataValuesComponent,
MetadataUriValuesComponent, MetadataUriValuesComponent,
MetadataFieldWrapperComponent, MetadataFieldWrapperComponent,
@@ -28,7 +31,8 @@ import { CollectionsComponent } from "./collections/collections.component";
ItemPageTitleFieldComponent, ItemPageTitleFieldComponent,
ItemPageSpecificFieldComponent, ItemPageSpecificFieldComponent,
FileSectionComponent, FileSectionComponent,
CollectionsComponent CollectionsComponent,
FullFileSectionComponent
], ],
imports: [ imports: [
ItemPageRoutingModule, ItemPageRoutingModule,

View File

@@ -1 +0,0 @@
@import '../../../styles/variables.scss';

View File

@@ -1,8 +1,8 @@
<ds-metadata-field-wrapper [label]="label | translate"> <ds-metadata-field-wrapper [label]="label | translate">
<div class="file-section"> <div class="file-section">
<a *ngFor="let file of (files | async); let last=last;" [href]="(file | async)?.retrieve"> <a *ngFor="let file of (files | async); let last=last;" [href]="file?.retrieve">
<span>{{(file | async)?.name}}</span> <span>{{file?.name}}</span>
<span>({{((file | async)?.size) | dsFileSize }})</span> <span>({{(file?.size) | dsFileSize }})</span>
<span *ngIf="!last" innerHTML="{{separator}}"></span> <span *ngIf="!last" innerHTML="{{separator}}"></span>
</a> </a>
</div> </div>

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Bitstream } from "../../core/shared/bitstream.model"; import { Bitstream } from "../../../../core/shared/bitstream.model";
import { Item } from "../../core/shared/item.model"; import { Item } from "../../../../core/shared/item.model";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
/** /**
@@ -18,21 +18,23 @@ export class FileSectionComponent implements OnInit {
label : string = "item.page.files"; label : string = "item.page.files";
separator: string = "<br/>" separator: string = "<br/>";
files: Observable<Array<Observable<Bitstream>>>; files: Observable<Bitstream[]>;
constructor() { constructor() {
this.universalInit(); this.universalInit();
} }
universalInit() { universalInit() {
} }
ngOnInit(): void { ngOnInit(): void {
this.initialize();
}
initialize(): void {
this.files = this.item.getFiles(); this.files = this.item.getFiles();
} }
} }

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../../core/shared/item.model"; import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component"; import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
@Component({ @Component({

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../../core/shared/item.model"; import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component"; import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
@Component({ @Component({

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../../core/shared/item.model"; import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component"; import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
@Component({ @Component({

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../core/shared/item.model"; import { Item } from "../../../../core/shared/item.model";
/** /**
* This component can be used to represent metadata on a simple item page. * This component can be used to represent metadata on a simple item page.

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../../core/shared/item.model"; import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component"; import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
@Component({ @Component({

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Item } from "../../../core/shared/item.model"; import { Item } from "../../../../../core/shared/item.model";
import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component"; import { ItemPageSpecificFieldComponent } from "../item-page-specific-field.component";
@Component({ @Component({

View File

@@ -8,12 +8,19 @@
<ds-item-page-file-section [item]="item.payload | async"></ds-item-page-file-section> <ds-item-page-file-section [item]="item.payload | async"></ds-item-page-file-section>
<ds-item-page-date-field [item]="item.payload | async"></ds-item-page-date-field> <ds-item-page-date-field [item]="item.payload | async"></ds-item-page-date-field>
<ds-item-page-author-field [item]="item.payload | async"></ds-item-page-author-field> <ds-item-page-author-field [item]="item.payload | async"></ds-item-page-author-field>
</div> </div>
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<ds-item-page-abstract-field <ds-item-page-abstract-field
[item]="item.payload | async"></ds-item-page-abstract-field> [item]="item.payload | async"></ds-item-page-abstract-field>
<ds-item-page-uri-field [item]="item.payload | async"></ds-item-page-uri-field> <ds-item-page-uri-field [item]="item.payload | async"></ds-item-page-uri-field>
<ds-item-page-collections [item]="item.payload | async"></ds-item-page-collections> <ds-item-page-collections [item]="item.payload | async"></ds-item-page-collections>
<div>
<a class="btn btn-secondary" [routerLink]="['/items/' + (item.payload | async)?.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Item } from "../core/shared/item.model"; import { Item } from "../../core/shared/item.model";
import { ItemDataService } from "../core/data/item-data.service"; import { ItemDataService } from "../../core/data/item-data.service";
import { RemoteData } from "../core/data/remote-data"; import { RemoteData } from "../../core/data/remote-data";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { Bitstream } from "../core/shared/bitstream.model"; import { Bitstream } from "../../core/shared/bitstream.model";
/** /**
* This component renders a simple item page. * This component renders a simple item page.
@@ -37,11 +37,16 @@ export class ItemPageComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.sub = this.route.params.subscribe(params => { this.sub = this.route.params.subscribe(params => {
this.id = +params['id']; this.initialize(params);
this.item = this.items.findById(params['id']);
this.thumbnail = this.item.payload.flatMap(i => i.getThumbnail());
}); });
} }
initialize(params) {
this.id = +params['id'];
this.item = this.items.findById(params['id']);
this.thumbnail = this.item.payload.flatMap(i => i.getThumbnail());
}
} }

View File

@@ -82,22 +82,25 @@ function ngApp(req, res) {
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) { function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
console.warn('Error in SSR, serving for direct CSR'); console.warn('Error in SSR, serving for direct CSR');
res.sendFile('index.html', { root: './src' }); res.sendFile('index.html', { root: './src' });
return false;
} }
Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => { if (EnvConfig.universal.preboot) {
res.render('index', { Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => {
req, res.render('index', {
res, req,
// time: true, // use this to determine what part of your app is slow only in development res,
async: EnvConfig.universal.async, // time: true, // use this to determine what part of your app is slow only in development
preboot: EnvConfig.universal.preboot, async: EnvConfig.universal.async,
baseUrl: EnvConfig.ui.nameSpace, preboot: EnvConfig.universal.preboot,
requestUrl: req.originalUrl, baseUrl: EnvConfig.ui.nameSpace,
originUrl: EnvConfig.ui.baseUrl requestUrl: req.originalUrl,
originUrl: EnvConfig.ui.baseUrl
});
}); });
}); }
else {
res.sendFile('index.html', { root: './src' });
}
} }
/** /**

View File

@@ -301,14 +301,10 @@ alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
amdefine@1.0.0: amdefine@1.0.0, amdefine@>=0.0.4:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
angular2-express-engine@2.1.0-rc.1: angular2-express-engine@2.1.0-rc.1:
version "2.1.0-rc.1" version "2.1.0-rc.1"
resolved "https://registry.yarnpkg.com/angular2-express-engine/-/angular2-express-engine-2.1.0-rc.1.tgz#79c8e481cde7ff1253b373cbf98de7c9fab4f215" resolved "https://registry.yarnpkg.com/angular2-express-engine/-/angular2-express-engine-2.1.0-rc.1.tgz#79c8e481cde7ff1253b373cbf98de7c9fab4f215"
@@ -3521,11 +3517,7 @@ lowercase-keys@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
lru-cache@2: lru-cache@2, lru-cache@2.2.x:
version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
lru-cache@2.2.x:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
@@ -4659,14 +4651,10 @@ punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
q@1.4.1: q@1.4.1, q@^1.1.2, q@^1.4.1:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
q@^1.1.2, q@^1.4.1:
version "1.5.0"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
qjobs@^1.1.4: qjobs@^1.1.4:
version "1.1.5" version "1.1.5"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
@@ -5063,13 +5051,13 @@ right-align@^0.1.1:
dependencies: dependencies:
align-text "^0.1.1" align-text "^0.1.1"
rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.6.0, rimraf@^2.6.1: rimraf@2, rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.6.0, rimraf@^2.6.1:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
dependencies: dependencies:
glob "^7.0.5" glob "^7.0.5"
rimraf@2.5.4, rimraf@^2.4.4: rimraf@2.5.4:
version "2.5.4" version "2.5.4"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04"
dependencies: dependencies: