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 @@
+
+
+
0">
+
{{"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
*/