From d4ff4aab36f316aaf7a0cfbfece33a9e569d1125 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 18 Feb 2020 16:05:38 +0100 Subject: [PATCH 01/13] 68729: Version and VersionHistory model, normalized and data-service classes --- .../normalized-version-history.model.ts | 26 ++++++++ .../cache/models/normalized-version.model.ts | 52 ++++++++++++++++ src/app/core/core.module.ts | 10 ++- src/app/core/data/version-data.service.ts | 40 ++++++++++++ .../core/data/version-history-data.service.ts | 40 ++++++++++++ src/app/core/shared/version-history.model.ts | 40 ++++++++++++ src/app/core/shared/version.model.ts | 61 +++++++++++++++++++ 7 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 src/app/core/cache/models/normalized-version-history.model.ts create mode 100644 src/app/core/cache/models/normalized-version.model.ts create mode 100644 src/app/core/data/version-data.service.ts create mode 100644 src/app/core/data/version-history-data.service.ts create mode 100644 src/app/core/shared/version-history.model.ts create mode 100644 src/app/core/shared/version.model.ts diff --git a/src/app/core/cache/models/normalized-version-history.model.ts b/src/app/core/cache/models/normalized-version-history.model.ts new file mode 100644 index 0000000000..336f589736 --- /dev/null +++ b/src/app/core/cache/models/normalized-version-history.model.ts @@ -0,0 +1,26 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { NormalizedObject } from './normalized-object.model'; +import { TypedObject } from '../object-cache.reducer'; +import { mapsTo, relationship } from '../builders/build-decorators'; +import { VersionHistory } from '../../shared/version-history.model'; +import { Version } from '../../shared/version.model'; + +/** + * Normalized model class for a DSpace Version History + */ +@mapsTo(VersionHistory) +@inheritSerialization(NormalizedObject) +export class NormalizedVersionHistory extends NormalizedObject implements TypedObject { + /** + * The identifier of this Version History + */ + @autoserialize + id: number; + + /** + * The list of versions within this history + */ + @deserialize + @relationship(Version, true) + versions: string[]; +} diff --git a/src/app/core/cache/models/normalized-version.model.ts b/src/app/core/cache/models/normalized-version.model.ts new file mode 100644 index 0000000000..b9781cbcb7 --- /dev/null +++ b/src/app/core/cache/models/normalized-version.model.ts @@ -0,0 +1,52 @@ +import { autoserialize, deserialize, inheritSerialization } from 'cerialize'; +import { Version } from '../../shared/version.model'; +import { mapsTo, relationship } from '../builders/build-decorators'; +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'; + +/** + * Normalized model class for a DSpace Version + */ +@mapsTo(Version) +@inheritSerialization(NormalizedObject) +export class NormalizedVersion extends NormalizedObject implements TypedObject { + /** + * The identifier of this Version + */ + @autoserialize + id: number; + + /** + * The version number of the version's history this version represents + */ + @autoserialize + version: number; + + /** + * The summary for the changes made in this version + */ + @autoserialize + summary: string; + + /** + * The Date this version was created + */ + @deserialize + created: Date; + + /** + * The full version history this version is apart of + */ + @deserialize + @relationship(VersionHistory, false) + versionhistory: string; + + /** + * The item this version represents + */ + @deserialize + @relationship(Item, false) + item: string; +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 1621c4081d..ce7769098f 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -142,6 +142,10 @@ import { NormalizedExternalSource } from './cache/models/normalized-external-sou import { NormalizedExternalSourceEntry } from './cache/models/normalized-external-source-entry.model'; import { ExternalSourceService } from './data/external-source.service'; import { LookupRelationService } from './data/lookup-relation.service'; +import { VersionDataService } from './data/version-data.service'; +import { NormalizedVersion } from './cache/models/normalized-version.model'; +import { NormalizedVersionHistory } from './cache/models/normalized-version-history.model'; +import { VersionHistoryDataService } from './data/version-history-data.service'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -257,6 +261,8 @@ const PROVIDERS = [ RelationshipTypeService, ExternalSourceService, LookupRelationService, + VersionDataService, + VersionHistoryDataService, // register AuthInterceptor as HttpInterceptor { provide: HTTP_INTERCEPTORS, @@ -304,7 +310,9 @@ export const normalizedModels = NormalizedRelationshipType, NormalizedItemType, NormalizedExternalSource, - NormalizedExternalSourceEntry + NormalizedExternalSourceEntry, + NormalizedVersion, + NormalizedVersionHistory ]; @NgModule({ diff --git a/src/app/core/data/version-data.service.ts b/src/app/core/data/version-data.service.ts new file mode 100644 index 0000000000..cdc7de69f9 --- /dev/null +++ b/src/app/core/data/version-data.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { DataService } from './data.service'; +import { Version } from '../shared/version.model'; +import { RequestService } from './request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { ObjectCacheService } from '../cache/object-cache.service'; +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 { Observable } from 'rxjs/internal/Observable'; + +@Injectable() +export class VersionDataService extends DataService { + protected linkPath = 'versions'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + /** + * Get the endpoint for browsing versions + */ + getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable { + return this.halService.getEndpoint(this.linkPath); + } +} diff --git a/src/app/core/data/version-history-data.service.ts b/src/app/core/data/version-history-data.service.ts new file mode 100644 index 0000000000..5f1543b664 --- /dev/null +++ b/src/app/core/data/version-history-data.service.ts @@ -0,0 +1,40 @@ +import { DataService } from './data.service'; +import { VersionHistory } from '../shared/version-history.model'; +import { Injectable } from '@angular/core'; +import { RequestService } from './request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { ObjectCacheService } from '../cache/object-cache.service'; +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 { Observable } from 'rxjs/internal/Observable'; + +@Injectable() +export class VersionHistoryDataService extends DataService { + protected linkPath = 'versionhistories'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + /** + * Get the endpoint for browsing versions + */ + getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable { + return this.halService.getEndpoint(this.linkPath); + } +} diff --git a/src/app/core/shared/version-history.model.ts b/src/app/core/shared/version-history.model.ts new file mode 100644 index 0000000000..de31bc9e0e --- /dev/null +++ b/src/app/core/shared/version-history.model.ts @@ -0,0 +1,40 @@ +import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { CacheableObject } from '../cache/object-cache.reducer'; +import { ResourceType } from './resource-type'; +import { excludeFromEquals } from '../utilities/equals.decorators'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../data/remote-data'; +import { GenericConstructor } from './generic-constructor'; +import { PaginatedList } from '../data/paginated-list'; +import { Version } from './version.model'; + +/** + * Class representing a DSpace Version History + */ +export class VersionHistory extends ListableObject implements CacheableObject { + static type = new ResourceType('versionhistory'); + + /** + * Link to itself + */ + @excludeFromEquals + self: string; + + /** + * The identifier of this Version History + */ + id: number; + + /** + * The list of versions within this history + */ + @excludeFromEquals + versions: Observable>>; + + /** + * Method that returns as which type of object this object should be rendered + */ + getRenderTypes(): Array> { + return [this.constructor as GenericConstructor]; + } +} diff --git a/src/app/core/shared/version.model.ts b/src/app/core/shared/version.model.ts new file mode 100644 index 0000000000..521cfa2664 --- /dev/null +++ b/src/app/core/shared/version.model.ts @@ -0,0 +1,61 @@ +import { ResourceType } from './resource-type'; +import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { CacheableObject } from '../cache/object-cache.reducer'; +import { excludeFromEquals } from '../utilities/equals.decorators'; +import { GenericConstructor } from './generic-constructor'; +import { Item } from './item.model'; +import { RemoteData } from '../data/remote-data'; +import { Observable } from 'rxjs/internal/Observable'; +import { VersionHistory } from './version-history.model'; + +/** + * Class representing a DSpace Version + */ +export class Version extends ListableObject implements CacheableObject { + static type = new ResourceType('version'); + + /** + * Link to itself + */ + @excludeFromEquals + self: string; + + /** + * The identifier of this Version + */ + id: number; + + /** + * The version number of the version's history this version represents + */ + version: number; + + /** + * The summary for the changes made in this version + */ + summary: string; + + /** + * The Date this version was created + */ + created: Date; + + /** + * The full version history this version is apart of + */ + @excludeFromEquals + versionhistory: Observable>; + + /** + * The item this version represents + */ + @excludeFromEquals + item: Observable>; + + /** + * Method that returns as which type of object this object should be rendered + */ + getRenderTypes(): Array> { + return [this.constructor as GenericConstructor]; + } +} From 173f14c41f323b61d5bed847d22043e8510b3405 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 11:48:17 +0100 Subject: [PATCH 02/13] 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 */ From d7da83ad5a08ec5e5b3959a5aa0ff057de5ec3bd Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 12:41:08 +0100 Subject: [PATCH 03/13] 68729: Pagination disableRouteParameterUpdate input --- .../shared/pagination/pagination.component.ts | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts index 9c378d1aff..04309b6f9f 100644 --- a/src/app/shared/pagination/pagination.component.ts +++ b/src/app/shared/pagination/pagination.component.ts @@ -99,6 +99,13 @@ export class PaginationComponent implements OnDestroy, OnInit { */ @Input() public hidePagerWhenSinglePage = true; + /** + * Option for disabling updating and reading route parameters on pagination changes + * In other words, changing pagination won't add or update the url parameters on the current page, and the url + * parameters won't affect the pagination of this component + */ + @Input() public disableRouteParameterUpdate = false; + /** * Current page. */ @@ -173,20 +180,35 @@ export class PaginationComponent implements OnDestroy, OnInit { this.checkConfig(this.paginationOptions); this.initializeConfig(); // Listen to changes - this.subs.push(this.route.queryParams - .subscribe((queryParams) => { - if (this.isEmptyPaginationParams(queryParams)) { - this.initializeConfig(queryParams); + if (!this.disableRouteParameterUpdate) { + this.subs.push(this.route.queryParams + .subscribe((queryParams) => { + this.initializeParams(queryParams); + })); + } + } + + /** + * Initialize the route and current parameters + * This method will fix any invalid or missing parameters + * @param params + */ + private initializeParams(params) { + if (this.isEmptyPaginationParams(params)) { + this.initializeConfig(params); + } else { + this.currentQueryParams = params; + const fixedProperties = this.validateParams(params); + if (isNotEmpty(fixedProperties)) { + if (!this.disableRouteParameterUpdate) { + this.fixRoute(fixedProperties); } else { - this.currentQueryParams = queryParams; - const fixedProperties = this.validateParams(queryParams); - if (isNotEmpty(fixedProperties)) { - this.fixRoute(fixedProperties); - } else { - this.setFields(); - } + this.initializeParams(fixedProperties); } - })); + } else { + this.setFields(); + } + } } private fixRoute(fixedProperties) { @@ -247,7 +269,7 @@ export class PaginationComponent implements OnDestroy, OnInit { * The page being navigated to. */ public doPageChange(page: number) { - this.updateRoute({ pageId: this.id, page: page.toString() }); + this.updateParams(Object.assign({}, this.currentQueryParams, { pageId: this.id, page: page.toString() })); } /** @@ -257,7 +279,7 @@ export class PaginationComponent implements OnDestroy, OnInit { * The page size being navigated to. */ public doPageSizeChange(pageSize: number) { - this.updateRoute({ pageId: this.id, page: 1, pageSize: pageSize }); + this.updateParams(Object.assign({}, this.currentQueryParams,{ pageId: this.id, page: 1, pageSize: pageSize })); } /** @@ -267,7 +289,7 @@ export class PaginationComponent implements OnDestroy, OnInit { * The sort direction being navigated to. */ public doSortDirectionChange(sortDirection: SortDirection) { - this.updateRoute({ pageId: this.id, page: 1, sortDirection: sortDirection }); + this.updateParams(Object.assign({}, this.currentQueryParams,{ pageId: this.id, page: 1, sortDirection: sortDirection })); } /** @@ -277,7 +299,7 @@ export class PaginationComponent implements OnDestroy, OnInit { * The sort field being navigated to. */ public doSortFieldChange(field: string) { - this.updateRoute({ pageId: this.id, page: 1, sortField: field }); + this.updateParams(Object.assign(this.currentQueryParams,{ pageId: this.id, page: 1, sortField: field })); } /** @@ -347,6 +369,20 @@ export class PaginationComponent implements OnDestroy, OnInit { }) } + /** + * Update the current query params and optionally update the route + * @param params + */ + private updateParams(params: {}) { + if (isNotEmpty(difference(params, this.currentQueryParams))) { + if (!this.disableRouteParameterUpdate) { + this.updateRoute(params); + } else { + this.initializeParams(params); + } + } + } + /** * Method to update the route parameters */ From 22ff110e9df22fd4e91b092511e477bed41c01d2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 12:43:06 +0100 Subject: [PATCH 04/13] 68729: disableRouteParameterUpdate for version list pagination --- src/app/+item-page/item-versions/item-versions.component.html | 1 + src/app/+item-page/item-versions/item-versions.component.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/app/+item-page/item-versions/item-versions.component.html b/src/app/+item-page/item-versions/item-versions.component.html index e14ff54569..0fb6bf08cc 100644 --- a/src/app/+item-page/item-versions/item-versions.component.html +++ b/src/app/+item-page/item-versions/item-versions.component.html @@ -7,6 +7,7 @@ [paginationOptions]="options" [pageInfoState]="versions" [collectionSize]="versions?.totalElements" + [disableRouteParameterUpdate]="true" (pageChange)="switchPage($event)"> diff --git a/src/app/+item-page/item-versions/item-versions.component.ts b/src/app/+item-page/item-versions/item-versions.component.ts index 3e67270450..b493843be1 100644 --- a/src/app/+item-page/item-versions/item-versions.component.ts +++ b/src/app/+item-page/item-versions/item-versions.component.ts @@ -102,6 +102,7 @@ export class ItemVersionsComponent implements OnInit { * @param page */ switchPage(page: number) { + this.options.currentPage = page; this.currentPage$.next(page); } From 179fbd527695fdbb2a3621d22c89a0f210004988 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 15:43:20 +0100 Subject: [PATCH 05/13] 68729: Item version history on full page --- src/app/+item-page/full/full-item-page.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/+item-page/full/full-item-page.component.html b/src/app/+item-page/full/full-item-page.component.html index c453df6bff..b93b8f1e12 100644 --- a/src/app/+item-page/full/full-item-page.component.html +++ b/src/app/+item-page/full/full-item-page.component.html @@ -21,6 +21,7 @@
+ From 29ff18264c95d89be66d7f4d9410049d77e66812 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Feb 2020 17:58:23 +0100 Subject: [PATCH 06/13] 68729: Edit Item Version History page + sidebar menu option - moving item-versions component to shared module --- resources/i18n/en.json5 | 5 +++ .../admin-sidebar/admin-sidebar.component.ts | 14 ++++++++ .../edit-item-page/edit-item-page.module.ts | 2 ++ .../edit-item-page.routing.module.ts | 16 +++++++++- .../item-version-history.component.html | 6 ++++ .../item-version-history.component.ts | 25 +++++++++++++++ .../+item-page/item-page-routing.module.ts | 5 +++ src/app/+item-page/item-page.module.ts | 2 -- ...item-version-history-selector.component.ts | 32 +++++++++++++++++++ .../item-versions.component.html | 6 ++-- .../item-versions/item-versions.component.ts | 32 +++++++++++++------ src/app/shared/shared.module.ts | 10 ++++-- 12 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html create mode 100644 src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts create mode 100644 src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts rename src/app/{+item-page => shared/item}/item-versions/item-versions.component.html (86%) rename src/app/{+item-page => shared/item}/item-versions/item-versions.component.ts (73%) diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 3369b76594..2e082f8a7e 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -980,9 +980,12 @@ "item.select.table.title": "Title", + "item.version.history.empty": "There are no other versions for this item yet.", "item.version.history.head": "Version History", + "item.version.history.return": "Return", + "item.version.history.selected": "Selected version", "item.version.history.table.version": "Version", @@ -1155,6 +1158,8 @@ "menu.section.edit_item": "Item", + "menu.section.edit_item_version_history": "Item Version History", + "menu.section.export": "Export", diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts index 72eb306bf1..e50d976dc9 100644 --- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts @@ -18,6 +18,7 @@ import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrapp import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; import {CreateItemParentSelectorComponent} from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; +import { EditItemVersionHistorySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component'; /** * Component representing the admin sidebar @@ -214,6 +215,19 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { } } as OnClickMenuItemModel, }, + { + id: 'edit_item_version_history', + parentID: 'edit', + active: false, + visible: true, + model: { + type: MenuItemType.ONCLICK, + text: 'menu.section.edit_item_version_history', + function: () => { + this.modalService.open(EditItemVersionHistorySelectorComponent); + } + } as OnClickMenuItemModel, + }, /* Import */ { diff --git a/src/app/+item-page/edit-item-page/edit-item-page.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.module.ts index 71924cf6c8..2cbd0c57d1 100644 --- a/src/app/+item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/+item-page/edit-item-page/edit-item-page.module.ts @@ -22,6 +22,7 @@ import { EditRelationshipComponent } from './item-relationships/edit-relationshi import { EditRelationshipListComponent } from './item-relationships/edit-relationship-list/edit-relationship-list.component'; import { ItemMoveComponent } from './item-move/item-move.component'; import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component'; +import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; /** * Module that contains all components related to the Edit Item page administrator functionality @@ -47,6 +48,7 @@ import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.co ItemMetadataComponent, ItemRelationshipsComponent, ItemBitstreamsComponent, + ItemVersionHistoryComponent, EditInPlaceFieldComponent, EditRelationshipComponent, EditRelationshipListComponent, diff --git a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts index 1b386440c0..4e69f01426 100644 --- a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts @@ -13,6 +13,11 @@ import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.compo import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component'; import { ItemMoveComponent } from './item-move/item-move.component'; import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component'; +import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; + +export function getItemEditVersionHistoryPath() { + return ITEM_EDIT_VERSION_HISTORY; +} const ITEM_EDIT_WITHDRAW_PATH = 'withdraw'; const ITEM_EDIT_REINSTATE_PATH = 'reinstate'; @@ -20,6 +25,7 @@ const ITEM_EDIT_PRIVATE_PATH = 'private'; const ITEM_EDIT_PUBLIC_PATH = 'public'; const ITEM_EDIT_DELETE_PATH = 'delete'; const ITEM_EDIT_MOVE_PATH = 'move'; +const ITEM_EDIT_VERSION_HISTORY = 'versionhistory'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -122,7 +128,15 @@ const ITEM_EDIT_MOVE_PATH = 'move'; resolve: { item: ItemPageResolver } - }]) + }, + { + path: ITEM_EDIT_VERSION_HISTORY, + component: ItemVersionHistoryComponent, + resolve: { + item: ItemPageResolver + } + } + ]) ], providers: [ ItemPageResolver, diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html new file mode 100644 index 0000000000..8acd98d640 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html @@ -0,0 +1,6 @@ + diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts new file mode 100644 index 0000000000..d3415dd392 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Item } from '../../../core/shared/item.model'; +import { map } from 'rxjs/operators'; +import { getSucceededRemoteData } from '../../../core/shared/operators'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'ds-item-version-history', + templateUrl: './item-version-history.component.html' +}) +export class ItemVersionHistoryComponent { + /** + * The item to display the version history for + */ + itemRD$: Observable>; + + constructor(private route: ActivatedRoute) { + } + + ngOnInit(): void { + this.itemRD$ = this.route.data.pipe(map((data) => data.item)).pipe(getSucceededRemoteData()) as Observable>; + } +} diff --git a/src/app/+item-page/item-page-routing.module.ts b/src/app/+item-page/item-page-routing.module.ts index ec562842aa..68adcdc95f 100644 --- a/src/app/+item-page/item-page-routing.module.ts +++ b/src/app/+item-page/item-page-routing.module.ts @@ -7,6 +7,7 @@ import { ItemPageResolver } from './item-page.resolver'; import { URLCombiner } from '../core/url-combiner/url-combiner'; import { getItemModulePath } from '../app-routing.module'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; +import { getItemEditVersionHistoryPath } from './edit-item-page/edit-item-page.routing.module'; export function getItemPageRoute(itemId: string) { return new URLCombiner(getItemModulePath(), itemId).toString(); @@ -14,8 +15,12 @@ export function getItemPageRoute(itemId: string) { export function getItemEditPath(id: string) { return new URLCombiner(getItemModulePath(),ITEM_EDIT_PATH.replace(/:id/, id)).toString() } +export function getFullItemEditVersionHistoryPath(id: string) { + return new URLCombiner(getItemModulePath(),ITEM_EDIT_VERSION_HISTORY_PATH.replace(/:id/, id)).toString() +} const ITEM_EDIT_PATH = ':id/edit'; +const ITEM_EDIT_VERSION_HISTORY_PATH = `${ITEM_EDIT_PATH}/${getItemEditVersionHistoryPath()}`; @NgModule({ imports: [ diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index bb90d56ad9..8d5d78ddd1 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -29,7 +29,6 @@ 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: [ @@ -61,7 +60,6 @@ import { ItemVersionsComponent } from './item-versions/item-versions.component'; RelatedEntitiesSearchComponent, TabbedRelatedEntitiesSearchComponent, AbstractIncrementalListComponent, - ItemVersionsComponent ], exports: [ ItemComponent, diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts new file mode 100644 index 0000000000..0c69c57c39 --- /dev/null +++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { getFullItemEditVersionHistoryPath } from '../../../../+item-page/item-page-routing.module'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; + +/** + * Component to wrap a list of existing items inside a modal + * Used to choose an item from to edit its version history + */ +@Component({ + selector: 'ds-edit-item-version-history-selector', + templateUrl: '../dso-selector-modal-wrapper.component.html', +}) +export class EditItemVersionHistorySelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { + objectType = DSpaceObjectType.ITEM; + selectorType = DSpaceObjectType.ITEM; + action = SelectorActionType.EDIT; + + constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router) { + super(activeModal, route); + } + + /** + * Navigate to the item edit version history page + */ + navigate(dso: DSpaceObject) { + this.router.navigate([getFullItemEditVersionHistoryPath(dso.uuid)]); + } +} diff --git a/src/app/+item-page/item-versions/item-versions.component.html b/src/app/shared/item/item-versions/item-versions.component.html similarity index 86% rename from src/app/+item-page/item-versions/item-versions.component.html rename to src/app/shared/item/item-versions/item-versions.component.html index 0fb6bf08cc..debc9ae079 100644 --- a/src/app/+item-page/item-versions/item-versions.component.html +++ b/src/app/shared/item/item-versions/item-versions.component.html @@ -1,8 +1,9 @@
-
+

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

-
* {{"item.version.history.selected" | translate}}
+
diff --git a/src/app/+item-page/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts similarity index 73% rename from src/app/+item-page/item-versions/item-versions.component.ts rename to src/app/shared/item/item-versions/item-versions.component.ts index b493843be1..396e3f4727 100644 --- a/src/app/+item-page/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -1,17 +1,18 @@ 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 { 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 { 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 { PaginatedList } from '../../../core/data/paginated-list'; +import { PaginationComponentOptions } from '../../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'; +import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; +import { PaginatedSearchOptions } from '../../search/paginated-search-options.model'; +import { AlertType } from '../../alert/aletr-type'; @Component({ selector: 'ds-item-versions', @@ -26,6 +27,19 @@ export class ItemVersionsComponent implements OnInit { */ @Input() item: Item; + /** + * An option to display the list of versions, even when there aren't any. + * Instead of the table, an alert will be displayed, notifying the user there are no other versions present + * for the current item. + */ + @Input() displayWhenEmpty = false; + + /** + * The AlertType enumeration + * @type {AlertType} + */ + AlertTypeEnum = AlertType; + /** * The item's version */ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 685787c5a4..835c211572 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,6 +177,8 @@ import { ExternalSourceEntryImportModalComponent } from './form/builder/ds-dynam import { ImportableListItemControlComponent } from './object-collection/shared/importable-list-item-control/importable-list-item-control.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component'; +import { EditItemVersionHistorySelectorComponent } from './dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component'; +import { ItemVersionsComponent } from './item/item-versions/item-versions.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -297,6 +299,7 @@ const COMPONENTS = [ EditCommunitySelectorComponent, EditCollectionSelectorComponent, EditItemSelectorComponent, + EditItemVersionHistorySelectorComponent, CommunitySearchResultListElementComponent, CollectionSearchResultListElementComponent, BrowseByComponent, @@ -339,7 +342,8 @@ const COMPONENTS = [ SelectableListItemControlComponent, ExternalSourceEntryImportModalComponent, ImportableListItemControlComponent, - ExistingMetadataListElementComponent + ExistingMetadataListElementComponent, + ItemVersionsComponent ]; const ENTRY_COMPONENTS = [ @@ -382,6 +386,7 @@ const ENTRY_COMPONENTS = [ EditCommunitySelectorComponent, EditCollectionSelectorComponent, EditItemSelectorComponent, + EditItemVersionHistorySelectorComponent, StartsWithTextComponent, PlainTextMetadataListElementComponent, ItemMetadataListElementComponent, @@ -402,7 +407,8 @@ const ENTRY_COMPONENTS = [ DsDynamicLookupRelationSearchTabComponent, DsDynamicLookupRelationSelectionTabComponent, DsDynamicLookupRelationExternalSourceTabComponent, - ExternalSourceEntryImportModalComponent + ExternalSourceEntryImportModalComponent, + ItemVersionsComponent ]; const SHARED_ITEM_PAGE_COMPONENTS = [ From ade218749213113187d38e48f40902a66bcfe28d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 20 Feb 2020 12:56:32 +0100 Subject: [PATCH 07/13] 68729: Version test cases --- .../item-version-history.component.spec.ts | 44 +++++++ .../item-version-history.component.ts | 3 + .../data/version-history-data.service.spec.ts | 54 ++++++++ ...version-history-selector.component.spec.ts | 66 ++++++++++ .../item-versions.component.html | 26 ++-- .../item-versions.component.spec.ts | 121 ++++++++++++++++++ 6 files changed, 301 insertions(+), 13 deletions(-) create mode 100644 src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts create mode 100644 src/app/core/data/version-history-data.service.spec.ts create mode 100644 src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts create mode 100644 src/app/shared/item/item-versions/item-versions.component.spec.ts diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts new file mode 100644 index 0000000000..2c2e25c803 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts @@ -0,0 +1,44 @@ +import { ItemVersionHistoryComponent } from './item-version-history.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { VarDirective } from '../../../shared/utils/var.directive'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Item } from '../../../core/shared/item.model'; +import { ActivatedRoute } from '@angular/router'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject } from '../../../shared/testing/utils'; + +describe('ItemVersionHistoryComponent', () => { + let component: ItemVersionHistoryComponent; + let fixture: ComponentFixture; + + const item = Object.assign(new Item(), { + uuid: 'item-identifier-1', + handle: '123456789/1', + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ItemVersionHistoryComponent, VarDirective], + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], + providers: [ + { provide: ActivatedRoute, useValue: { data: observableOf({ item: createSuccessfulRemoteDataObject(item) }) } } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemVersionHistoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should initialize the itemRD$ from the route\'s data', (done) => { + component.itemRD$.subscribe((itemRD) => { + expect(itemRD.payload).toBe(item); + done(); + }); + }); +}); diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts index d3415dd392..ee0323d1aa 100644 --- a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts @@ -10,6 +10,9 @@ import { ActivatedRoute } from '@angular/router'; selector: 'ds-item-version-history', templateUrl: './item-version-history.component.html' }) +/** + * Component for listing and managing an item's version history + */ export class ItemVersionHistoryComponent { /** * The item to display the version history for diff --git a/src/app/core/data/version-history-data.service.spec.ts b/src/app/core/data/version-history-data.service.spec.ts new file mode 100644 index 0000000000..6ffefa1c07 --- /dev/null +++ b/src/app/core/data/version-history-data.service.spec.ts @@ -0,0 +1,54 @@ +import { RequestService } from './request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { VersionHistoryDataService } from './version-history-data.service'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub'; +import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub'; +import { getMockRequestService } from '../../shared/mocks/mock-request.service'; +import { GetRequest } from './request.models'; + +const url = 'fake-url'; + +describe('VersionHistoryDataService', () => { + let service: VersionHistoryDataService; + + let requestService: RequestService; + let notificationsService: any; + let rdbService: RemoteDataBuildService; + let objectCache: ObjectCacheService; + let halService: any; + + beforeEach(() => { + createService(); + }); + + describe('getVersions', () => { + let result; + + beforeEach(() => { + result = service.getVersions(1); + }); + + it('should configure a GET request', () => { + expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest)); + }); + }); + + /** + * Create a VersionHistoryDataService used for testing + * @param requestEntry$ Supply a requestEntry to be returned by the REST API (optional) + */ + function createService(requestEntry$?) { + requestService = getMockRequestService(requestEntry$); + rdbService = jasmine.createSpyObj('rdbService', { + buildList: jasmine.createSpy('buildList') + }); + objectCache = jasmine.createSpyObj('objectCache', { + remove: jasmine.createSpy('remove') + }); + halService = new HALEndpointServiceStub(url); + notificationsService = new NotificationsServiceStub(); + + service = new VersionHistoryDataService(requestService, rdbService, null, null, objectCache, halService, notificationsService, null, null); + } +}); diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts new file mode 100644 index 0000000000..eb3106696c --- /dev/null +++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts @@ -0,0 +1,66 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { MetadataValue } from '../../../../core/shared/metadata.models'; +import { RouterStub } from '../../../testing/router-stub'; +import { createSuccessfulRemoteDataObject } from '../../../testing/utils'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { ActivatedRoute, Router } from '@angular/router'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import * as itemRouter from '../../../../+item-page/item-page-routing.module'; +import { EditItemVersionHistorySelectorComponent } from './edit-item-version-history-selector.component'; + +describe('EditItemVersionHistorySelectorComponent', () => { + let component: EditItemVersionHistorySelectorComponent; + let fixture: ComponentFixture; + let debugElement: DebugElement; + + const item = new Item(); + item.uuid = '1234-1234-1234-1234'; + item.metadata = { 'dc.title': [Object.assign(new MetadataValue(), { value: 'Item title', language: undefined })] }; + const router = new RouterStub(); + const itemRD = createSuccessfulRemoteDataObject(item); + const modalStub = jasmine.createSpyObj('modalStub', ['close']); + const editPath = 'testEditVersionHistoryPath'; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [EditItemVersionHistorySelectorComponent], + providers: [ + { provide: NgbActiveModal, useValue: modalStub }, + { + provide: ActivatedRoute, + useValue: { root: { firstChild: { firstChild: { data: observableOf({ item: itemRD }) } } } } + }, + { + provide: Router, useValue: router + } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + + })); + + beforeEach(() => { + spyOnProperty(itemRouter, 'getFullItemEditVersionHistoryPath').and.callFake(() => { + return () => editPath; + }); + + fixture = TestBed.createComponent(EditItemVersionHistorySelectorComponent); + component = fixture.componentInstance; + debugElement = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call navigate on the router with the correct edit path when navigate is called', () => { + component.navigate(item); + expect(router.navigate).toHaveBeenCalledWith([editPath]); + }); + +}); diff --git a/src/app/shared/item/item-versions/item-versions.component.html b/src/app/shared/item/item-versions/item-versions.component.html index debc9ae079..54168773d0 100644 --- a/src/app/shared/item/item-versions/item-versions.component.html +++ b/src/app/shared/item/item-versions/item-versions.component.html @@ -21,21 +21,21 @@ - - {{version?.version}} - - - {{item?.handle}} - * - + + {{version?.version}} + + + {{item?.handle}} + * + - - - {{eperson?.name}} - + + + {{eperson?.name}} + - {{version?.created}} - {{version?.summary}} + {{version?.created}} + {{version?.summary}} diff --git a/src/app/shared/item/item-versions/item-versions.component.spec.ts b/src/app/shared/item/item-versions/item-versions.component.spec.ts new file mode 100644 index 0000000000..4e1cc80215 --- /dev/null +++ b/src/app/shared/item/item-versions/item-versions.component.spec.ts @@ -0,0 +1,121 @@ +import { ItemVersionsComponent } from './item-versions.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { VarDirective } from '../../utils/var.directive'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Item } from '../../../core/shared/item.model'; +import { Version } from '../../../core/shared/version.model'; +import { VersionHistory } from '../../../core/shared/version-history.model'; +import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../testing/utils'; +import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; +import { By } from '@angular/platform-browser'; + +describe('ItemVersionsComponent', () => { + let component: ItemVersionsComponent; + let fixture: ComponentFixture; + + const versionHistory = Object.assign(new VersionHistory(), { + id: 1 + }); + const version1 = Object.assign(new Version(), { + id: 1, + version: 1, + created: new Date(2020, 1, 1), + summary: 'first version', + versionhistory: createSuccessfulRemoteDataObject$(versionHistory) + }); + const version2 = Object.assign(new Version(), { + id: 2, + version: 2, + summary: 'second version', + created: new Date(2020, 1, 2), + versionhistory: createSuccessfulRemoteDataObject$(versionHistory) + }); + const versions = [version1, version2]; + versionHistory.versions = createSuccessfulRemoteDataObject$(createPaginatedList(versions)); + const item1 = Object.assign(new Item(), { + uuid: 'item-identifier-1', + handle: '123456789/1', + version: createSuccessfulRemoteDataObject$(version1) + }); + const item2 = Object.assign(new Item(), { + uuid: 'item-identifier-2', + handle: '123456789/2', + version: createSuccessfulRemoteDataObject$(version2) + }); + const items = [item1, item2]; + version1.item = createSuccessfulRemoteDataObject$(item1); + version2.item = createSuccessfulRemoteDataObject$(item2); + const versionHistoryService = jasmine.createSpyObj('versionHistoryService', { + getVersions: createSuccessfulRemoteDataObject$(createPaginatedList(versions)) + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ItemVersionsComponent, VarDirective], + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], + providers: [ + { provide: VersionHistoryDataService, useValue: versionHistoryService } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemVersionsComponent); + component = fixture.componentInstance; + component.item = item1; + fixture.detectChanges(); + }); + + it(`should display ${versions.length} rows`, () => { + const rows = fixture.debugElement.queryAll(By.css('tbody tr')); + expect(rows.length).toBe(versions.length); + }); + + versions.forEach((version: Version, index: number) => { + const versionItem = items[index]; + + it(`should display version ${version.version} in the correct column for version ${version.id}`, () => { + const id = fixture.debugElement.query(By.css(`#version-row-${version.id} .version-row-element-version`)); + expect(id.nativeElement.textContent).toEqual('' + version.version); + }); + + it(`should display item handle ${versionItem.handle} in the correct column for version ${version.id}`, () => { + const item = fixture.debugElement.query(By.css(`#version-row-${version.id} .version-row-element-item`)); + expect(item.nativeElement.textContent).toContain(versionItem.handle); + }); + + // This version's item is equal to the component's item (the selected item) + // Check if the handle contains an asterisk + if (item1.uuid === versionItem.uuid) { + it('should add an asterisk to the handle of the selected item', () => { + const item = fixture.debugElement.query(By.css(`#version-row-${version.id} .version-row-element-item`)); + expect(item.nativeElement.textContent).toContain('*'); + }); + } + + it(`should display date ${version.created} in the correct column for version ${version.id}`, () => { + const date = fixture.debugElement.query(By.css(`#version-row-${version.id} .version-row-element-date`)); + expect(date.nativeElement.textContent).toEqual('' + version.created); + }); + + it(`should display summary ${version.summary} in the correct column for version ${version.id}`, () => { + const summary = fixture.debugElement.query(By.css(`#version-row-${version.id} .version-row-element-summary`)); + expect(summary.nativeElement.textContent).toEqual(version.summary); + }); + }); + + describe('switchPage', () => { + const page = 5; + + beforeEach(() => { + component.switchPage(page); + }); + + it('should set the option\'s currentPage to the new page', () => { + expect(component.options.currentPage).toEqual(page); + }); + }); +}); From 3f11ae9fa59e0044aef650135bb49416ced7e50d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 25 Feb 2020 14:35:25 +0100 Subject: [PATCH 08/13] 68729: Version(History) resource types --- src/app/core/shared/version-history.resource-type.ts | 9 +++++++++ src/app/core/shared/version.resource-type.ts | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/app/core/shared/version-history.resource-type.ts create mode 100644 src/app/core/shared/version.resource-type.ts diff --git a/src/app/core/shared/version-history.resource-type.ts b/src/app/core/shared/version-history.resource-type.ts new file mode 100644 index 0000000000..c6d92ce138 --- /dev/null +++ b/src/app/core/shared/version-history.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from './resource-type'; + +/** + * The resource type for VersionHistory + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const VERSION_HISTORY = new ResourceType('versionhistory'); diff --git a/src/app/core/shared/version.resource-type.ts b/src/app/core/shared/version.resource-type.ts new file mode 100644 index 0000000000..ac0f56239e --- /dev/null +++ b/src/app/core/shared/version.resource-type.ts @@ -0,0 +1,9 @@ +import { ResourceType } from './resource-type'; + +/** + * The resource type for Version + * + * Needs to be in a separate file to prevent circular + * dependencies in webpack. + */ +export const VERSION = new ResourceType('version'); From 56433d07768b97b35205aea80def0275ad5c3bc7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 25 Feb 2020 14:55:26 +0100 Subject: [PATCH 09/13] 68729: E-Person link on version list + test fixes --- src/app/core/data/version-history-data.service.spec.ts | 4 ++-- .../org-unit-item-metadata-list-element.component.spec.ts | 4 ++-- .../item/item-versions/item-versions.component.spec.ts | 6 +++--- .../shared/item/item-versions/item-versions.component.ts | 3 +-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/app/core/data/version-history-data.service.spec.ts b/src/app/core/data/version-history-data.service.spec.ts index 6ffefa1c07..6728df71f1 100644 --- a/src/app/core/data/version-history-data.service.spec.ts +++ b/src/app/core/data/version-history-data.service.spec.ts @@ -26,7 +26,7 @@ describe('VersionHistoryDataService', () => { let result; beforeEach(() => { - result = service.getVersions(1); + result = service.getVersions('1'); }); it('should configure a GET request', () => { @@ -49,6 +49,6 @@ describe('VersionHistoryDataService', () => { halService = new HALEndpointServiceStub(url); notificationsService = new NotificationsServiceStub(); - service = new VersionHistoryDataService(requestService, rdbService, null, null, objectCache, halService, notificationsService, null, null); + service = new VersionHistoryDataService(requestService, rdbService, null, objectCache, halService, notificationsService, null, null); } }); diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts index 37cbe47c72..4f899edaa8 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts @@ -27,12 +27,12 @@ describe('OrgUnitItemMetadataListElementComponent', () => { }).compileComponents(); })); - beforeEach(async(() => { + beforeEach(() => { fixture = TestBed.createComponent(OrgUnitItemMetadataListElementComponent); comp = fixture.componentInstance; comp.metadataRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); - })); + }); it('should show the name of the organisation as a link', () => { const linkText = fixture.debugElement.query(By.css('a')).nativeElement.textContent; diff --git a/src/app/shared/item/item-versions/item-versions.component.spec.ts b/src/app/shared/item/item-versions/item-versions.component.spec.ts index 4e1cc80215..18fa4cf983 100644 --- a/src/app/shared/item/item-versions/item-versions.component.spec.ts +++ b/src/app/shared/item/item-versions/item-versions.component.spec.ts @@ -16,17 +16,17 @@ describe('ItemVersionsComponent', () => { let fixture: ComponentFixture; const versionHistory = Object.assign(new VersionHistory(), { - id: 1 + id: '1' }); const version1 = Object.assign(new Version(), { - id: 1, + id: '1', version: 1, created: new Date(2020, 1, 1), summary: 'first version', versionhistory: createSuccessfulRemoteDataObject$(versionHistory) }); const version2 = Object.assign(new Version(), { - id: 2, + id: '2', version: 2, summary: 'second version', created: new Date(2020, 1, 2), diff --git a/src/app/shared/item/item-versions/item-versions.component.ts b/src/app/shared/item/item-versions/item-versions.component.ts index 6623c32e4b..b45857af2f 100644 --- a/src/app/shared/item/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -13,7 +13,6 @@ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { VersionHistoryDataService } from '../../../core/data/version-history-data.service'; import { PaginatedSearchOptions } from '../../search/paginated-search-options.model'; import { AlertType } from '../../alert/aletr-type'; -import { hasValueOperator } from '../../empty.util'; import { followLink } from '../../utils/follow-link-config.model'; @Component({ @@ -104,7 +103,7 @@ export class ItemVersionsComponent implements OnInit { switchMap(([versionHistory, page]: [VersionHistory, number]) => this.versionHistoryService.getVersions(versionHistory.id, new PaginatedSearchOptions({pagination: Object.assign({}, this.options, { currentPage: page })}), - followLink('item'))) + followLink('item'), followLink('eperson'))) ); this.hasEpersons$ = this.versionsRD$.pipe( getAllSucceededRemoteData(), From cbd36ce1f9bbfc22882ea5adaa7a6f02e89bcb73 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 25 Feb 2020 15:54:01 +0100 Subject: [PATCH 10/13] 68729: Resolve missing links to undefined --- .../core/cache/builders/link.service.spec.ts | 47 +++++++++++++++++++ src/app/core/cache/builders/link.service.ts | 19 ++++---- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/app/core/cache/builders/link.service.spec.ts b/src/app/core/cache/builders/link.service.spec.ts index b34aea320a..dbd65eefb5 100644 --- a/src/app/core/cache/builders/link.service.spec.ts +++ b/src/app/core/cache/builders/link.service.spec.ts @@ -218,5 +218,52 @@ describe('LinkService', () => { }); }); + describe('when a link is missing', () => { + beforeEach(() => { + testModel = Object.assign(new TestModel(), { + value: 'a test value', + _links: { + self: { + href: 'http://self.link' + }, + predecessor: { + href: 'http://predecessor.link' + } + } + }); + spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService); + }); + + describe('resolving the available link', () => { + beforeEach(() => { + spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ + resourceType: TEST_MODEL, + linkName: 'predecessor', + propertyName: 'predecessor' + }); + result = service.resolveLinks(testModel, followLink('predecessor')); + }); + + it('should return the model with the resolved link', () => { + expect(result.predecessor).toBe('findByHref'); + }); + }); + + describe('resolving the missing link', () => { + beforeEach(() => { + spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ + resourceType: TEST_MODEL, + linkName: 'successor', + propertyName: 'successor' + }); + result = service.resolveLinks(testModel, followLink('successor')); + }); + + it('should return the model with no resolved link', () => { + expect(result.successor).toBeUndefined(); + }); + }); + }); + }); /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/cache/builders/link.service.ts b/src/app/core/cache/builders/link.service.ts index c41a5484a1..dc65eab68f 100644 --- a/src/app/core/cache/builders/link.service.ts +++ b/src/app/core/cache/builders/link.service.ts @@ -55,16 +55,19 @@ export class LinkService { parent: this.parentInjector }).get(provider); - const href = model._links[matchingLinkDef.linkName].href; + const link = model._links[matchingLinkDef.linkName]; + if (hasValue(link)) { + const href = link.href; - try { - if (matchingLinkDef.isList) { - model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow); - } else { - model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow); + try { + if (matchingLinkDef.isList) { + model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow); + } else { + model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow); + } + } catch (e) { + throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`); } - } catch (e) { - throw new Error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} from ${href}`); } } return model; From 6b8e134e4541ed7fbd95ae62c202fb84f8b2ec81 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 12 Mar 2020 15:54:09 +0100 Subject: [PATCH 11/13] 68729: followLink embed fix --- src/app/+item-page/item-page.resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/item-page.resolver.ts b/src/app/+item-page/item-page.resolver.ts index 7a2dafe156..501bb34d2c 100644 --- a/src/app/+item-page/item-page.resolver.ts +++ b/src/app/+item-page/item-page.resolver.ts @@ -28,7 +28,7 @@ export class ItemPageResolver implements Resolve> { followLink('owningCollection'), followLink('bundles'), followLink('relationships'), - followLink('version', undefined, followLink('versionhistory')), + followLink('version', undefined, true, followLink('versionhistory')), ).pipe( find((RD) => hasValue(RD.error) || RD.hasSucceeded), ); From 30c7e563f56c647a23c3be34b23324c1ad7a500b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 17 Mar 2020 16:01:00 +0100 Subject: [PATCH 12/13] 68729: Edit version history page as tab, removed sidebar link, added under-construction notice --- resources/i18n/en.json5 | 8 ++- .../admin-sidebar/admin-sidebar.component.ts | 14 ---- .../edit-item-page.routing.module.ts | 15 ++--- .../item-version-history.component.html | 10 +-- .../item-version-history.component.ts | 9 ++- .../+item-page/item-page-routing.module.ts | 5 -- ...version-history-selector.component.spec.ts | 66 ------------------- ...item-version-history-selector.component.ts | 32 --------- .../item-versions.component.html | 2 +- .../item-versions/item-versions.component.ts | 5 ++ src/app/shared/shared.module.ts | 3 - 11 files changed, 30 insertions(+), 139 deletions(-) delete mode 100644 src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts delete mode 100644 src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 554d079214..33b375de7e 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -913,6 +913,12 @@ "item.edit.tabs.status.title": "Item Edit - Status", + "item.edit.tabs.versionhistory.head": "Version History", + + "item.edit.tabs.versionhistory.title": "Item Edit - Version History", + + "item.edit.tabs.versionhistory.under-construction": "Editing or adding new versions is not yet possible in this user interface.", + "item.edit.tabs.view.head": "View Item", "item.edit.tabs.view.title": "Item Edit - View", @@ -1172,8 +1178,6 @@ "menu.section.edit_item": "Item", - "menu.section.edit_item_version_history": "Item Version History", - "menu.section.export": "Export", diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts index e50d976dc9..72eb306bf1 100644 --- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts @@ -18,7 +18,6 @@ import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrapp import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; import {CreateItemParentSelectorComponent} from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; -import { EditItemVersionHistorySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component'; /** * Component representing the admin sidebar @@ -215,19 +214,6 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { } } as OnClickMenuItemModel, }, - { - id: 'edit_item_version_history', - parentID: 'edit', - active: false, - visible: true, - model: { - type: MenuItemType.ONCLICK, - text: 'menu.section.edit_item_version_history', - function: () => { - this.modalService.open(EditItemVersionHistorySelectorComponent); - } - } as OnClickMenuItemModel, - }, /* Import */ { diff --git a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts index 05d1d8f847..2718c66246 100644 --- a/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/+item-page/edit-item-page/edit-item-page.routing.module.ts @@ -1,4 +1,3 @@ -import { ItemPageResolver } from '../item-page.resolver'; import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { EditItemPageComponent } from './edit-item-page.component'; @@ -16,17 +15,12 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component'; -export function getItemEditVersionHistoryPath() { - return ITEM_EDIT_VERSION_HISTORY; -} - const ITEM_EDIT_WITHDRAW_PATH = 'withdraw'; const ITEM_EDIT_REINSTATE_PATH = 'reinstate'; const ITEM_EDIT_PRIVATE_PATH = 'private'; const ITEM_EDIT_PUBLIC_PATH = 'public'; const ITEM_EDIT_DELETE_PATH = 'delete'; const ITEM_EDIT_MOVE_PATH = 'move'; -const ITEM_EDIT_VERSION_HISTORY = 'versionhistory'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -81,6 +75,11 @@ const ITEM_EDIT_VERSION_HISTORY = 'versionhistory'; /* TODO - change when curate page exists */ component: ItemBitstreamsComponent, data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true } + }, + { + path: 'versionhistory', + component: ItemVersionHistoryComponent, + data: { title: 'item.edit.tabs.versionhistory.title', showBreadcrumbs: true } } ] }, @@ -112,10 +111,6 @@ const ITEM_EDIT_VERSION_HISTORY = 'versionhistory'; path: ITEM_EDIT_MOVE_PATH, component: ItemMoveComponent, data: { title: 'item.edit.move.title' }, - }, - { - path: ITEM_EDIT_VERSION_HISTORY, - component: ItemVersionHistoryComponent, } ] } diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html index 8acd98d640..acabbd1010 100644 --- a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.html @@ -1,6 +1,6 @@ -
- - - {{"item.version.history.return" | translate}} - +
+ +
+
+
diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts index ee0323d1aa..ce662c5753 100644 --- a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.ts @@ -5,6 +5,7 @@ import { Item } from '../../../core/shared/item.model'; import { map } from 'rxjs/operators'; import { getSucceededRemoteData } from '../../../core/shared/operators'; import { ActivatedRoute } from '@angular/router'; +import { AlertType } from '../../../shared/alert/aletr-type'; @Component({ selector: 'ds-item-version-history', @@ -19,10 +20,16 @@ export class ItemVersionHistoryComponent { */ itemRD$: Observable>; + /** + * The AlertType enumeration + * @type {AlertType} + */ + AlertTypeEnum = AlertType; + constructor(private route: ActivatedRoute) { } ngOnInit(): void { - this.itemRD$ = this.route.data.pipe(map((data) => data.item)).pipe(getSucceededRemoteData()) as Observable>; + this.itemRD$ = this.route.parent.data.pipe(map((data) => data.item)).pipe(getSucceededRemoteData()) as Observable>; } } diff --git a/src/app/+item-page/item-page-routing.module.ts b/src/app/+item-page/item-page-routing.module.ts index 09bd7f8b62..686c5ff2fc 100644 --- a/src/app/+item-page/item-page-routing.module.ts +++ b/src/app/+item-page/item-page-routing.module.ts @@ -10,7 +10,6 @@ import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver'; import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service'; import { LinkService } from '../core/cache/builders/link.service'; -import { getItemEditVersionHistoryPath } from './edit-item-page/edit-item-page.routing.module'; export function getItemPageRoute(itemId: string) { return new URLCombiner(getItemModulePath(), itemId).toString(); @@ -19,12 +18,8 @@ export function getItemPageRoute(itemId: string) { export function getItemEditPath(id: string) { return new URLCombiner(getItemModulePath(), id, ITEM_EDIT_PATH).toString() } -export function getFullItemEditVersionHistoryPath(id: string) { - return new URLCombiner(getItemModulePath(), id, ITEM_EDIT_VERSION_HISTORY_PATH).toString() -} const ITEM_EDIT_PATH = 'edit'; -const ITEM_EDIT_VERSION_HISTORY_PATH = `${ITEM_EDIT_PATH}/${getItemEditVersionHistoryPath()}`; @NgModule({ imports: [ diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts deleted file mode 100644 index eb3106696c..0000000000 --- a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { Item } from '../../../../core/shared/item.model'; -import { MetadataValue } from '../../../../core/shared/metadata.models'; -import { RouterStub } from '../../../testing/router-stub'; -import { createSuccessfulRemoteDataObject } from '../../../testing/utils'; -import { TranslateModule } from '@ngx-translate/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { ActivatedRoute, Router } from '@angular/router'; -import { of as observableOf } from 'rxjs/internal/observable/of'; -import * as itemRouter from '../../../../+item-page/item-page-routing.module'; -import { EditItemVersionHistorySelectorComponent } from './edit-item-version-history-selector.component'; - -describe('EditItemVersionHistorySelectorComponent', () => { - let component: EditItemVersionHistorySelectorComponent; - let fixture: ComponentFixture; - let debugElement: DebugElement; - - const item = new Item(); - item.uuid = '1234-1234-1234-1234'; - item.metadata = { 'dc.title': [Object.assign(new MetadataValue(), { value: 'Item title', language: undefined })] }; - const router = new RouterStub(); - const itemRD = createSuccessfulRemoteDataObject(item); - const modalStub = jasmine.createSpyObj('modalStub', ['close']); - const editPath = 'testEditVersionHistoryPath'; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], - declarations: [EditItemVersionHistorySelectorComponent], - providers: [ - { provide: NgbActiveModal, useValue: modalStub }, - { - provide: ActivatedRoute, - useValue: { root: { firstChild: { firstChild: { data: observableOf({ item: itemRD }) } } } } - }, - { - provide: Router, useValue: router - } - ], - schemas: [NO_ERRORS_SCHEMA] - }).compileComponents(); - - })); - - beforeEach(() => { - spyOnProperty(itemRouter, 'getFullItemEditVersionHistoryPath').and.callFake(() => { - return () => editPath; - }); - - fixture = TestBed.createComponent(EditItemVersionHistorySelectorComponent); - component = fixture.componentInstance; - debugElement = fixture.debugElement; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should call navigate on the router with the correct edit path when navigate is called', () => { - component.navigate(item); - expect(router.navigate).toHaveBeenCalledWith([editPath]); - }); - -}); diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts deleted file mode 100644 index 0c69c57c39..0000000000 --- a/src/app/shared/dso-selector/modal-wrappers/edit-item-version-history-selector/edit-item-version-history-selector.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; -import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { getFullItemEditVersionHistoryPath } from '../../../../+item-page/item-page-routing.module'; -import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; - -/** - * Component to wrap a list of existing items inside a modal - * Used to choose an item from to edit its version history - */ -@Component({ - selector: 'ds-edit-item-version-history-selector', - templateUrl: '../dso-selector-modal-wrapper.component.html', -}) -export class EditItemVersionHistorySelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { - objectType = DSpaceObjectType.ITEM; - selectorType = DSpaceObjectType.ITEM; - action = SelectorActionType.EDIT; - - constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router) { - super(activeModal, route); - } - - /** - * Navigate to the item edit version history page - */ - navigate(dso: DSpaceObject) { - this.router.navigate([getFullItemEditVersionHistoryPath(dso.uuid)]); - } -} diff --git a/src/app/shared/item/item-versions/item-versions.component.html b/src/app/shared/item/item-versions/item-versions.component.html index 54168773d0..6e93f4c7ca 100644 --- a/src/app/shared/item/item-versions/item-versions.component.html +++ b/src/app/shared/item/item-versions/item-versions.component.html @@ -1,7 +1,7 @@
-

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

+

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

Date: Tue, 17 Mar 2020 16:06:07 +0100 Subject: [PATCH 13/13] 68729: Test fix --- .../item-version-history/item-version-history.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts index 2c2e25c803..9bc39649f4 100644 --- a/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts @@ -23,7 +23,7 @@ describe('ItemVersionHistoryComponent', () => { declarations: [ItemVersionHistoryComponent, VarDirective], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], providers: [ - { provide: ActivatedRoute, useValue: { data: observableOf({ item: createSuccessfulRemoteDataObject(item) }) } } + { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ item: createSuccessfulRemoteDataObject(item) }) } } } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents();