mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[CST-4499] Version history (WIP) - Version page added (redirecting to item's page)
This commit is contained in:
@@ -34,5 +34,14 @@ export function getEntityEditRoute(entityType: string, itemId: string) {
|
|||||||
return new URLCombiner(getEntityPageRoute(entityType, itemId), ITEM_EDIT_PATH).toString();
|
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_EDIT_PATH = 'edit';
|
||||||
|
export const ITEM_VERSION_PATH = 'version';
|
||||||
export const UPLOAD_BITSTREAM_PATH = 'bitstreams/new';
|
export const UPLOAD_BITSTREAM_PATH = 'bitstreams/new';
|
||||||
|
@@ -3,6 +3,7 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { ItemPageResolver } from './item-page.resolver';
|
import { ItemPageResolver } from './item-page.resolver';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver';
|
import { ItemBreadcrumbResolver } from '../core/breadcrumbs/item-breadcrumb.resolver';
|
||||||
|
import { VersionResolver } from './version-page/version.resolver';
|
||||||
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
import { DSOBreadcrumbsService } from '../core/breadcrumbs/dso-breadcrumbs.service';
|
||||||
import { LinkService } from '../core/cache/builders/link.service';
|
import { LinkService } from '../core/cache/builders/link.service';
|
||||||
import { UploadBitstreamComponent } from './bitstreams/upload/upload-bitstream.component';
|
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 { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model';
|
||||||
import { ThemedItemPageComponent } from './simple/themed-item-page.component';
|
import { ThemedItemPageComponent } from './simple/themed-item-page.component';
|
||||||
import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component';
|
import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component';
|
||||||
|
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
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,
|
DSOBreadcrumbsService,
|
||||||
LinkService,
|
LinkService,
|
||||||
ItemPageAdministratorGuard,
|
ItemPageAdministratorGuard,
|
||||||
|
VersionResolver,
|
||||||
]
|
]
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@@ -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 { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
|
||||||
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
|
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
|
||||||
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
|
||||||
|
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
// put only entry components that use custom decorator
|
// put only entry components that use custom decorator
|
||||||
@@ -60,7 +61,8 @@ const DECLARATIONS = [
|
|||||||
AbstractIncrementalListComponent,
|
AbstractIncrementalListComponent,
|
||||||
MediaViewerComponent,
|
MediaViewerComponent,
|
||||||
MediaViewerVideoComponent,
|
MediaViewerVideoComponent,
|
||||||
MediaViewerImageComponent
|
MediaViewerImageComponent,
|
||||||
|
VersionPageComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -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<VersionPageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ VersionPageComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(VersionPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -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<RemoteData<Version>>;
|
||||||
|
itemRD$: Observable<RemoteData<Item>>;
|
||||||
|
|
||||||
|
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<Version>),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/app/item-page/version-page/version.resolver.ts
Normal file
54
src/app/item-page/version-page/version.resolver.ts
Normal file
@@ -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<Version>[] = [
|
||||||
|
followLink('item'),
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a resolver that requests a specific version before the route is activated
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class VersionResolver implements Resolve<RemoteData<Version>> {
|
||||||
|
constructor(
|
||||||
|
protected versionService: VersionDataService,
|
||||||
|
protected store: Store<any>,
|
||||||
|
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<<RemoteData<Item>> 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<RemoteData<Version>> {
|
||||||
|
const versionRD$ = this.versionService.findById(route.params.id,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
...VERSION_PAGE_LINKS_TO_FOLLOW
|
||||||
|
).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
);
|
||||||
|
|
||||||
|
versionRD$.subscribe((versionRD: RemoteData<Version>) => {
|
||||||
|
this.store.dispatch(new ResolvedAction(state.url, versionRD.payload));
|
||||||
|
});
|
||||||
|
|
||||||
|
return versionRD$;
|
||||||
|
}
|
||||||
|
}
|
@@ -24,13 +24,18 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
|
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
|
||||||
<td class="version-row-element-version">{{version?.version}}</td>
|
<td class="version-row-element-version">
|
||||||
|
<a [routerLink]="getVersionRoute(version.id)">{{version.version}}</a>
|
||||||
|
<span *ngIf="version?.id === itemVersion?.id">*</span>
|
||||||
|
</td>
|
||||||
<td *ngIf="(hasEpersons$ | async)" class="version-row-element-editor">
|
<td *ngIf="(hasEpersons$ | async)" class="version-row-element-editor">
|
||||||
<span *ngVar="(version?.eperson | async)?.payload as eperson">
|
<span *ngVar="(version?.eperson | async)?.payload as eperson">
|
||||||
<a *ngIf="eperson" [href]="'mailto:' + eperson?.email">{{eperson?.name}}</a>
|
<a *ngIf="eperson" [href]="'mailto:' + eperson?.email">{{eperson?.name}}</a>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="version-row-element-date">{{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}}</td>
|
<td class="version-row-element-date">
|
||||||
|
{{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}}
|
||||||
|
</td>
|
||||||
<td class="version-row-element-summary">
|
<td class="version-row-element-summary">
|
||||||
<ng-container *ngIf="isThisBeingEdited(version); then editSummary else showSummary"></ng-container>
|
<ng-container *ngIf="isThisBeingEdited(version); then editSummary else showSummary"></ng-container>
|
||||||
<ng-template #showSummary>{{version?.summary}}</ng-template>
|
<ng-template #showSummary>{{version?.summary}}</ng-template>
|
||||||
|
@@ -21,7 +21,7 @@ import { AlertType } from '../../alert/aletr-type';
|
|||||||
import { followLink } from '../../utils/follow-link-config.model';
|
import { followLink } from '../../utils/follow-link-config.model';
|
||||||
import { hasValue, hasValueOperator } from '../../empty.util';
|
import { hasValue, hasValueOperator } from '../../empty.util';
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
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 { FormBuilder } from '@angular/forms';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component';
|
import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component';
|
||||||
@@ -192,6 +192,14 @@ export class ItemVersionsComponent implements OnInit {
|
|||||||
this.versionBeingEditedId = undefined;
|
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
|
* Applies changes to version currently being edited
|
||||||
*/
|
*/
|
||||||
@@ -250,6 +258,7 @@ export class ItemVersionsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
// TODO non usare subscribe annidate
|
||||||
/*version.item.pipe(
|
/*version.item.pipe(
|
||||||
getFirstSucceededRemoteDataPayload(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
switchMap((getItemRes) => this.itemService.delete(getItemRes.id))
|
switchMap((getItemRes) => this.itemService.delete(getItemRes.id))
|
||||||
|
Reference in New Issue
Block a user