diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index fa75b0b2f0..157ada622d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -183,6 +183,7 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu { path: REQUEST_COPY_MODULE_PATH, loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), + canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] }, { path: FORBIDDEN_PATH, diff --git a/src/app/core/data/item-request-data.service.ts b/src/app/core/data/item-request-data.service.ts index a0a7fe6d03..41ad19211a 100644 --- a/src/app/core/data/item-request-data.service.ts +++ b/src/app/core/data/item-request-data.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { filter, find, map } from 'rxjs/operators'; +import { distinctUntilChanged, 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 { getFirstCompletedRemoteData, sendRequest } from '../shared/operators'; import { RemoteData } from './remote-data'; -import { PostRequest } from './request.models'; +import { PostRequest, PutRequest } from './request.models'; import { RequestService } from './request.service'; import { ItemRequest } from '../shared/item-request.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; @@ -14,11 +14,13 @@ import { Store } from '@ngrx/store'; import { CoreState } from '../core.reducers'; import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { RequestCopyEmail } from '../../request-copy/email-request-copy/request-copy-email.model'; +import { HttpOptions } from '../dspace-rest/dspace-rest.service'; /** - * A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint + * A service responsible for fetching/sending data from/to the REST API on the itemrequests endpoint */ @Injectable( { @@ -46,12 +48,20 @@ export class ItemRequestDataService extends DataService { return this.halService.getEndpoint(this.linkPath); } - getFindItemRequestEndpoint(requestID: string): Observable { + /** + * Get the endpoint for an {@link ItemRequest} by their token + * @param token + */ + getItemRequestEndpointByToken(token: string): Observable { return this.halService.getEndpoint(this.linkPath).pipe( filter((href: string) => isNotEmpty(href)), - map((href: string) => `${href}/${requestID}`)); + map((href: string) => `${href}/${token}`)); } + /** + * Request a copy of an item + * @param itemRequest + */ requestACopy(itemRequest: ItemRequest): Observable> { const requestId = this.requestService.generateRequestId(); @@ -70,4 +80,52 @@ export class ItemRequestDataService extends DataService { ); } + /** + * Deny the request of an item + * @param token Token of the {@link ItemRequest} + * @param email Email to send back to the user requesting the item + */ + deny(token: string, email: RequestCopyEmail): Observable> { + return this.process(token, email, false); + } + + /** + * Grant the request of an item + * @param token Token of the {@link ItemRequest} + * @param email Email to send back to the user requesting the item + * @param suggestOpenAccess Whether or not to suggest the item to become open access + */ + grant(token: string, email: RequestCopyEmail, suggestOpenAccess = false): Observable> { + return this.process(token, email, true, suggestOpenAccess); + } + + /** + * Process the request of an item + * @param token Token of the {@link ItemRequest} + * @param email Email to send back to the user requesting the item + * @param grant Grant or deny the request (true = grant, false = deny) + * @param suggestOpenAccess Whether or not to suggest the item to become open access + */ + process(token: string, email: RequestCopyEmail, grant: boolean, suggestOpenAccess = false): Observable> { + const requestId = this.requestService.generateRequestId(); + + this.getItemRequestEndpointByToken(token).pipe( + distinctUntilChanged(), + map((endpointURL: string) => { + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/json'); + options.headers = headers; + return new PutRequest(requestId, endpointURL, JSON.stringify({ + acceptRequest: grant, + responseMessage: email.message, + subject: email.subject, + suggestOpenAccess, + }), options); + }), + sendRequest(this.requestService)).subscribe(); + + return this.rdbService.buildFromRequestUUID(requestId); + } + } diff --git a/src/app/request-copy/deny-request-copy/deny-request-copy.component.html b/src/app/request-copy/deny-request-copy/deny-request-copy.component.html index 226fac1f67..b00bc079dd 100644 --- a/src/app/request-copy/deny-request-copy/deny-request-copy.component.html +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.html @@ -3,7 +3,7 @@

{{'deny-request-copy.intro' | translate}}

- +
diff --git a/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts b/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts index 4ffeb61605..0795cf5919 100644 --- a/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts @@ -1,27 +1,57 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { ItemRequest } from '../../core/shared/item-request.model'; import { Observable } from 'rxjs/internal/Observable'; import { - getFirstCompletedRemoteData, + getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators'; import { RemoteData } from '../../core/data/remote-data'; import { AuthService } from '../../core/auth/auth.service'; +import { TranslateService } from '@ngx-translate/core'; +import { combineLatest as observableCombineLatest } from 'rxjs'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { EPerson } from '../../core/eperson/models/eperson.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { Item } from '../../core/shared/item.model'; +import { isNotEmpty } from '../../shared/empty.util'; +import { ItemRequestDataService } from '../../core/data/item-request-data.service'; +import { RequestCopyEmail } from '../email-request-copy/request-copy-email.model'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; @Component({ selector: 'ds-deny-request-copy', styleUrls: ['./deny-request-copy.component.scss'], templateUrl: './deny-request-copy.component.html' }) +/** + * Component for denying an item request + */ export class DenyRequestCopyComponent implements OnInit { + /** + * The item request to deny + */ itemRequestRD$: Observable>; + /** + * The default subject of the message to send to the user requesting the item + */ + subject$: Observable; + /** + * The default contents of the message to send to the user requesting the item + */ + message$: Observable; + constructor( private router: Router, private route: ActivatedRoute, private authService: AuthService, + private translateService: TranslateService, + private itemDataService: ItemDataService, + private nameService: DSONameService, + private itemRequestService: ItemRequestDataService, + private notificationsService: NotificationsService, ) { } @@ -32,6 +62,51 @@ export class DenyRequestCopyComponent implements OnInit { getFirstCompletedRemoteData(), redirectOn4xx(this.router, this.authService), ); + + const msgParams$ = observableCombineLatest( + this.itemRequestRD$.pipe(getFirstSucceededRemoteDataPayload()), + this.authService.getAuthenticatedUserFromStore(), + ).pipe( + switchMap(([itemRequest, user]: [ItemRequest, EPerson]) => { + return this.itemDataService.findById(itemRequest.itemId).pipe( + getFirstSucceededRemoteDataPayload(), + map((item: Item) => { + const uri = item.firstMetadataValue('dc.identifier.uri'); + return Object.assign({ + recipientName: itemRequest.requestName, + itemUrl: isNotEmpty(uri) ? uri : item.handle, + itemName: this.nameService.getName(item), + authorName: user.name, + authorEmail: user.email, + }); + }), + ); + }), + ); + + this.subject$ = this.translateService.get('deny-request-copy.email.subject'); + this.message$ = msgParams$.pipe( + switchMap((params) => this.translateService.get('deny-request-copy.email.message', params)), + ); + } + + /** + * Deny the item request + * @param email Subject and contents of the message to send back to the user requesting the item + */ + deny(email: RequestCopyEmail) { + this.itemRequestRD$.pipe( + getFirstSucceededRemoteDataPayload(), + switchMap((itemRequest: ItemRequest) => this.itemRequestService.deny(itemRequest.token, email)), + getFirstCompletedRemoteData() + ).subscribe((rd) => { + if (rd.hasSucceeded) { + this.notificationsService.success(this.translateService.get('deny-request-copy.success')); + this.router.navigateByUrl('/'); + } else { + this.notificationsService.error(this.translateService.get('deny-request-copy.error'), rd.errorMessage); + } + }); } } diff --git a/src/app/request-copy/email-request-copy/email-request-copy.component.html b/src/app/request-copy/email-request-copy/email-request-copy.component.html index b4c7b65aba..6f5bf9189b 100644 --- a/src/app/request-copy/email-request-copy/email-request-copy.component.html +++ b/src/app/request-copy/email-request-copy/email-request-copy.component.html @@ -8,17 +8,18 @@
- +
{{ 'grant-deny-request-copy.email.message.empty' | translate }}
+