mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-18 07:23:03 +00:00
88248: #346: Withdrawn item tombstone page
This commit is contained in:
@@ -4,19 +4,21 @@
|
||||
<ds-item-alerts [item]="item"></ds-item-alerts>
|
||||
<ds-item-versions-notice [item]="item"></ds-item-versions-notice>
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
<div *ngIf="!item.isWithdrawn || (isAdmin$|async)" class="full-item-info">
|
||||
<div class="d-flex flex-row">
|
||||
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item"
|
||||
[tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
||||
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
||||
{{"item.page.link.simple" | translate}}
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-responsive table-striped">
|
||||
<tbody>
|
||||
<div class="simple-view-link my-3" *ngIf="!fromWfi">
|
||||
<a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
|
||||
{{"item.page.link.simple" | translate}}
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-responsive table-striped">
|
||||
<tbody>
|
||||
<ng-container *ngFor="let mdEntry of (metadata$ | async) | keyvalue">
|
||||
<tr *ngFor="let mdValue of mdEntry.value">
|
||||
<td>{{mdEntry.key}}</td>
|
||||
@@ -24,14 +26,16 @@
|
||||
<td>{{mdValue.language}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section>
|
||||
<ds-item-page-collections [item]="item"></ds-item-page-collections>
|
||||
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions>
|
||||
<div class="button-row bottom" *ngIf="fromWfi">
|
||||
<div class="text-right">
|
||||
<button class="btn btn-outline-secondary mr-1" (click)="back()"><i class="fas fa-arrow-left"></i> {{'item.page.return' | translate}}</button>
|
||||
</tbody>
|
||||
</table>
|
||||
<ds-item-page-full-file-section [item]="item"></ds-item-page-full-file-section>
|
||||
<ds-item-page-collections [item]="item"></ds-item-page-collections>
|
||||
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions>
|
||||
<div class="button-row bottom" *ngIf="fromWfi">
|
||||
<div class="text-right">
|
||||
<button class="btn btn-outline-secondary mr-1" (click)="back()"><i
|
||||
class="fas fa-arrow-left"></i> {{'item.page.return' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -11,12 +11,15 @@ import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { BehaviorSubject, of as observableOf } from 'rxjs';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
import { createRelationshipsObservable } from '../simple/item-types/shared/item.component.spec';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
@@ -30,6 +33,13 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
}
|
||||
});
|
||||
|
||||
const mockWithdrawnItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
metadata: [],
|
||||
relationships: createRelationshipsObservable(),
|
||||
isWithdrawn: true
|
||||
});
|
||||
|
||||
const metadataServiceStub = {
|
||||
/* tslint:disable:no-empty */
|
||||
processRemoteData: () => {
|
||||
@@ -44,6 +54,7 @@ describe('FullItemPageComponent', () => {
|
||||
let authService: AuthService;
|
||||
let routeStub: ActivatedRouteStub;
|
||||
let routeData;
|
||||
let authorizationDataService: AuthorizationDataService;
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +72,10 @@ describe('FullItemPageComponent', () => {
|
||||
data: observableOf(routeData)
|
||||
});
|
||||
|
||||
authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||
isAuthorized: observableOf(false),
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -74,6 +89,7 @@ describe('FullItemPageComponent', () => {
|
||||
{ provide: ItemDataService, useValue: {} },
|
||||
{ provide: MetadataService, useValue: metadataServiceStub },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -111,4 +127,53 @@ describe('FullItemPageComponent', () => {
|
||||
expect(simpleViewBtn).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('when the item is withdrawn and the user is an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.isAdmin$ = observableOf(true);
|
||||
comp.itemRD$ = new BehaviorSubject<RemoteData<Item>>(createSuccessfulRemoteDataObject(mockWithdrawnItem));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
describe('when the item is withdrawn and the user is not an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = new BehaviorSubject<RemoteData<Item>>(createSuccessfulRemoteDataObject(mockWithdrawnItem));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||
expect(objectLoader).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn and the user is an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.isAdmin$ = observableOf(true);
|
||||
comp.itemRD$ = new BehaviorSubject<RemoteData<Item>>(createSuccessfulRemoteDataObject(mockItem));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn and the user is not an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = new BehaviorSubject<RemoteData<Item>>(createSuccessfulRemoteDataObject(mockItem));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('.full-item-info'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -15,6 +15,7 @@ import { fadeInOut } from '../../shared/animations/fade';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { Location } from '@angular/common';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,9 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit,
|
||||
router: Router,
|
||||
items: ItemDataService,
|
||||
authService: AuthService,
|
||||
authorizationService: AuthorizationDataService,
|
||||
private _location: Location) {
|
||||
super(route, router, items, authService);
|
||||
super(route, router, items, authService, authorizationService);
|
||||
}
|
||||
|
||||
/*** AoT inheritance fix, will hopefully be resolved in the near future **/
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<ds-item-alerts [item]="item"></ds-item-alerts>
|
||||
<ds-item-versions-notice [item]="item"></ds-item-versions-notice>
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
<ds-listable-object-component-loader *ngIf="!item.isWithdrawn || (isAdmin$|async)" [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -21,6 +21,7 @@ import {
|
||||
} from '../../shared/remote-data.utils';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
@@ -28,10 +29,18 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
const mockWithdrawnItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
metadata: [],
|
||||
relationships: createRelationshipsObservable(),
|
||||
isWithdrawn: true
|
||||
});
|
||||
|
||||
describe('ItemPageComponent', () => {
|
||||
let comp: ItemPageComponent;
|
||||
let fixture: ComponentFixture<ItemPageComponent>;
|
||||
let authService: AuthService;
|
||||
let authorizationDataService: AuthorizationDataService;
|
||||
|
||||
const mockMetadataService = {
|
||||
/* tslint:disable:no-empty */
|
||||
@@ -48,6 +57,9 @@ describe('ItemPageComponent', () => {
|
||||
isAuthenticated: observableOf(true),
|
||||
setRedirectUrl: {}
|
||||
});
|
||||
authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||
isAuthorized: observableOf(false),
|
||||
});
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
@@ -63,6 +75,7 @@ describe('ItemPageComponent', () => {
|
||||
{ provide: MetadataService, useValue: mockMetadataService },
|
||||
{ provide: Router, useValue: {} },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: AuthorizationDataService, useValue: authorizationDataService },
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -102,4 +115,53 @@ describe('ItemPageComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is withdrawn and the user is an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.isAdmin$ = observableOf(true);
|
||||
comp.itemRD$ = createSuccessfulRemoteDataObject$(mockWithdrawnItem);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
describe('when the item is withdrawn and the user is not an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = createSuccessfulRemoteDataObject$(mockWithdrawnItem);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||
expect(objectLoader).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn and the user is an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.isAdmin$ = observableOf(true);
|
||||
comp.itemRD$ = createSuccessfulRemoteDataObject$(mockItem);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item is not withdrawn and the user is not an admin', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = createSuccessfulRemoteDataObject$(mockItem);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the item', () => {
|
||||
const objectLoader = fixture.debugElement.query(By.css('ds-listable-object-component-loader'));
|
||||
expect(objectLoader.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@@ -13,6 +13,8 @@ import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shar
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { getItemPageRoute } from '../item-page-routing-paths';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||
|
||||
/**
|
||||
* This component renders a simple item page.
|
||||
@@ -48,11 +50,17 @@ export class ItemPageComponent implements OnInit {
|
||||
*/
|
||||
itemPageRoute$: Observable<string>;
|
||||
|
||||
/**
|
||||
* Whether the current user is an admin or not
|
||||
*/
|
||||
isAdmin$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private items: ItemDataService,
|
||||
private authService: AuthService,
|
||||
private authorizationService: AuthorizationDataService
|
||||
) { }
|
||||
|
||||
/**
|
||||
@@ -67,5 +75,7 @@ export class ItemPageComponent implements OnInit {
|
||||
getAllSucceededRemoteDataPayload(),
|
||||
map((item) => getItemPageRoute(item))
|
||||
);
|
||||
|
||||
this.isAdmin$ = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,13 @@
|
||||
<div>
|
||||
<div *ngIf="item && !item.isDiscoverable" class="private-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.private' | translate"></ds-alert>
|
||||
</div>
|
||||
<div *ngIf="item && item.isWithdrawn" class="withdrawn-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.withdrawn' | translate"></ds-alert>
|
||||
</div>
|
||||
<div *ngIf="item && !item.isDiscoverable" class="private-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning" [content]="'item.alerts.private' | translate"></ds-alert>
|
||||
</div>
|
||||
<div *ngIf="item && item.isWithdrawn" class="withdrawn-warning">
|
||||
<ds-alert [type]="AlertTypeEnum.Warning">
|
||||
<div class="d-flex justify-content-between flex-wrap">
|
||||
<span>{{'item.alerts.withdrawn' | translate}}</span>
|
||||
<a routerLink="/home" class="btn btn-primary btn-sm">{{"404.link.home-page" | translate}}</a>
|
||||
</div>
|
||||
</ds-alert>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user