diff --git a/src/app/item-page/item-page-routing-paths.ts b/src/app/item-page/item-page-routing-paths.ts index 43b37f954a..3e0a356efb 100644 --- a/src/app/item-page/item-page-routing-paths.ts +++ b/src/app/item-page/item-page-routing-paths.ts @@ -34,5 +34,14 @@ export function getEntityEditRoute(entityType: string, itemId: string) { return new URLCombiner(getEntityPageRoute(entityType, itemId), ITEM_EDIT_PATH).toString(); } +/** + * Get the route to an item's version + * @param versionId the ID of the version for which the route will be retrieved + */ +export function getItemVersionRoute(versionId: string) { + return new URLCombiner(getItemModuleRoute(), ITEM_VERSION_PATH, versionId).toString(); +} + export const ITEM_EDIT_PATH = 'edit'; +export const ITEM_VERSION_PATH = 'version'; export const UPLOAD_BITSTREAM_PATH = 'bitstreams/new'; diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index f2d0a23935..52de34e8ed 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule } from '@angular/router'; import { ItemPageResolver } from './item-page.resolver'; import { AuthenticatedGuard } from '../core/auth/authenticated.guard'; import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver'; +import { VersionResolver } from './version-page/version.resolver'; import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service'; import { LinkService } from '../core/cache/builders/link.service'; import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component'; @@ -12,6 +13,7 @@ import { MenuItemType } from '../shared/menu/initial-menus-state'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component'; +import { VersionPageComponent } from './version-page/version-page/version-page.component'; @NgModule({ imports: [ @@ -58,6 +60,18 @@ import { ThemedFullItemPageComponent } from './full/themed-full-item-page.compon }], }, }, + }, + { + path: 'version', + children: [ + { + path: ':id', + component: VersionPageComponent, + resolve: { + dso: VersionResolver, + }, + } + ], } ]) ], @@ -67,6 +81,7 @@ import { ThemedFullItemPageComponent } from './full/themed-full-item-page.compon DSOBreadcrumbsService, LinkService, ItemPageAdministratorGuard, + VersionResolver, ] }) diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index 1c6cd83e9c..6556617e4a 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -31,6 +31,7 @@ import { MediaViewerComponent } from './media-viewer/media-viewer.component'; import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component'; import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component'; import { NgxGalleryModule } from '@kolkov/ngx-gallery'; +import { VersionPageComponent } from './version-page/version-page/version-page.component'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -60,7 +61,8 @@ const DECLARATIONS = [ AbstractIncrementalListComponent, MediaViewerComponent, MediaViewerVideoComponent, - MediaViewerImageComponent + MediaViewerImageComponent, + VersionPageComponent, ]; @NgModule({ @@ -72,7 +74,7 @@ const DECLARATIONS = [ StatisticsModule.forRoot(), JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(), - NgxGalleryModule, + NgxGalleryModule, ], declarations: [ ...DECLARATIONS diff --git a/src/app/item-page/version-page/version-page/version-page.component.html b/src/app/item-page/version-page/version-page/version-page.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/item-page/version-page/version-page/version-page.component.scss b/src/app/item-page/version-page/version-page/version-page.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/item-page/version-page/version-page/version-page.component.spec.ts b/src/app/item-page/version-page/version-page/version-page.component.spec.ts new file mode 100644 index 0000000000..7e3061f397 --- /dev/null +++ b/src/app/item-page/version-page/version-page/version-page.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { VersionPageComponent } from './version-page.component'; + +describe('VersionPageComponent', () => { + let component: VersionPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ VersionPageComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(VersionPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/item-page/version-page/version-page/version-page.component.ts b/src/app/item-page/version-page/version-page/version-page.component.ts new file mode 100644 index 0000000000..d58c98a967 --- /dev/null +++ b/src/app/item-page/version-page/version-page/version-page.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../../../core/data/remote-data'; +import { ActivatedRoute, Router } from '@angular/router'; +import { AuthService } from '../../../core/auth/auth.service'; +import { map, switchMap } from 'rxjs/operators'; +import { + getFirstCompletedRemoteData, + getFirstSucceededRemoteDataPayload, + redirectOn4xx +} from '../../../core/shared/operators'; +import { VersionDataService } from '../../../core/data/version-data.service'; +import { Version } from '../../../core/shared/version.model'; +import { Item } from '../../../core/shared/item.model'; +import { getItemPageRoute } from '../../item-page-routing-paths'; +import { getPageNotFoundRoute } from '../../../app-routing-paths'; + +@Component({ + selector: 'ds-version-page', + templateUrl: './version-page.component.html', + styleUrls: ['./version-page.component.scss'] +}) +export class VersionPageComponent implements OnInit { + + versionRD$: Observable>; + itemRD$: Observable>; + + constructor( + protected route: ActivatedRoute, + private router: Router, + private versionService: VersionDataService, + private authService: AuthService, + ) { + } + + ngOnInit(): void { + /* Retrieve version from resolver or redirect on 4xx */ + this.versionRD$ = this.route.data.pipe( + map((data) => data.dso as RemoteData), + redirectOn4xx(this.router, this.authService), + ); + + /* Retrieve item from version and reroute to item's page or handle missing item */ + this.versionRD$.pipe( + getFirstSucceededRemoteDataPayload(), + switchMap((version) => version.item), + redirectOn4xx(this.router, this.authService), + getFirstCompletedRemoteData(), + ).subscribe((itemRD) => { + console.log(JSON.stringify(itemRD)); + if (itemRD.hasNoContent) { + this.router.navigateByUrl(getPageNotFoundRoute(), { skipLocationChange: true }); + } else { + const itemUrl = getItemPageRoute(itemRD.payload); + this.router.navigateByUrl(itemUrl); + } + }); + + } + +} diff --git a/src/app/item-page/version-page/version.resolver.ts b/src/app/item-page/version-page/version.resolver.ts new file mode 100644 index 0000000000..8341052468 --- /dev/null +++ b/src/app/item-page/version-page/version.resolver.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../../core/data/remote-data'; +import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { Store } from '@ngrx/store'; +import { ResolvedAction } from '../../core/resolving/resolver.actions'; +import { Version } from '../../core/shared/version.model'; +import { VersionDataService } from '../../core/data/version-data.service'; + +/** + * The self links defined in this list are expected to be requested somewhere in the near future + * Requesting them as embeds will limit the number of requests + */ +export const VERSION_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig[] = [ + followLink('item'), +]; + +/** + * This class represents a resolver that requests a specific version before the route is activated + */ +@Injectable() +export class VersionResolver implements Resolve> { + constructor( + protected versionService: VersionDataService, + protected store: Store, + protected router: Router + ) { + } + + /** + * Method for resolving a version based on the parameters in the current route + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns Observable<> Emits the found item based on the parameters in the current route, + * or an error if something went wrong + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { + const versionRD$ = this.versionService.findById(route.params.id, + true, + false, + ...VERSION_PAGE_LINKS_TO_FOLLOW + ).pipe( + getFirstCompletedRemoteData(), + ); + + versionRD$.subscribe((versionRD: RemoteData) => { + this.store.dispatch(new ResolvedAction(state.url, versionRD.payload)); + }); + + return versionRD$; + } +} 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 4e488a3e23..4e05e035c9 100644 --- a/src/app/shared/item/item-versions/item-versions.component.html +++ b/src/app/shared/item/item-versions/item-versions.component.html @@ -24,13 +24,18 @@ - {{version?.version}} + + {{version.version}} + * + {{eperson?.name}} - {{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}} + + {{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}} + {{version?.summary}} 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 d68c201359..2e26695d82 100644 --- a/src/app/shared/item/item-versions/item-versions.component.ts +++ b/src/app/shared/item/item-versions/item-versions.component.ts @@ -21,7 +21,7 @@ import { AlertType } from '../../alert/aletr-type'; import { followLink } from '../../utils/follow-link-config.model'; import { hasValue, hasValueOperator } from '../../empty.util'; import { PaginationService } from '../../../core/pagination/pagination.service'; -import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; +import { getItemPageRoute, getItemVersionRoute } from '../../../item-page/item-page-routing-paths'; import { FormBuilder } from '@angular/forms'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component'; @@ -192,6 +192,14 @@ export class ItemVersionsComponent implements OnInit { this.versionBeingEditedId = undefined; } + /** + * Get the route to the specified version + * @param versionId the ID of the version for which the route will be retrieved + */ + getVersionRoute(versionId: string) { + return getItemVersionRoute(versionId); + } + /** * Applies changes to version currently being edited */ @@ -250,6 +258,7 @@ export class ItemVersionsComponent implements OnInit { } ); }); + // TODO non usare subscribe annidate /*version.item.pipe( getFirstSucceededRemoteDataPayload(), switchMap((getItemRes) => this.itemService.delete(getItemRes.id))