mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Request-a-copy improv: Secure item view comps
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="item-page">
|
||||||
|
<ds-item-access-by-token-view [object]="(itemRD$ | async)?.payload"
|
||||||
|
[itemRequest$]="itemRequest$"
|
||||||
|
[viewMode]="viewMode">
|
||||||
|
</ds-item-access-by-token-view>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,299 @@
|
|||||||
|
import { KeyValuePipe } from '@angular/common';
|
||||||
|
import { PLATFORM_ID } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
fakeAsync,
|
||||||
|
TestBed,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
of as observableOf,
|
||||||
|
} from 'rxjs';
|
||||||
|
|
||||||
|
import { getForbiddenRoute } from '../../app-routing-paths';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
|
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||||
|
import { ItemAccessByTokenPageComponent } from './item-access-by-token-page.component';
|
||||||
|
import { ItemAccessByTokenViewComponent } from './item-access-by-token-view.component';
|
||||||
|
|
||||||
|
describe('ItemAccessByTokenPageComponent', () => {
|
||||||
|
let component: ItemAccessByTokenPageComponent;
|
||||||
|
let fixture: ComponentFixture<ItemAccessByTokenPageComponent>;
|
||||||
|
let itemRequestService: jasmine.SpyObj<ItemRequestDataService>;
|
||||||
|
let router: jasmine.SpyObj<Router>;
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(false),
|
||||||
|
});
|
||||||
|
let signpostingDataService: SignpostingDataService;
|
||||||
|
|
||||||
|
const mocklink = {
|
||||||
|
href: 'http://test.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocklink2 = {
|
||||||
|
href: 'http://test2.org',
|
||||||
|
rel: 'test',
|
||||||
|
type: 'test',
|
||||||
|
};
|
||||||
|
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
|
||||||
|
getLinks: observableOf([mocklink, mocklink2]),
|
||||||
|
});
|
||||||
|
const linkHeadService = jasmine.createSpyObj('linkHeadService', {
|
||||||
|
addTag: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockItem = Object.assign(new Item(), {
|
||||||
|
uuid: 'test-item-uuid',
|
||||||
|
id: 'test-item-id',
|
||||||
|
metadata: {
|
||||||
|
'dspace.entity.type': [{
|
||||||
|
value: 'Publication',
|
||||||
|
language: 'en',
|
||||||
|
place: 0,
|
||||||
|
authority: null,
|
||||||
|
confidence: -1,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
_links: {
|
||||||
|
self: { href: 'obj-selflink' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockItemRequest = Object.assign(new ItemRequest(), {
|
||||||
|
token: 'valid-token',
|
||||||
|
accessToken: 'valid-token',
|
||||||
|
itemId: mockItem.uuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryParams = { accessToken: 'valid-token' };
|
||||||
|
const mockActivatedRoute = {
|
||||||
|
queryParams: new BehaviorSubject(queryParams),
|
||||||
|
data: observableOf({
|
||||||
|
dso: createSuccessfulRemoteDataObject(mockItem),
|
||||||
|
}),
|
||||||
|
params: observableOf({ itemId: mockItem.uuid, queryParams: [ { accessToken: 'valid-token' } ] }),
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
itemRequestService = jasmine.createSpyObj('ItemRequestDataService', {
|
||||||
|
getSanitizedRequestByAccessToken: observableOf(createSuccessfulRemoteDataObject(mockItemRequest)),
|
||||||
|
});
|
||||||
|
router = jasmine.createSpyObj('Router', ['navigateByUrl'], {
|
||||||
|
events: observableOf([]),
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
KeyValuePipe,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ItemRequestDataService, useValue: itemRequestService },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: ItemDataService, useValue: {} },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ServerResponseService, useValue: {} },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: NotifyInfoService, useValue: { isCoarConfigEnabled: () => observableOf(false) } },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
KeyValuePipe,
|
||||||
|
{
|
||||||
|
provide: Store,
|
||||||
|
useValue: {
|
||||||
|
pipe: () => observableOf({}),
|
||||||
|
dispatch: () => {
|
||||||
|
},
|
||||||
|
select: () => observableOf({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AuthService, useValue: {
|
||||||
|
isAuthenticated: () => observableOf(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).overrideComponent(ItemAccessByTokenPageComponent, {
|
||||||
|
set: {
|
||||||
|
template: '<div></div>',
|
||||||
|
},
|
||||||
|
}).overrideComponent(ItemAccessByTokenViewComponent, {
|
||||||
|
set: {
|
||||||
|
template: '<div></div>',
|
||||||
|
},
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ItemAccessByTokenPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests in this component are concerned only with successful access token processing (or error handling)
|
||||||
|
* and a resulting item request object. Testing of template elements is out of scope and left for child components.
|
||||||
|
*/
|
||||||
|
describe('ngOnInit - basic component testing', () => {
|
||||||
|
it('should find valid access token and sanitize it', fakeAsync(() => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
KeyValuePipe,
|
||||||
|
RouterTestingModule.withRoutes([]),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ItemRequestDataService, useValue: itemRequestService },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: AuthService, useValue: {} },
|
||||||
|
{ provide: ItemDataService, useValue: {} },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ServerResponseService, useValue: {} },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: NotifyInfoService, useValue: { isCoarConfigEnabled: () => observableOf(false) } },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
KeyValuePipe,
|
||||||
|
{
|
||||||
|
provide: Store,
|
||||||
|
useValue: {
|
||||||
|
pipe: () => observableOf({}),
|
||||||
|
dispatch: () => {},
|
||||||
|
select: () => observableOf({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ provide: AuthService, useValue: {
|
||||||
|
isAuthenticated: () => observableOf(false ) },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).overrideComponent(ItemAccessByTokenViewComponent, {
|
||||||
|
set: { template: '<div></div>' } } ).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ItemAccessByTokenPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(itemRequestService.getSanitizedRequestByAccessToken).toHaveBeenCalledWith('valid-token');
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should process valid access token and load item request', fakeAsync(() => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
KeyValuePipe,
|
||||||
|
RouterTestingModule.withRoutes([]),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ItemRequestDataService, useValue: itemRequestService },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
{ provide: AuthService, useValue: {} },
|
||||||
|
{ provide: ItemDataService, useValue: {} },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ServerResponseService, useValue: {} },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: NotifyInfoService, useValue: { isCoarConfigEnabled: () => observableOf(false) } },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
KeyValuePipe,
|
||||||
|
{
|
||||||
|
provide: Store,
|
||||||
|
useValue: {
|
||||||
|
pipe: () => observableOf({}),
|
||||||
|
dispatch: () => {},
|
||||||
|
select: () => observableOf({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ provide: AuthService, useValue: {
|
||||||
|
isAuthenticated: () => observableOf(false ) },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).overrideComponent(ItemAccessByTokenViewComponent, {
|
||||||
|
set: { template: '<div></div>' } } ).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ItemAccessByTokenPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.itemRequest$.subscribe((request) => {
|
||||||
|
expect(request).toBeTruthy();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should redirect to forbidden route when access token is missing', fakeAsync(() => {
|
||||||
|
const routeWithoutToken = {
|
||||||
|
queryParams: observableOf({}),
|
||||||
|
data: observableOf({
|
||||||
|
dso: createSuccessfulRemoteDataObject(mockItem),
|
||||||
|
}),
|
||||||
|
params: observableOf({ itemId: mockItem.uuid }),
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
KeyValuePipe,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ItemRequestDataService, useValue: itemRequestService },
|
||||||
|
{ provide: Router, useValue: router },
|
||||||
|
{ provide: ActivatedRoute, useValue: routeWithoutToken },
|
||||||
|
{ provide: AuthService, useValue: {} },
|
||||||
|
{ provide: ItemDataService, useValue: {} },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ServerResponseService, useValue: {} },
|
||||||
|
{ provide: SignpostingDataService, useValue: signpostingDataService },
|
||||||
|
{ provide: LinkHeadService, useValue: linkHeadService },
|
||||||
|
{ provide: NotifyInfoService, useValue: { isCoarConfigEnabled: () => observableOf(false) } },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
|
{
|
||||||
|
provide: Store,
|
||||||
|
useValue: {
|
||||||
|
pipe: () => observableOf({}),
|
||||||
|
dispatch: () => {},
|
||||||
|
select: () => observableOf({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ provide: AuthService, useValue: {
|
||||||
|
isAuthenticated: () => observableOf(false ) },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).overrideComponent(ItemAccessByTokenViewComponent, {
|
||||||
|
set: {
|
||||||
|
template: '<div></div>',
|
||||||
|
} })
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ItemAccessByTokenPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(router.navigateByUrl).toHaveBeenCalledWith(getForbiddenRoute(), { skipLocationChange: false });
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@@ -0,0 +1,178 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
KeyValuePipe,
|
||||||
|
Location,
|
||||||
|
NgForOf,
|
||||||
|
NgIf,
|
||||||
|
} from '@angular/common';
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
Inject,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
PLATFORM_ID,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
RouterLink,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import {
|
||||||
|
filter,
|
||||||
|
map,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
tap,
|
||||||
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { getForbiddenRoute } from '../../app-routing-paths';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { NotifyInfoService } from '../../core/coar-notify/notify-info/notify-info.service';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ItemDataService } from '../../core/data/item-data.service';
|
||||||
|
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||||
|
import { SignpostingDataService } from '../../core/data/signposting-data.service';
|
||||||
|
import { LinkHeadService } from '../../core/services/link-head.service';
|
||||||
|
import { ServerResponseService } from '../../core/services/server-response.service';
|
||||||
|
import { redirectOn4xx } from '../../core/shared/authorized.operators';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import {
|
||||||
|
getFirstCompletedRemoteData,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
} from '../../core/shared/operators';
|
||||||
|
import { fadeInOut } from '../../shared/animations/fade';
|
||||||
|
import { DsoEditMenuComponent } from '../../shared/dso-page/dso-edit-menu/dso-edit-menu.component';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
import { ErrorComponent } from '../../shared/error/error.component';
|
||||||
|
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
|
||||||
|
import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
|
import { ThemedResultsBackButtonComponent } from '../../shared/results-back-button/themed-results-back-button.component';
|
||||||
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
|
import { ViewTrackerComponent } from '../../statistics/angulartics/dspace/view-tracker.component';
|
||||||
|
import { ThemedThumbnailComponent } from '../../thumbnail/themed-thumbnail.component';
|
||||||
|
import { ThemedItemAlertsComponent } from '../alerts/themed-item-alerts.component';
|
||||||
|
import { CollectionsComponent } from '../field-components/collections/collections.component';
|
||||||
|
import { ThemedFullFileSectionComponent } from '../full/field-components/file-section/themed-full-file-section.component';
|
||||||
|
import { ThemedMediaViewerComponent } from '../media-viewer/themed-media-viewer.component';
|
||||||
|
import { MiradorViewerComponent } from '../mirador-viewer/mirador-viewer.component';
|
||||||
|
import { ThemedFileSectionComponent } from '../simple/field-components/file-section/themed-file-section.component';
|
||||||
|
import { ItemPageAbstractFieldComponent } from '../simple/field-components/specific-field/abstract/item-page-abstract-field.component';
|
||||||
|
import { ItemPageDateFieldComponent } from '../simple/field-components/specific-field/date/item-page-date-field.component';
|
||||||
|
import { GenericItemPageFieldComponent } from '../simple/field-components/specific-field/generic/generic-item-page-field.component';
|
||||||
|
import { ThemedItemPageTitleFieldComponent } from '../simple/field-components/specific-field/title/themed-item-page-field.component';
|
||||||
|
import { ItemPageUriFieldComponent } from '../simple/field-components/specific-field/uri/item-page-uri-field.component';
|
||||||
|
import { ItemPageComponent } from '../simple/item-page.component';
|
||||||
|
import { ThemedMetadataRepresentationListComponent } from '../simple/metadata-representation-list/themed-metadata-representation-list.component';
|
||||||
|
import { ItemVersionsComponent } from '../versions/item-versions.component';
|
||||||
|
import { ItemVersionsNoticeComponent } from '../versions/notice/item-versions-notice.component';
|
||||||
|
import { ItemSecureFileSectionComponent } from './field-components/file-section/item-secure-file-section.component';
|
||||||
|
import { ItemAccessByTokenViewComponent } from './item-access-by-token-view.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-access-by-token-item-page',
|
||||||
|
styleUrls: ['./item-access-by-token-page.component.scss'],
|
||||||
|
templateUrl: './item-access-by-token-page.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
animations: [fadeInOut],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ErrorComponent,
|
||||||
|
ThemedLoadingComponent,
|
||||||
|
TranslateModule,
|
||||||
|
ThemedFullFileSectionComponent,
|
||||||
|
CollectionsComponent,
|
||||||
|
ItemVersionsComponent,
|
||||||
|
NgIf,
|
||||||
|
NgForOf,
|
||||||
|
AsyncPipe,
|
||||||
|
KeyValuePipe,
|
||||||
|
RouterLink,
|
||||||
|
ThemedItemPageTitleFieldComponent,
|
||||||
|
DsoEditMenuComponent,
|
||||||
|
ItemVersionsNoticeComponent,
|
||||||
|
ViewTrackerComponent,
|
||||||
|
ThemedItemAlertsComponent,
|
||||||
|
VarDirective,
|
||||||
|
ItemSecureFileSectionComponent,
|
||||||
|
GenericItemPageFieldComponent,
|
||||||
|
ItemPageAbstractFieldComponent,
|
||||||
|
ItemPageDateFieldComponent,
|
||||||
|
ItemPageUriFieldComponent,
|
||||||
|
MetadataFieldWrapperComponent,
|
||||||
|
MiradorViewerComponent,
|
||||||
|
ThemedFileSectionComponent,
|
||||||
|
ThemedMediaViewerComponent,
|
||||||
|
ThemedMetadataRepresentationListComponent,
|
||||||
|
ThemedResultsBackButtonComponent,
|
||||||
|
ThemedThumbnailComponent,
|
||||||
|
ItemAccessByTokenViewComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ItemAccessByTokenPageComponent extends ItemPageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
itemRequest$: Observable<ItemRequest>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected router: Router,
|
||||||
|
protected items: ItemDataService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
protected authorizationService: AuthorizationDataService,
|
||||||
|
protected _location: Location,
|
||||||
|
protected responseService: ServerResponseService,
|
||||||
|
protected signpostingDataService: SignpostingDataService,
|
||||||
|
protected linkHeadService: LinkHeadService,
|
||||||
|
protected notifyInfoService: NotifyInfoService,
|
||||||
|
private itemRequestDataService: ItemRequestDataService,
|
||||||
|
@Inject(PLATFORM_ID) protected platformId: string,
|
||||||
|
) {
|
||||||
|
super(route, router, items, authorizationService, responseService, signpostingDataService, linkHeadService, notifyInfoService, platformId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly hasValue = hasValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise this component
|
||||||
|
* 1. take the access token from the query params and complete the stream
|
||||||
|
* 2. test for access token or redirect to forbidden page
|
||||||
|
* 3. get the sanitized token, make sure it is valid (if not, redirect to forbidden page)
|
||||||
|
* 4. return observable to itemRequest$ for the view to subscribe to
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.itemRequest$ = this.route.queryParams.pipe(
|
||||||
|
take(1),
|
||||||
|
map(params => {
|
||||||
|
if (!hasValue(params?.accessToken)) {
|
||||||
|
this.router.navigateByUrl(getForbiddenRoute(), { skipLocationChange: false });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return params.accessToken;
|
||||||
|
}),
|
||||||
|
filter(token => hasValue(token)),
|
||||||
|
switchMap(token => this.itemRequestDataService.getSanitizedRequestByAccessToken(token)),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
redirectOn4xx(this.router, this.authService),
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
tap(request => {
|
||||||
|
if (!hasValue(request)) {
|
||||||
|
this.router.navigateByUrl(getForbiddenRoute());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Call item page component initialization.
|
||||||
|
super.ngOnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate back in browser history.
|
||||||
|
*/
|
||||||
|
back() {
|
||||||
|
this._location.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,101 @@
|
|||||||
|
<ng-container *ngIf="itemRequest$| async as itemRequest">
|
||||||
|
|
||||||
|
<div class="row" *ngIf="iiifEnabled">
|
||||||
|
<div class="col-12">
|
||||||
|
<ds-mirador-viewer id="iiif-viewer"
|
||||||
|
[object]="object"
|
||||||
|
[searchable]="iiifSearchEnabled"
|
||||||
|
[query]="iiifQuery$ | async">
|
||||||
|
</ds-mirador-viewer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<ds-item-page-title-field [item]="object" class="mr-auto">
|
||||||
|
</ds-item-page-title-field>
|
||||||
|
<ds-dso-edit-menu></ds-dso-edit-menu>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-warning wb-100 mb-2">
|
||||||
|
<p><i class="fa-solid fa-lock-open mr-2" style="color: #26a269;"></i>{{'bitstream-request-a-copy.access-by-token.warning' | translate}}</p>
|
||||||
|
<p *ngIf="hasValue(this.itemRequest?.accessPeriod) && this.itemRequest?.accessPeriod > 0">{{ 'bitstream-request-a-copy.access-by-token.expiry-label' | translate }} {{ getAccessPeriodEndDate() }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-4">
|
||||||
|
<ng-container *ngIf="!(mediaViewer.image || mediaViewer.video)">
|
||||||
|
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
|
||||||
|
<ds-thumbnail [thumbnail]="object?.thumbnail | async"></ds-thumbnail>
|
||||||
|
</ds-metadata-field-wrapper>
|
||||||
|
</ng-container>
|
||||||
|
<div *ngIf="mediaViewer.image || mediaViewer.video" class="mb-2">
|
||||||
|
<ds-item-secure-media-viewer [item]="object" [accessToken]="itemRequest.accessToken">
|
||||||
|
</ds-item-secure-media-viewer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
|
||||||
|
<ds-metadata-representation-list class="ds-item-page-mixed-author-field"
|
||||||
|
[parentItem]="object"
|
||||||
|
[itemType]="'Person'"
|
||||||
|
[metadataFields]="['dc.contributor.author', 'dc.creator']"
|
||||||
|
[label]="'relationships.isAuthorOf' | translate">
|
||||||
|
</ds-metadata-representation-list>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['journal.title']"
|
||||||
|
[label]="'item.page.journal-title'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['journal.identifier.issn']"
|
||||||
|
[label]="'item.page.journal-issn'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['journalvolume.identifier.name']"
|
||||||
|
[label]="'item.page.volume-title'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['dc.publisher']"
|
||||||
|
[label]="'item.page.publisher'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-6">
|
||||||
|
<ds-item-page-abstract-field [item]="object"></ds-item-page-abstract-field>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['dc.description']"
|
||||||
|
[label]="'item.page.description'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['dc.subject']"
|
||||||
|
[separator]="', '"
|
||||||
|
[label]="'item.page.subject'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
<ds-generic-item-page-field [item]="object"
|
||||||
|
[fields]="['dc.identifier.citation']"
|
||||||
|
[label]="'item.page.citation'">
|
||||||
|
</ds-generic-item-page-field>
|
||||||
|
<ds-item-page-uri-field [item]="object"
|
||||||
|
[fields]="['dc.identifier.uri']"
|
||||||
|
[label]="'item.page.uri'">
|
||||||
|
</ds-item-page-uri-field>
|
||||||
|
<ds-item-page-collections [item]="object"></ds-item-page-collections>
|
||||||
|
<ds-item-page-uri-field [item]="object"
|
||||||
|
[fields]="['notify.relation.endorsedBy']"
|
||||||
|
[label]="'item.page.endorsement'">
|
||||||
|
</ds-item-page-uri-field>
|
||||||
|
<ds-item-page-uri-field [item]="object"
|
||||||
|
[fields]="['datacite.relation.isReviewedBy']"
|
||||||
|
[label]="'item.page.review'">
|
||||||
|
</ds-item-page-uri-field>
|
||||||
|
<ds-item-page-uri-field [item]="object"
|
||||||
|
[fields]="['datacite.relation.isSupplementedBy']"
|
||||||
|
[label]="'item.page.dataset'">
|
||||||
|
</ds-item-page-uri-field>
|
||||||
|
<ds-item-page-uri-field [item]="object"
|
||||||
|
[fields]="['datacite.relation.isReferencedBy']"
|
||||||
|
[label]="'item.page.dataset'">
|
||||||
|
</ds-item-page-uri-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<ds-item-secure-full-file-section [item]="object" [itemRequest]="itemRequest">
|
||||||
|
</ds-item-secure-full-file-section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,213 @@
|
|||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
waitForAsync,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { provideMockStore } from '@ngrx/store/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
of as observableOf,
|
||||||
|
of,
|
||||||
|
} from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
APP_CONFIG,
|
||||||
|
APP_DATA_SERVICES_MAP,
|
||||||
|
} from '../../../config/app-config.interface';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||||
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import { ITEM_REQUEST } from '../../core/shared/item-request.resource-type';
|
||||||
|
import { DsoEditMenuComponent } from '../../shared/dso-page/dso-edit-menu/dso-edit-menu.component';
|
||||||
|
import { ErrorComponent } from '../../shared/error/error.component';
|
||||||
|
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||||
|
import { ThemedResultsBackButtonComponent } from '../../shared/results-back-button/themed-results-back-button.component';
|
||||||
|
import { RouterLinkDirectiveStub } from '../../shared/testing/router-link-directive.stub';
|
||||||
|
import { ViewTrackerComponent } from '../../statistics/angulartics/dspace/view-tracker.component';
|
||||||
|
import { ThemedItemAlertsComponent } from '../alerts/themed-item-alerts.component';
|
||||||
|
import { CollectionsComponent } from '../field-components/collections/collections.component';
|
||||||
|
import { ThemedFullFileSectionComponent } from '../full/field-components/file-section/themed-full-file-section.component';
|
||||||
|
import { ThemedMediaViewerComponent } from '../media-viewer/themed-media-viewer.component';
|
||||||
|
import { MiradorViewerComponent } from '../mirador-viewer/mirador-viewer.component';
|
||||||
|
import { ThemedFileSectionComponent } from '../simple/field-components/file-section/themed-file-section.component';
|
||||||
|
import { ThemedMetadataRepresentationListComponent } from '../simple/metadata-representation-list/themed-metadata-representation-list.component';
|
||||||
|
import { ItemVersionsComponent } from '../versions/item-versions.component';
|
||||||
|
import { ItemVersionsNoticeComponent } from '../versions/notice/item-versions-notice.component';
|
||||||
|
import { ItemSecureFileDownloadLinkComponent } from './field-components/file-download-link/item-secure-file-download-link.component';
|
||||||
|
import { ItemSecureFileSectionComponent } from './field-components/file-section/item-secure-file-section.component';
|
||||||
|
import { ItemSecureMediaViewerComponent } from './field-components/media-viewer/item-secure-media-viewer.component';
|
||||||
|
import { ItemAccessByTokenViewComponent } from './item-access-by-token-view.component';
|
||||||
|
|
||||||
|
|
||||||
|
describe('ItemAccessByTokenViewComponent', () => {
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
let itemRequestDataService: ItemRequestDataService;
|
||||||
|
let bitstream: Bitstream;
|
||||||
|
let item: Item;
|
||||||
|
let itemRequest: ItemRequest;
|
||||||
|
let component: ItemAccessByTokenViewComponent;
|
||||||
|
let fixture: ComponentFixture<ItemAccessByTokenViewComponent>;
|
||||||
|
let routeStub: any;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
itemRequestDataService = jasmine.createSpyObj('itemRequestDataService', {
|
||||||
|
canDownload: observableOf(true),
|
||||||
|
});
|
||||||
|
bitstream = Object.assign(new Bitstream(), {
|
||||||
|
uuid: 'bitstreamUuid',
|
||||||
|
});
|
||||||
|
item = Object.assign(new Item(), {
|
||||||
|
uuid: 'itemUuid',
|
||||||
|
metadata: {
|
||||||
|
'dspace.entity.type': [
|
||||||
|
{
|
||||||
|
value: 'Publication',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
_links: {
|
||||||
|
self: { href: 'obj-selflink' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
routeStub = {
|
||||||
|
data: observableOf({
|
||||||
|
dso: createSuccessfulRemoteDataObject(item),
|
||||||
|
}),
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockItemRequest: ItemRequest = Object.assign(new ItemRequest(), {
|
||||||
|
|
||||||
|
});
|
||||||
|
itemRequest = Object.assign(new ItemRequest(),
|
||||||
|
{
|
||||||
|
itemId: item.uuid,
|
||||||
|
bitstreamId: bitstream.uuid,
|
||||||
|
allfiles: false,
|
||||||
|
requestEmail: 'user@name.org',
|
||||||
|
requestName: 'User Name',
|
||||||
|
requestMessage: 'I would like to request a copy',
|
||||||
|
accessPeriod: 3600,
|
||||||
|
decisionDate: new Date().toISOString(),
|
||||||
|
token: 'test-token',
|
||||||
|
type: ITEM_REQUEST,
|
||||||
|
requestDate: new Date().toISOString(),
|
||||||
|
accessToken: 'test-token',
|
||||||
|
expires: null,
|
||||||
|
acceptRequest: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTestbed() {
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(), ItemSecureFileDownloadLinkComponent,
|
||||||
|
RouterLinkDirectiveStub,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
{ provide: RouterLinkDirectiveStub },
|
||||||
|
{ provide: ItemRequestDataService, useValue: itemRequestDataService },
|
||||||
|
provideMockStore(),
|
||||||
|
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
}).overrideComponent(ItemAccessByTokenViewComponent, {
|
||||||
|
remove: {
|
||||||
|
imports: [
|
||||||
|
ErrorComponent,
|
||||||
|
ThemedLoadingComponent,
|
||||||
|
ThemedFullFileSectionComponent,
|
||||||
|
CollectionsComponent,
|
||||||
|
ItemVersionsComponent,
|
||||||
|
DsoEditMenuComponent,
|
||||||
|
ItemVersionsNoticeComponent,
|
||||||
|
ViewTrackerComponent,
|
||||||
|
ThemedItemAlertsComponent,
|
||||||
|
ItemSecureFileSectionComponent,
|
||||||
|
MiradorViewerComponent,
|
||||||
|
ThemedFileSectionComponent,
|
||||||
|
ThemedMediaViewerComponent,
|
||||||
|
ThemedMetadataRepresentationListComponent,
|
||||||
|
ThemedResultsBackButtonComponent,
|
||||||
|
ItemSecureMediaViewerComponent,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}).compileComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockItem = Object.assign(new Item(), {
|
||||||
|
uuid: 'test-item-uuid',
|
||||||
|
id: 'test-item-id',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: observableOf(true),
|
||||||
|
});
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ItemAccessByTokenViewComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.object = item;
|
||||||
|
component.itemRequest$ = of(itemRequest);
|
||||||
|
component.itemRequestSubject.next(itemRequest);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Component and inputs initialised properly', () => {
|
||||||
|
it('should initialize with valid ItemRequest input', () => {
|
||||||
|
//component.itemRequestSubject.next(itemRequest);
|
||||||
|
component.itemRequest$.subscribe(request => {
|
||||||
|
expect(request).toBeDefined();
|
||||||
|
expect(request.accessPeriod).toBe(3600);
|
||||||
|
expect(request.token).toBe('test-token');
|
||||||
|
expect(request.requestName).toBe('User Name');
|
||||||
|
expect(request.requestEmail).toBe('user@name.org');
|
||||||
|
expect(request.requestMessage).toBe('I would like to request a copy');
|
||||||
|
expect(request.allfiles).toBe(false);
|
||||||
|
expect(request.bitstreamId).toBe(bitstream.uuid);
|
||||||
|
expect(request.acceptRequest).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAccessPeriodEndDate', () => {
|
||||||
|
it('should calculate correct end date based on decision date and access period', () => {
|
||||||
|
const testDecisionDate = '2024-01-01T00:00:00Z';
|
||||||
|
const testAccessPeriod = 3600;
|
||||||
|
|
||||||
|
const testRequest = {
|
||||||
|
...itemRequest,
|
||||||
|
decisionDate: testDecisionDate,
|
||||||
|
accessPeriod: testAccessPeriod,
|
||||||
|
};
|
||||||
|
component.itemRequest$ = of(testRequest);
|
||||||
|
component.itemRequestSubject.next(testRequest);
|
||||||
|
const expectedDate = new Date(testDecisionDate);
|
||||||
|
expectedDate.setUTCSeconds(expectedDate.getUTCSeconds() + testAccessPeriod);
|
||||||
|
|
||||||
|
expect(component.getAccessPeriodEndDate()).toEqual(expectedDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when access period is 0', () => {
|
||||||
|
component.itemRequestSubject.next({ ...itemRequest, accessPeriod: 0 });
|
||||||
|
expect(component.getAccessPeriodEndDate()).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@@ -0,0 +1,128 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
KeyValuePipe,
|
||||||
|
NgForOf,
|
||||||
|
NgIf,
|
||||||
|
} from '@angular/common';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
Router,
|
||||||
|
RouterLink,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
Observable,
|
||||||
|
} from 'rxjs';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { RouteService } from '../../core/services/route.service';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import { DsoEditMenuComponent } from '../../shared/dso-page/dso-edit-menu/dso-edit-menu.component';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
import { ErrorComponent } from '../../shared/error/error.component';
|
||||||
|
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
|
||||||
|
import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component';
|
||||||
|
import { ThemedResultsBackButtonComponent } from '../../shared/results-back-button/themed-results-back-button.component';
|
||||||
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
|
import { ViewTrackerComponent } from '../../statistics/angulartics/dspace/view-tracker.component';
|
||||||
|
import { ThemedThumbnailComponent } from '../../thumbnail/themed-thumbnail.component';
|
||||||
|
import { ThemedItemAlertsComponent } from '../alerts/themed-item-alerts.component';
|
||||||
|
import { CollectionsComponent } from '../field-components/collections/collections.component';
|
||||||
|
import { ThemedFullFileSectionComponent } from '../full/field-components/file-section/themed-full-file-section.component';
|
||||||
|
import { ThemedMediaViewerComponent } from '../media-viewer/themed-media-viewer.component';
|
||||||
|
import { MiradorViewerComponent } from '../mirador-viewer/mirador-viewer.component';
|
||||||
|
import { ThemedFileSectionComponent } from '../simple/field-components/file-section/themed-file-section.component';
|
||||||
|
import { ItemPageAbstractFieldComponent } from '../simple/field-components/specific-field/abstract/item-page-abstract-field.component';
|
||||||
|
import { ItemPageDateFieldComponent } from '../simple/field-components/specific-field/date/item-page-date-field.component';
|
||||||
|
import { GenericItemPageFieldComponent } from '../simple/field-components/specific-field/generic/generic-item-page-field.component';
|
||||||
|
import { ThemedItemPageTitleFieldComponent } from '../simple/field-components/specific-field/title/themed-item-page-field.component';
|
||||||
|
import { ItemPageUriFieldComponent } from '../simple/field-components/specific-field/uri/item-page-uri-field.component';
|
||||||
|
import { ItemComponent } from '../simple/item-types/shared/item.component';
|
||||||
|
import { ThemedMetadataRepresentationListComponent } from '../simple/metadata-representation-list/themed-metadata-representation-list.component';
|
||||||
|
import { ItemVersionsComponent } from '../versions/item-versions.component';
|
||||||
|
import { ItemVersionsNoticeComponent } from '../versions/notice/item-versions-notice.component';
|
||||||
|
import { ItemSecureFileSectionComponent } from './field-components/file-section/item-secure-file-section.component';
|
||||||
|
import { ItemSecureMediaViewerComponent } from './field-components/media-viewer/item-secure-media-viewer.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-item-access-by-token-view',
|
||||||
|
styleUrls: ['./item-access-by-token-view.component.scss'],
|
||||||
|
templateUrl: './item-access-by-token-view.component.html',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ErrorComponent,
|
||||||
|
ThemedLoadingComponent,
|
||||||
|
TranslateModule,
|
||||||
|
ThemedFullFileSectionComponent,
|
||||||
|
CollectionsComponent,
|
||||||
|
ItemVersionsComponent,
|
||||||
|
NgIf,
|
||||||
|
NgForOf,
|
||||||
|
AsyncPipe,
|
||||||
|
KeyValuePipe,
|
||||||
|
RouterLink,
|
||||||
|
ThemedItemPageTitleFieldComponent,
|
||||||
|
DsoEditMenuComponent,
|
||||||
|
ItemVersionsNoticeComponent,
|
||||||
|
ViewTrackerComponent,
|
||||||
|
ThemedItemAlertsComponent,
|
||||||
|
VarDirective,
|
||||||
|
ItemSecureFileSectionComponent,
|
||||||
|
GenericItemPageFieldComponent,
|
||||||
|
ItemPageAbstractFieldComponent,
|
||||||
|
ItemPageDateFieldComponent,
|
||||||
|
ItemPageUriFieldComponent,
|
||||||
|
MetadataFieldWrapperComponent,
|
||||||
|
MiradorViewerComponent,
|
||||||
|
ThemedFileSectionComponent,
|
||||||
|
ThemedMediaViewerComponent,
|
||||||
|
ThemedMetadataRepresentationListComponent,
|
||||||
|
ThemedResultsBackButtonComponent,
|
||||||
|
ThemedThumbnailComponent,
|
||||||
|
ItemSecureMediaViewerComponent,
|
||||||
|
//ItemPageTitleFieldComponent,
|
||||||
|
//ThumbnailComponent,
|
||||||
|
//MetadataRepresentationListComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ItemAccessByTokenViewComponent extends ItemComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() itemRequest$: Observable<ItemRequest>;
|
||||||
|
itemRequestSubject = new BehaviorSubject<ItemRequest>(null);
|
||||||
|
expiryDate: Date;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected routeService: RouteService,
|
||||||
|
protected router: Router,
|
||||||
|
) {
|
||||||
|
super(routeService, router);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly hasValue = hasValue;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.itemRequest$.pipe(
|
||||||
|
filter(request => hasValue(request)),
|
||||||
|
).subscribe(request => {
|
||||||
|
this.itemRequestSubject.next(request);
|
||||||
|
super.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccessPeriodEndDate(): Date {
|
||||||
|
const request = this.itemRequestSubject.getValue();
|
||||||
|
// Set expiry, if not 0
|
||||||
|
if (hasValue(request) && request.accessPeriod > 0) {
|
||||||
|
const date = new Date(request.decisionDate);
|
||||||
|
date.setUTCSeconds(date.getUTCSeconds() + request.accessPeriod);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,4 +11,5 @@ import {
|
|||||||
})
|
})
|
||||||
export class RouterLinkDirectiveStub {
|
export class RouterLinkDirectiveStub {
|
||||||
@Input() routerLink: any;
|
@Input() routerLink: any;
|
||||||
|
@Input() queryParams: any;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user