mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
83635: Request a copy - Request page
This commit is contained in:
@@ -22,6 +22,9 @@ export function getBitstreamModuleRoute() {
|
|||||||
export function getBitstreamDownloadRoute(bitstream): string {
|
export function getBitstreamDownloadRoute(bitstream): string {
|
||||||
return new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString();
|
return new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString();
|
||||||
}
|
}
|
||||||
|
export function getBitstreamRequestACopyRoute(bitstream): string {
|
||||||
|
return new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'request-a-copy').toString();
|
||||||
|
}
|
||||||
|
|
||||||
export const ADMIN_MODULE_PATH = 'admin';
|
export const ADMIN_MODULE_PATH = 'admin';
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import { ResourcePolicyResolver } from '../shared/resource-policies/resolvers/re
|
|||||||
import { ResourcePolicyEditComponent } from '../shared/resource-policies/edit/resource-policy-edit.component';
|
import { ResourcePolicyEditComponent } from '../shared/resource-policies/edit/resource-policy-edit.component';
|
||||||
import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component';
|
import { BitstreamAuthorizationsComponent } from './bitstream-authorizations/bitstream-authorizations.component';
|
||||||
import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver';
|
||||||
|
import { BitstreamRequestACopyPageComponent } from '../shared/bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||||
|
|
||||||
const EDIT_BITSTREAM_PATH = ':id/edit';
|
const EDIT_BITSTREAM_PATH = ':id/edit';
|
||||||
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
||||||
@@ -44,6 +45,14 @@ const EDIT_BITSTREAM_AUTHORIZATIONS_PATH = ':id/authorizations';
|
|||||||
bitstream: BitstreamPageResolver
|
bitstream: BitstreamPageResolver
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Resolve angular bitstream download URLs
|
||||||
|
path: ':id/request-a-copy',
|
||||||
|
component: BitstreamRequestACopyPageComponent,
|
||||||
|
resolve: {
|
||||||
|
bitstream: BitstreamPageResolver
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: EDIT_BITSTREAM_PATH,
|
path: EDIT_BITSTREAM_PATH,
|
||||||
component: EditBitstreamPageComponent,
|
component: EditBitstreamPageComponent,
|
||||||
|
@@ -14,6 +14,7 @@ export enum FeatureID {
|
|||||||
IsCollectionAdmin = 'isCollectionAdmin',
|
IsCollectionAdmin = 'isCollectionAdmin',
|
||||||
IsCommunityAdmin = 'isCommunityAdmin',
|
IsCommunityAdmin = 'isCommunityAdmin',
|
||||||
CanDownload = 'canDownload',
|
CanDownload = 'canDownload',
|
||||||
|
CanRequestACopy = 'canRequestACopy',
|
||||||
CanManageVersions = 'canManageVersions',
|
CanManageVersions = 'canManageVersions',
|
||||||
CanManageBitstreamBundles = 'canManageBitstreamBundles',
|
CanManageBitstreamBundles = 'canManageBitstreamBundles',
|
||||||
CanManageRelationships = 'canManageRelationships',
|
CanManageRelationships = 'canManageRelationships',
|
||||||
|
59
src/app/core/data/item-request-data.service.ts
Normal file
59
src/app/core/data/item-request-data.service.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { filter, find, map } from 'rxjs/operators';
|
||||||
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
|
import { RemoteData } from './remote-data';
|
||||||
|
import { PostRequest } from './request.models';
|
||||||
|
import { RequestService } from './request.service';
|
||||||
|
import { ItemRequest } from '../shared/item-request.model';
|
||||||
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint
|
||||||
|
*/
|
||||||
|
@Injectable(
|
||||||
|
{
|
||||||
|
providedIn: 'root',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export class ItemRequestDataService {
|
||||||
|
|
||||||
|
protected linkPath = 'itemrequests';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected requestService: RequestService,
|
||||||
|
protected rdbService: RemoteDataBuildService,
|
||||||
|
protected halService: HALEndpointService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemRequestEndpoint(): Observable<string> {
|
||||||
|
return this.halService.getEndpoint(this.linkPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFindItemRequestEndpoint(requestID: string): Observable<string> {
|
||||||
|
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||||
|
filter((href: string) => isNotEmpty(href)),
|
||||||
|
map((href: string) => `${href}/${requestID}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
requestACopy(itemRequest: ItemRequest): Observable<RemoteData<ItemRequest>> {
|
||||||
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
|
const href$ = this.getItemRequestEndpoint();
|
||||||
|
|
||||||
|
href$.pipe(
|
||||||
|
find((href: string) => hasValue(href)),
|
||||||
|
map((href: string) => {
|
||||||
|
const request = new PostRequest(requestId, href, itemRequest);
|
||||||
|
this.requestService.send(request);
|
||||||
|
})
|
||||||
|
).subscribe();
|
||||||
|
|
||||||
|
return this.rdbService.buildFromRequestUUID<ItemRequest>(requestId).pipe(
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
src/app/core/shared/item-request.model.ts
Normal file
79
src/app/core/shared/item-request.model.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { autoserialize } from 'cerialize';
|
||||||
|
import { typedObject } from '../cache/builders/build-decorators';
|
||||||
|
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||||
|
import { ResourceType } from './resource-type';
|
||||||
|
import { ITEM_REQUEST } from './item-request.resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model class for a Configuration Property
|
||||||
|
*/
|
||||||
|
@typedObject
|
||||||
|
export class ItemRequest {
|
||||||
|
static type = ITEM_REQUEST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object type
|
||||||
|
*/
|
||||||
|
@excludeFromEquals
|
||||||
|
@autoserialize
|
||||||
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* opaque string which uniquely identifies this request
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if the request is for all bitstreams of the item.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
allfiles: boolean;
|
||||||
|
/**
|
||||||
|
* email address of the person requesting the files.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
requestEmail: string;
|
||||||
|
/**
|
||||||
|
* Human-readable name of the person requesting the files.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
requestName: string;
|
||||||
|
/**
|
||||||
|
* arbitrary message provided by the person requesting the files.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
requestMessage: string;
|
||||||
|
/**
|
||||||
|
* date that the request was recorded.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
requestDate: string;
|
||||||
|
/**
|
||||||
|
* true if the request has been granted.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
acceptRequest: boolean;
|
||||||
|
/**
|
||||||
|
* date that the request was granted or denied.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
decisionDate: string;
|
||||||
|
/**
|
||||||
|
* date on which the request is considered expired.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
expires: string;
|
||||||
|
/**
|
||||||
|
* UUID of the requested Item.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
itemId: string;
|
||||||
|
/**
|
||||||
|
* UUID of the requested bitstream.
|
||||||
|
*/
|
||||||
|
@autoserialize
|
||||||
|
bitstreamId: string;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
9
src/app/core/shared/item-request.resource-type.ts
Normal file
9
src/app/core/shared/item-request.resource-type.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ResourceType } from './resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type for ItemRequest.
|
||||||
|
*
|
||||||
|
* Needs to be in a separate file to prevent circular
|
||||||
|
* dependencies in webpack.
|
||||||
|
*/
|
||||||
|
export const ITEM_REQUEST = new ResourceType('itemrequest');
|
@@ -0,0 +1,86 @@
|
|||||||
|
<div class="container">
|
||||||
|
<h3 class="mb-4">{{'bitstream-request-a-copy.header' | translate}}</h3>
|
||||||
|
<div *ngIf="canDownload$|async" class="alert alert-success">
|
||||||
|
<span>{{'bitstream-request-a-copy.alert.canDownload1' | translate}}</span>
|
||||||
|
<a [routerLink]="getBitstreamLink()">{{'bitstream-request-a-copy.alert.canDownload2'| translate}}</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>{{'bitstream-request-a-copy.intro' | translate}}</p>
|
||||||
|
<p>{{itemName}}</p>
|
||||||
|
</div>
|
||||||
|
<form [class]="'ng-invalid'" [formGroup]="requestCopyForm" (ngSubmit)="onSubmit()">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="name">{{'bitstream-request-a-copy.name.label' | translate}}</label>
|
||||||
|
<input [className]="(name.invalid) && (name.dirty || name.touched) ? 'form-control is-invalid' :'form-control'"
|
||||||
|
type="text" id="name" formControlName="name"/>
|
||||||
|
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||||
|
class="invalid-feedback show-feedback">
|
||||||
|
<span *ngIf="name.errors && name.errors.required">
|
||||||
|
{{ 'bitstream-request-a-copy.name.error' | translate }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="email">{{'bitstream-request-a-copy.email.label' | translate}}</label>
|
||||||
|
<input
|
||||||
|
[className]="(email.invalid) && (email.dirty || email.touched) ? 'form-control is-invalid' :'form-control'"
|
||||||
|
id="email" formControlName="email">
|
||||||
|
<div *ngIf="email.invalid && (email.dirty || email.touched)"
|
||||||
|
class="invalid-feedback show-feedback">
|
||||||
|
<span *ngIf="email.errors">
|
||||||
|
{{ 'bitstream-request-a-copy.email.error' | translate }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted ds-hint">{{'bitstream-request-a-copy.email.hint' |translate}}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<div>{{'bitstream-request-a-copy.allfiles.label' |translate}}</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<input [className]="'form-check-input'" type="radio"
|
||||||
|
id="allfiles-true" formControlName="allfiles" value="true" [checked]="true">
|
||||||
|
<label class="form-check-label"
|
||||||
|
for="allfiles-true">{{'bitstream-request-a-copy.files-all-true.label' | translate}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<input [className]="'form-check-input'" type="radio"
|
||||||
|
id="allfiles-false" formControlName="allfiles" value="false">
|
||||||
|
<label class="form-check-label"
|
||||||
|
for="allfiles-false">{{'bitstream-request-a-copy.files-all-false.label' | translate}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<label
|
||||||
|
for="message">{{'bitstream-request-a-copy.message.label' | translate}}</label>
|
||||||
|
<textarea rows="5"
|
||||||
|
[className]="'form-control'"
|
||||||
|
id="message" formControlName="message"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-right">
|
||||||
|
|
||||||
|
<a (click)="navigateBack()" role="button" class="btn btn-outline-secondary mr-1">
|
||||||
|
<i class="fas fa-arrow-left"></i> {{'bitstream-request-a-copy.return' | translate}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button
|
||||||
|
[disabled]="requestCopyForm.invalid"
|
||||||
|
class="btn btn-default btn-primary"
|
||||||
|
(click)="onSubmit()">{{'bitstream-request-a-copy.submit' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,256 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import {
|
||||||
|
createFailedRemoteDataObject$,
|
||||||
|
createSuccessfulRemoteDataObject,
|
||||||
|
createSuccessfulRemoteDataObject$
|
||||||
|
} from '../remote-data.utils';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page.component';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { RouterStub } from '../testing/router.stub';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { NotificationsServiceStub } from '../testing/notifications-service.stub';
|
||||||
|
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||||
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { DSONameServiceMock } from '../mocks/dso-name.service.mock';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { Bundle } from '../../core/shared/bundle.model';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
|
describe('BitstreamRequestACopyPageComponent', () => {
|
||||||
|
let component: BitstreamRequestACopyPageComponent;
|
||||||
|
let fixture: ComponentFixture<BitstreamRequestACopyPageComponent>;
|
||||||
|
|
||||||
|
let authService: AuthService;
|
||||||
|
let authorizationService: AuthorizationDataService;
|
||||||
|
let activatedRoute;
|
||||||
|
let router;
|
||||||
|
let itemRequestDataService;
|
||||||
|
let notificationsService;
|
||||||
|
let location;
|
||||||
|
|
||||||
|
let item: Item;
|
||||||
|
let bitstream: Bitstream;
|
||||||
|
let eperson;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
eperson = Object.assign(new EPerson(), {
|
||||||
|
email: 'test@mail.org',
|
||||||
|
metadata: {
|
||||||
|
'eperson.firstname': [{value: 'Test'}],
|
||||||
|
'eperson.lastname': [{value: 'User'}],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
authService = jasmine.createSpyObj('authService', {
|
||||||
|
isAuthenticated: observableOf(false),
|
||||||
|
getAuthenticatedUserFromStore: observableOf(eperson)
|
||||||
|
});
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationSerivice', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
itemRequestDataService = jasmine.createSpyObj('itemRequestDataService', {
|
||||||
|
requestACopy: createSuccessfulRemoteDataObject$({})
|
||||||
|
});
|
||||||
|
|
||||||
|
location = jasmine.createSpyObj('location', {
|
||||||
|
back: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationsService = new NotificationsServiceStub();
|
||||||
|
|
||||||
|
item = Object.assign(new Item(), {uuid: 'item-uuid'});
|
||||||
|
|
||||||
|
const bundle = Object.assign(new Bundle(), {
|
||||||
|
uuid: 'bundle-uuid',
|
||||||
|
item: createSuccessfulRemoteDataObject$(item)
|
||||||
|
});
|
||||||
|
|
||||||
|
bitstream = Object.assign(new Bitstream(), {
|
||||||
|
uuid: 'bitstreamUuid',
|
||||||
|
bundle: createSuccessfulRemoteDataObject$(bundle),
|
||||||
|
_links: {
|
||||||
|
content: {href: 'bitstream-content-link'},
|
||||||
|
self: {href: 'bitstream-self-link'},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
activatedRoute = {
|
||||||
|
data: observableOf({
|
||||||
|
bitstream: createSuccessfulRemoteDataObject(
|
||||||
|
bitstream
|
||||||
|
)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
router = new RouterStub();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTestbed() {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CommonModule, TranslateModule.forRoot(), FormsModule, ReactiveFormsModule],
|
||||||
|
declarations: [BitstreamRequestACopyPageComponent],
|
||||||
|
providers: [
|
||||||
|
{provide: Location, useValue: location},
|
||||||
|
{provide: ActivatedRoute, useValue: activatedRoute},
|
||||||
|
{provide: Router, useValue: router},
|
||||||
|
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||||
|
{provide: AuthService, useValue: authService},
|
||||||
|
{provide: ItemRequestDataService, useValue: itemRequestDataService},
|
||||||
|
{provide: NotificationsService, useValue: notificationsService},
|
||||||
|
{provide: DSONameService, useValue: new DSONameServiceMock()},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('init', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should init the comp', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should show a form to request a copy', () => {
|
||||||
|
describe('when the user is not logged in', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('show the form with no values filled in based on the user', () => {
|
||||||
|
expect(component.name.value).toEqual('');
|
||||||
|
expect(component.email.value).toEqual('');
|
||||||
|
expect(component.allfiles.value).toEqual('true');
|
||||||
|
expect(component.message.value).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the user is logged in', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(true));
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('show the form with values filled in based on the user', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.name.value).toEqual(eperson.name);
|
||||||
|
expect(component.email.value).toEqual(eperson.email);
|
||||||
|
expect(component.allfiles.value).toEqual('true');
|
||||||
|
expect(component.message.value).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the user has authorization to download the file', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
(authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(true));
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should show an alert indicating the user can download the file', () => {
|
||||||
|
const alert = fixture.debugElement.query(By.css('.alert')).nativeElement;
|
||||||
|
expect(alert.innerHTML).toContain('bitstream-request-a-copy.alert.canDownload');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSubmit', () => {
|
||||||
|
describe('onSuccess', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should take the current form information and submit it', () => {
|
||||||
|
component.name.patchValue('User Name');
|
||||||
|
component.email.patchValue('user@name.org');
|
||||||
|
component.allfiles.patchValue('false');
|
||||||
|
component.message.patchValue('I would like to request a copy');
|
||||||
|
|
||||||
|
component.onSubmit();
|
||||||
|
const 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'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(itemRequestDataService.requestACopy).toHaveBeenCalledWith(itemRequest);
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
expect(location.back).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onFail', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
init();
|
||||||
|
(itemRequestDataService.requestACopy as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$());
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(BitstreamRequestACopyPageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should take the current form information and submit it', () => {
|
||||||
|
component.name.patchValue('User Name');
|
||||||
|
component.email.patchValue('user@name.org');
|
||||||
|
component.allfiles.patchValue('false');
|
||||||
|
component.message.patchValue('I would like to request a copy');
|
||||||
|
|
||||||
|
component.onSubmit();
|
||||||
|
const 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'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(itemRequestDataService.requestACopy).toHaveBeenCalledWith(itemRequest);
|
||||||
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
|
expect(location.back).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,199 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { map, switchMap, take } from 'rxjs/operators';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { hasValue, isNotEmpty } from '../empty.util';
|
||||||
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||||
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||||
|
import { getBitstreamDownloadRoute, getForbiddenRoute } from '../../app-routing-paths';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ItemRequestDataService } from '../../core/data/item-request-data.service';
|
||||||
|
import { ItemRequest } from '../../core/shared/item-request.model';
|
||||||
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
|
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
|
||||||
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { Location } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-bitstream-request-a-copy-page',
|
||||||
|
templateUrl: './bitstream-request-a-copy-page.component.html'
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Page component for requesting a copy for a bitstream
|
||||||
|
*/
|
||||||
|
export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
bitstream$: Observable<Bitstream>;
|
||||||
|
|
||||||
|
canDownload$: Observable<boolean>;
|
||||||
|
private subs: Subscription[] = [];
|
||||||
|
requestCopyForm: FormGroup;
|
||||||
|
bitstream: Bitstream;
|
||||||
|
item: Item;
|
||||||
|
itemName: string;
|
||||||
|
|
||||||
|
constructor(private location: Location,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
protected router: Router,
|
||||||
|
private authorizationService: AuthorizationDataService,
|
||||||
|
private auth: AuthService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private itemRequestDataService: ItemRequestDataService,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private dsoNameService: DSONameService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.requestCopyForm = this.formBuilder.group({
|
||||||
|
name: new FormControl('', {
|
||||||
|
validators: [Validators.required],
|
||||||
|
}),
|
||||||
|
email: new FormControl('', {
|
||||||
|
validators: [Validators.required,
|
||||||
|
Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$')]
|
||||||
|
}),
|
||||||
|
allfiles: new FormControl(''),
|
||||||
|
message: new FormControl(''),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.bitstream$ = this.route.data.pipe(
|
||||||
|
map((data) => data.bitstream),
|
||||||
|
getFirstSucceededRemoteDataPayload()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subs.push(this.bitstream$.subscribe((bitstream) => {
|
||||||
|
this.bitstream = bitstream;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.subs.push(this.bitstream$.pipe(
|
||||||
|
switchMap((bitstream) => bitstream.bundle),
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
switchMap((bundle) => bundle.item),
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
).subscribe((item) => {
|
||||||
|
this.item = item;
|
||||||
|
this.itemName = this.dsoNameService.getName(item);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.canDownload$ = this.bitstream$.pipe(
|
||||||
|
switchMap((bitstream) => this.authorizationService.isAuthorized(FeatureID.CanDownload, isNotEmpty(bitstream) ? bitstream.self : undefined))
|
||||||
|
);
|
||||||
|
const canRequestCopy$ = this.bitstream$.pipe(
|
||||||
|
switchMap((bitstream) => this.authorizationService.isAuthorized(FeatureID.CanRequestACopy, isNotEmpty(bitstream) ? bitstream.self : undefined)),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subs.push(observableCombineLatest([this.canDownload$, canRequestCopy$]).subscribe(([canDownload, canRequestCopy]) => {
|
||||||
|
if (!canDownload && !canRequestCopy) {
|
||||||
|
this.router.navigateByUrl(getForbiddenRoute(), {skipLocationChange: true});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.initValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this.requestCopyForm.get('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
get email() {
|
||||||
|
return this.requestCopyForm.get('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
get message() {
|
||||||
|
return this.requestCopyForm.get('message');
|
||||||
|
}
|
||||||
|
|
||||||
|
get allfiles() {
|
||||||
|
return this.requestCopyForm.get('allfiles');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the form values based on the current user.
|
||||||
|
*/
|
||||||
|
private initValues() {
|
||||||
|
this.getCurrentUser().pipe(take(1)).subscribe((user) => {
|
||||||
|
this.requestCopyForm.patchValue({allfiles: 'true'});
|
||||||
|
if (hasValue(user)) {
|
||||||
|
this.requestCopyForm.patchValue({name: user.name, email: user.email});
|
||||||
|
console.log('ping');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the current user
|
||||||
|
*/
|
||||||
|
private getCurrentUser(): Observable<EPerson> {
|
||||||
|
return this.auth.isAuthenticated().pipe(
|
||||||
|
switchMap((authenticated) => {
|
||||||
|
if (authenticated) {
|
||||||
|
return this.auth.getAuthenticatedUserFromStore();
|
||||||
|
} else {
|
||||||
|
return observableOf(undefined);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit the the form values as an item request to the server.
|
||||||
|
* When the submission is successful, the user will be redirected to the item page and a success notification will be shown.
|
||||||
|
* When the submission fails, the user will stay on the page and an error notification will be shown
|
||||||
|
*/
|
||||||
|
onSubmit() {
|
||||||
|
const itemRequest = new ItemRequest();
|
||||||
|
if (hasValue(this.item)) {
|
||||||
|
itemRequest.itemId = this.item.uuid;
|
||||||
|
}
|
||||||
|
itemRequest.bitstreamId = this.bitstream.uuid;
|
||||||
|
itemRequest.allfiles = this.allfiles.value;
|
||||||
|
itemRequest.requestEmail = this.email.value;
|
||||||
|
itemRequest.requestName = this.name.value;
|
||||||
|
itemRequest.requestMessage = this.message.value;
|
||||||
|
|
||||||
|
this.itemRequestDataService.requestACopy(itemRequest).pipe(
|
||||||
|
getFirstCompletedRemoteData()
|
||||||
|
).subscribe((rd) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
this.notificationsService.success(this.translateService.get('bitstream-request-a-copy.submit.success'));
|
||||||
|
this.navigateBack();
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error(this.translateService.get('bitstream-request-a-copy.submit.error'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (hasValue(this.subs)) {
|
||||||
|
this.subs.forEach((sub) => {
|
||||||
|
if (hasValue(sub)) {
|
||||||
|
sub.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates back to the user's previous location
|
||||||
|
*/
|
||||||
|
navigateBack() {
|
||||||
|
this.location.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the link to the bistream download page
|
||||||
|
*/
|
||||||
|
getBitstreamLink() {
|
||||||
|
return [getBitstreamDownloadRoute(this.bitstream)];
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
<a [href]="bitstreamPath" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
<a [href]="(bitstreamPath$| async)" [target]="isBlank ? '_blank': '_self'" [ngClass]="cssClasses">
|
||||||
|
<span *ngIf="!(canDownload$ |async)"><i class="fas fa-lock"></i></span>
|
||||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@@ -1,62 +1,132 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { FileDownloadLinkComponent } from './file-download-link.component';
|
import { FileDownloadLinkComponent } from './file-download-link.component';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
|
||||||
import { FileService } from '../../core/shared/file.service';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { Bitstream } from '../../core/shared/bitstream.model';
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
import { getBitstreamModuleRoute } from '../../app-routing-paths';
|
import { getBitstreamModuleRoute } from '../../app-routing-paths';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
|
||||||
describe('FileDownloadLinkComponent', () => {
|
describe('FileDownloadLinkComponent', () => {
|
||||||
let component: FileDownloadLinkComponent;
|
let component: FileDownloadLinkComponent;
|
||||||
let fixture: ComponentFixture<FileDownloadLinkComponent>;
|
let fixture: ComponentFixture<FileDownloadLinkComponent>;
|
||||||
|
|
||||||
let authService: AuthService;
|
let scheduler;
|
||||||
let fileService: FileService;
|
let authorizationService: AuthorizationDataService;
|
||||||
|
|
||||||
let bitstream: Bitstream;
|
let bitstream: Bitstream;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
authService = jasmine.createSpyObj('authService', {
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
isAuthenticated: observableOf(true)
|
isAuthorized: cold('-a', {a: true})
|
||||||
});
|
});
|
||||||
fileService = jasmine.createSpyObj('fileService', ['downloadFile']);
|
|
||||||
bitstream = Object.assign(new Bitstream(), {
|
bitstream = Object.assign(new Bitstream(), {
|
||||||
uuid: 'bitstreamUuid',
|
uuid: 'bitstreamUuid',
|
||||||
|
_links: {
|
||||||
|
self: {href: 'obj-selflink'}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
function initTestbed() {
|
||||||
init();
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [FileDownloadLinkComponent],
|
declarations: [FileDownloadLinkComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: AuthService, useValue: authService },
|
{provide: AuthorizationDataService, useValue: authorizationService},
|
||||||
{ provide: FileService, useValue: fileService },
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(FileDownloadLinkComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
component.bitstream = bitstream;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('init', () => {
|
describe('init', () => {
|
||||||
|
|
||||||
describe('getBitstreamPath', () => {
|
describe('getBitstreamPath', () => {
|
||||||
it('should set the bitstreamPath based on the input bitstream', () => {
|
describe('when the user has download rights', () => {
|
||||||
expect(component.bitstreamPath).toEqual(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString());
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
init();
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FileDownloadLinkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.bitstream = bitstream;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should return the bitstreamPath based on the input bitstream', () => {
|
||||||
|
expect(component.bitstreamPath$).toBeObservable(cold('-a', {a: new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString()}));
|
||||||
|
expect(component.canDownload$).toBeObservable(cold('--a', {a: true}));
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should init the component', () => {
|
||||||
|
scheduler.flush();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const link = fixture.debugElement.query(By.css('a')).nativeElement;
|
||||||
|
expect(link.href).toContain(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString());
|
||||||
|
const lock = fixture.debugElement.query(By.css('.fa-lock'));
|
||||||
|
expect(lock).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the user has no download rights but has the right to request a copy', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
init();
|
||||||
|
(authorizationService.isAuthorized as jasmine.Spy).and.callFake((featureId, object) => {
|
||||||
|
if (featureId === FeatureID.CanDownload) {
|
||||||
|
return cold('-a', {a: false});
|
||||||
|
}
|
||||||
|
return cold('-a', {a: true});
|
||||||
|
});
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FileDownloadLinkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.bitstream = bitstream;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should return the bitstreamPath based on the input bitstream', () => {
|
||||||
|
expect(component.bitstreamPath$).toBeObservable(cold('-a', {a: new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'request-a-copy').toString()}));
|
||||||
|
expect(component.canDownload$).toBeObservable(cold('--a', {a: false}));
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should init the component', () => {
|
||||||
|
scheduler.flush();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const link = fixture.debugElement.query(By.css('a')).nativeElement;
|
||||||
|
expect(link.href).toContain(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'request-a-copy').toString());
|
||||||
|
const lock = fixture.debugElement.query(By.css('.fa-lock')).nativeElement;
|
||||||
|
expect(lock).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('when the user has no download rights and no request a copy rights', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
init();
|
||||||
|
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(cold('-a', {a: false}));
|
||||||
|
initTestbed();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FileDownloadLinkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.bitstream = bitstream;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should return the bitstreamPath based on the input bitstream', () => {
|
||||||
|
expect(component.bitstreamPath$).toBeObservable(cold('-a', {a: new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString()}));
|
||||||
|
expect(component.canDownload$).toBeObservable(cold('--a', {a: false}));
|
||||||
|
|
||||||
|
});
|
||||||
|
it('should init the component', () => {
|
||||||
|
scheduler.flush();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const link = fixture.debugElement.query(By.css('a')).nativeElement;
|
||||||
|
expect(link.href).toContain(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString());
|
||||||
|
const lock = fixture.debugElement.query(By.css('.fa-lock')).nativeElement;
|
||||||
|
expect(lock).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should init the component', () => {
|
|
||||||
const link = fixture.debugElement.query(By.css('a')).nativeElement;
|
|
||||||
expect(link.href).toContain(new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { Bitstream } from '../../core/shared/bitstream.model';
|
import { Bitstream } from '../../core/shared/bitstream.model';
|
||||||
import { getBitstreamDownloadRoute } from '../../app-routing-paths';
|
import { getBitstreamDownloadRoute, getBitstreamRequestACopyRoute } from '../../app-routing-paths';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
import { isNotEmpty } from '../empty.util';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { of as observableOf, combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-file-download-link',
|
selector: 'ds-file-download-link',
|
||||||
@@ -29,13 +34,34 @@ export class FileDownloadLinkComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() isBlank = false;
|
@Input() isBlank = false;
|
||||||
|
|
||||||
bitstreamPath: string;
|
@Input() enableRequestACopy = true;
|
||||||
|
|
||||||
ngOnInit() {
|
bitstreamPath$: Observable<string>;
|
||||||
this.bitstreamPath = this.getBitstreamPath();
|
|
||||||
|
canDownload$: Observable<boolean>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private authorizationService: AuthorizationDataService,
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBitstreamPath() {
|
ngOnInit() {
|
||||||
|
if (this.enableRequestACopy) {
|
||||||
|
this.canDownload$ = this.authorizationService.isAuthorized(FeatureID.CanDownload, isNotEmpty(this.bitstream) ? this.bitstream.self : undefined);
|
||||||
|
const canRequestACopy$ = this.authorizationService.isAuthorized(FeatureID.CanRequestACopy, isNotEmpty(this.bitstream) ? this.bitstream.self : undefined);
|
||||||
|
this.bitstreamPath$ = observableCombineLatest([this.canDownload$, canRequestACopy$]).pipe(
|
||||||
|
map(([canDownload, canRequestACopy]) => this.getBitstreamPath(canDownload, canRequestACopy))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.bitstreamPath$ = observableOf(getBitstreamDownloadRoute(this.bitstream));
|
||||||
|
this.canDownload$ = observableOf(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBitstreamPath(canDownload: boolean, canRequestACopy: boolean) {
|
||||||
|
if (!canDownload && canRequestACopy) {
|
||||||
|
return getBitstreamRequestACopyRoute(this.bitstream);
|
||||||
|
}
|
||||||
return getBitstreamDownloadRoute(this.bitstream);
|
return getBitstreamDownloadRoute(this.bitstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -233,6 +233,7 @@ import { OnClickMenuItemComponent } from './menu/menu-item/onclick-menu-item.com
|
|||||||
import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component';
|
import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component';
|
||||||
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
|
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
|
||||||
import { SearchNavbarComponent } from '../search-navbar/search-navbar.component';
|
import { SearchNavbarComponent } from '../search-navbar/search-navbar.component';
|
||||||
|
import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declaration needed to make sure all decorator functions are called in time
|
* Declaration needed to make sure all decorator functions are called in time
|
||||||
@@ -431,6 +432,7 @@ const COMPONENTS = [
|
|||||||
GroupSearchBoxComponent,
|
GroupSearchBoxComponent,
|
||||||
FileDownloadLinkComponent,
|
FileDownloadLinkComponent,
|
||||||
BitstreamDownloadPageComponent,
|
BitstreamDownloadPageComponent,
|
||||||
|
BitstreamRequestACopyPageComponent,
|
||||||
CollectionDropdownComponent,
|
CollectionDropdownComponent,
|
||||||
ExportMetadataSelectorComponent,
|
ExportMetadataSelectorComponent,
|
||||||
ConfirmationModalComponent,
|
ConfirmationModalComponent,
|
||||||
@@ -512,6 +514,7 @@ const ENTRY_COMPONENTS = [
|
|||||||
CollectionDropdownComponent,
|
CollectionDropdownComponent,
|
||||||
FileDownloadLinkComponent,
|
FileDownloadLinkComponent,
|
||||||
BitstreamDownloadPageComponent,
|
BitstreamDownloadPageComponent,
|
||||||
|
BitstreamRequestACopyPageComponent,
|
||||||
CurationFormComponent,
|
CurationFormComponent,
|
||||||
ExportMetadataSelectorComponent,
|
ExportMetadataSelectorComponent,
|
||||||
ConfirmationModalComponent,
|
ConfirmationModalComponent,
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="float-right w-15" [class.sticky-buttons]="!readMode">
|
<div class="float-right w-15" [class.sticky-buttons]="!readMode">
|
||||||
<ng-container *ngIf="readMode">
|
<ng-container *ngIf="readMode">
|
||||||
<ds-file-download-link [cssClasses]="'btn btn-link'" [isBlank]="true" [bitstream]="getBitstream()">
|
<ds-file-download-link [cssClasses]="'btn btn-link'" [isBlank]="true" [bitstream]="getBitstream()" [enableRequestACopy]="false">
|
||||||
<i class="fa fa-download fa-2x text-normal" aria-hidden="true"></i>
|
<i class="fa fa-download fa-2x text-normal" aria-hidden="true"></i>
|
||||||
</ds-file-download-link>
|
</ds-file-download-link>
|
||||||
<button class="btn btn-link"
|
<button class="btn btn-link"
|
||||||
|
@@ -588,6 +588,40 @@
|
|||||||
|
|
||||||
"bitstream.edit.title": "Edit bitstream",
|
"bitstream.edit.title": "Edit bitstream",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.alert.canDownload1": "You already have access to this file. If you want to download the file, click ",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.alert.canDownload2": "here",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.header": "Request a copy of the file",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.intro": "Enter the following information to request a copy",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.name.label": "Name *",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.name.error": "The name is required",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.email.label": "Your e-mail address *",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.email.hint": "This email address is used for sending the file.",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.email.error": "Please enter a valid email address.",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.allfiles.label": "Files",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.files-all-false.label": "Only the requested file",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.files-all-true.label": "All files (of this item) in restricted access",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.message.label": "Message",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.return": "Back",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.submit": "Request copy",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.submit.success": "The item request was submitted successfully.",
|
||||||
|
|
||||||
|
"bitstream-request-a-copy.submit.error": "Something went wrong with submitting the item request.",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"browse.comcol.by.author": "By Author",
|
"browse.comcol.by.author": "By Author",
|
||||||
|
Reference in New Issue
Block a user