From 173f14c41f323b61d5bed847d22043e8510b3405 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 11:48:17 +0100 Subject: [PATCH] 68729: ItemVersionsComponent + changes to Version(History) model and services --- resources/i18n/en.json5 | 16 +++ src/app/+item-page/item-page.module.ts | 4 +- .../item-versions.component.html | 44 +++++++ .../item-versions/item-versions.component.ts | 108 ++++++++++++++++++ .../simple/item-page.component.html | 1 + .../cache/models/normalized-item.model.ts | 8 ++ .../cache/models/normalized-version.model.ts | 8 ++ src/app/core/data/version-data.service.ts | 3 + .../core/data/version-history-data.service.ts | 40 ++++++- src/app/core/shared/item.model.ts | 6 + src/app/core/shared/version.model.ts | 7 ++ 11 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 src/app/+item-page/item-versions/item-versions.component.html create mode 100644 src/app/+item-page/item-versions/item-versions.component.ts diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 8d956d1a0e..3369b76594 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -981,6 +981,22 @@ + "item.version.history.head": "Version History", + + "item.version.history.selected": "Selected version", + + "item.version.history.table.version": "Version", + + "item.version.history.table.item": "Item", + + "item.version.history.table.editor": "Editor", + + "item.version.history.table.date": "Date", + + "item.version.history.table.summary": "Summary", + + + "journal.listelement.badge": "Journal", "journal.page.description": "Description", diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index 5c54becdde..bb90d56ad9 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -29,6 +29,7 @@ import { MetadataFieldWrapperComponent } from './field-components/metadata-field import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { AbstractIncrementalListComponent } from './simple/abstract-incremental-list/abstract-incremental-list.component'; +import { ItemVersionsComponent } from './item-versions/item-versions.component'; @NgModule({ imports: [ @@ -59,7 +60,8 @@ import { AbstractIncrementalListComponent } from './simple/abstract-incremental- MetadataRepresentationListComponent, RelatedEntitiesSearchComponent, TabbedRelatedEntitiesSearchComponent, - AbstractIncrementalListComponent + AbstractIncrementalListComponent, + ItemVersionsComponent ], exports: [ ItemComponent, diff --git a/src/app/+item-page/item-versions/item-versions.component.html b/src/app/+item-page/item-versions/item-versions.component.html new file mode 100644 index 0000000000..e14ff54569 --- /dev/null +++ b/src/app/+item-page/item-versions/item-versions.component.html @@ -0,0 +1,44 @@ +
+
+
+

{{"item.version.history.head" | translate}}

+ + + + + + + + + + + + + + + + + + + + +
{{"item.version.history.table.version" | translate}}{{"item.version.history.table.item" | translate}}{{"item.version.history.table.editor" | translate}}{{"item.version.history.table.date" | translate}}{{"item.version.history.table.summary" | translate}}
{{version?.version}} + + {{item?.handle}} + * + + + + {{eperson?.name}} + + {{version?.created}}{{version?.summary}}
+
* {{"item.version.history.selected" | translate}}
+
+
+
+
diff --git a/src/app/+item-page/item-versions/item-versions.component.ts b/src/app/+item-page/item-versions/item-versions.component.ts new file mode 100644 index 0000000000..3e67270450 --- /dev/null +++ b/src/app/+item-page/item-versions/item-versions.component.ts @@ -0,0 +1,108 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Item } from '../../core/shared/item.model'; +import { Version } from '../../core/shared/version.model'; +import { RemoteData } from '../../core/data/remote-data'; +import { Observable } from 'rxjs/internal/Observable'; +import { VersionHistory } from '../../core/shared/version-history.model'; +import { getAllSucceededRemoteData, getRemoteDataPayload } from '../../core/shared/operators'; +import { map, startWith, switchMap } from 'rxjs/operators'; +import { combineLatest as observableCombineLatest } from 'rxjs'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { VersionHistoryDataService } from '../../core/data/version-history-data.service'; +import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; + +@Component({ + selector: 'ds-item-versions', + templateUrl: './item-versions.component.html' +}) +/** + * Component listing all available versions of the history the provided item is a part of + */ +export class ItemVersionsComponent implements OnInit { + /** + * The item to display a version history for + */ + @Input() item: Item; + + /** + * The item's version + */ + versionRD$: Observable>; + + /** + * The item's full version history + */ + versionHistoryRD$: Observable>; + + /** + * The version history's list of versions + */ + versionsRD$: Observable>>; + + /** + * Verify if the list of versions has at least one e-person to display + * Used to hide the "Editor" column when no e-persons are present to display + */ + hasEpersons$: Observable; + + /** + * The amount of versions to display per page + */ + pageSize = 10; + + /** + * The page options to use for fetching the versions + * Start at page 1 and always use the set page size + */ + options = Object.assign(new PaginationComponentOptions(),{ + id: 'item-versions-options', + currentPage: 1, + pageSize: this.pageSize + }); + + /** + * The current page being displayed + */ + currentPage$ = new BehaviorSubject(1); + + constructor(private versionHistoryService: VersionHistoryDataService) { + } + + /** + * Initialize all observables + */ + ngOnInit(): void { + this.versionRD$ = this.item.version; + this.versionHistoryRD$ = this.versionRD$.pipe( + getAllSucceededRemoteData(), + getRemoteDataPayload(), + switchMap((version: Version) => version.versionhistory) + ); + const versionHistory$ = this.versionHistoryRD$.pipe( + getAllSucceededRemoteData(), + getRemoteDataPayload(), + ); + this.versionsRD$ = observableCombineLatest(versionHistory$, this.currentPage$).pipe( + switchMap(([versionHistory, page]: [VersionHistory, number]) => + this.versionHistoryService.getVersions(versionHistory.id, + new PaginatedSearchOptions({pagination: Object.assign({}, this.options, { currentPage: page })}))) + ); + this.hasEpersons$ = this.versionsRD$.pipe( + getAllSucceededRemoteData(), + getRemoteDataPayload(), + map((versions: PaginatedList) => versions.page.filter((version: Version) => version.eperson !== undefined).length > 0), + startWith(false) + ); + } + + /** + * Update the current page + * @param page + */ + switchPage(page: number) { + this.currentPage$.next(page); + } + +} diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index b4b32fb05c..3b942f9d33 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -3,6 +3,7 @@
+
diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts index 9b7edf70c0..06d13d158e 100644 --- a/src/app/core/cache/models/normalized-item.model.ts +++ b/src/app/core/cache/models/normalized-item.model.ts @@ -6,6 +6,7 @@ import { mapsTo, relationship } from '../builders/build-decorators'; import { Collection } from '../../shared/collection.model'; import { Relationship } from '../../shared/item-relationships/relationship.model'; import { Bundle } from '../../shared/bundle.model'; +import { Version } from '../../shared/version.model'; /** * Normalized model class for a DSpace Item @@ -69,4 +70,11 @@ export class NormalizedItem extends NormalizedDSpaceObject { @relationship(Relationship, true) relationships: string[]; + /** + * The version this item represents in its history + */ + @deserialize + @relationship(Version, false) + version: string; + } diff --git a/src/app/core/cache/models/normalized-version.model.ts b/src/app/core/cache/models/normalized-version.model.ts index b9781cbcb7..39b2fcab89 100644 --- a/src/app/core/cache/models/normalized-version.model.ts +++ b/src/app/core/cache/models/normalized-version.model.ts @@ -5,6 +5,7 @@ import { TypedObject } from '../object-cache.reducer'; import { NormalizedObject } from './normalized-object.model'; import { Item } from '../../shared/item.model'; import { VersionHistory } from '../../shared/version-history.model'; +import { EPerson } from '../../eperson/models/eperson.model'; /** * Normalized model class for a DSpace Version @@ -49,4 +50,11 @@ export class NormalizedVersion extends NormalizedObject implements Type @deserialize @relationship(Item, false) item: string; + + /** + * The e-person who created this version + */ + @deserialize + @relationship(EPerson, false) + eperson: string; } diff --git a/src/app/core/data/version-data.service.ts b/src/app/core/data/version-data.service.ts index cdc7de69f9..01db3a2997 100644 --- a/src/app/core/data/version-data.service.ts +++ b/src/app/core/data/version-data.service.ts @@ -14,6 +14,9 @@ import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; import { FindListOptions } from './request.models'; import { Observable } from 'rxjs/internal/Observable'; +/** + * Service responsible for handling requests related to the Version object + */ @Injectable() export class VersionDataService extends DataService { protected linkPath = 'versions'; diff --git a/src/app/core/data/version-history-data.service.ts b/src/app/core/data/version-history-data.service.ts index 5f1543b664..63d65cce02 100644 --- a/src/app/core/data/version-history-data.service.ts +++ b/src/app/core/data/version-history-data.service.ts @@ -11,12 +11,21 @@ import { HALEndpointService } from '../shared/hal-endpoint.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { HttpClient } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; -import { FindListOptions } from './request.models'; +import { FindListOptions, GetRequest } from './request.models'; import { Observable } from 'rxjs/internal/Observable'; +import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; +import { RemoteData } from './remote-data'; +import { PaginatedList } from './paginated-list'; +import { Version } from '../shared/version.model'; +import { map, switchMap, take } from 'rxjs/operators'; +/** + * Service responsible for handling requests related to the VersionHistory object + */ @Injectable() export class VersionHistoryDataService extends DataService { protected linkPath = 'versionhistories'; + protected versionsEndpoint = 'versions'; constructor( protected requestService: RequestService, @@ -37,4 +46,33 @@ export class VersionHistoryDataService extends DataService { getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable { return this.halService.getEndpoint(this.linkPath); } + + /** + * Get the versions endpoint for a version history + * @param versionHistoryId + */ + getVersionsEndpoint(versionHistoryId: number): Observable { + return this.getBrowseEndpoint().pipe( + switchMap((href: string) => this.halService.getEndpoint(this.versionsEndpoint, `${href}/${versionHistoryId}`)) + ); + } + + /** + * Get a version history's versions using paginated search options + * @param versionHistoryId The version history's ID + * @param searchOptions The search options to use + */ + getVersions(versionHistoryId: number, searchOptions?: PaginatedSearchOptions): Observable>> { + const hrefObs = this.getVersionsEndpoint(versionHistoryId).pipe( + map((href) => searchOptions ? searchOptions.toRestUrl(href) : href) + ); + hrefObs.pipe( + take(1) + ).subscribe((href) => { + const request = new GetRequest(this.requestService.generateRequestId(), href); + this.requestService.configure(request); + }); + + return this.rdbService.buildList(hrefObs); + } } diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index bd304274ab..668db36f84 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -14,6 +14,7 @@ import { Bundle } from './bundle.model'; import { GenericConstructor } from './generic-constructor'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator'; +import { Version } from './version.model'; /** * Class representing a DSpace Item @@ -67,6 +68,11 @@ export class Item extends DSpaceObject { relationships: Observable>>; + /** + * The version this item represents in its history + */ + version: Observable>; + /** * Retrieves the thumbnail of this item * @returns {Observable} the primaryBitstream of the 'THUMBNAIL' bundle diff --git a/src/app/core/shared/version.model.ts b/src/app/core/shared/version.model.ts index 521cfa2664..1f32af500f 100644 --- a/src/app/core/shared/version.model.ts +++ b/src/app/core/shared/version.model.ts @@ -7,6 +7,7 @@ import { Item } from './item.model'; import { RemoteData } from '../data/remote-data'; import { Observable } from 'rxjs/internal/Observable'; import { VersionHistory } from './version-history.model'; +import { EPerson } from '../eperson/models/eperson.model'; /** * Class representing a DSpace Version @@ -52,6 +53,12 @@ export class Version extends ListableObject implements CacheableObject { @excludeFromEquals item: Observable>; + /** + * The e-person who created this version + */ + @excludeFromEquals + eperson: Observable>; + /** * Method that returns as which type of object this object should be rendered */