mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Request-a-copy: Changes to support access expiry as delta/date storage - psql max
This commit is contained in:
@@ -125,7 +125,7 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
|
|||||||
* @param suggestOpenAccess Whether or not to suggest the item to become open access
|
* @param suggestOpenAccess Whether or not to suggest the item to become open access
|
||||||
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
|
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
|
||||||
*/
|
*/
|
||||||
grant(token: string, email: RequestCopyEmail, suggestOpenAccess = false, accessPeriod = 0): Observable<RemoteData<ItemRequest>> {
|
grant(token: string, email: RequestCopyEmail, suggestOpenAccess = false, accessPeriod: string = null): Observable<RemoteData<ItemRequest>> {
|
||||||
return this.process(token, email, true, suggestOpenAccess, accessPeriod);
|
return this.process(token, email, true, suggestOpenAccess, accessPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
|
|||||||
* @param suggestOpenAccess Whether or not to suggest the item to become open access
|
* @param suggestOpenAccess Whether or not to suggest the item to become open access
|
||||||
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
|
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
|
||||||
*/
|
*/
|
||||||
process(token: string, email: RequestCopyEmail, grant: boolean, suggestOpenAccess = false, accessPeriod = 0): Observable<RemoteData<ItemRequest>> {
|
process(token: string, email: RequestCopyEmail, grant: boolean, suggestOpenAccess = false, accessPeriod: string = null): Observable<RemoteData<ItemRequest>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
this.getItemRequestEndpointByToken(token).pipe(
|
this.getItemRequestEndpointByToken(token).pipe(
|
||||||
@@ -161,26 +161,6 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
|
|||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUID(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this, after discussion about implications and compare to bitstream data service byItemHandle
|
|
||||||
// Reviewers may ask that we instead just wrap the REST response in pagination even though we only expect one obj
|
|
||||||
/**
|
|
||||||
* Get a sanitized item request using the searchBy method and the access token sent to the original requester.
|
|
||||||
*
|
|
||||||
* @param accessToken access token contained in the secure link sent to a requester
|
|
||||||
*/
|
|
||||||
getSanitizedRequestByAccessTokenPaged(accessToken: string): Observable<RemoteData<PaginatedList<ItemRequest>>> {
|
|
||||||
// We only expect / want one result as access tokens are unique
|
|
||||||
const findListOptions = Object.assign({}, new FindListOptions(), {
|
|
||||||
elementsPerPage: 1,
|
|
||||||
currentPage: 1,
|
|
||||||
searchParams: [
|
|
||||||
new RequestParam('accessToken', accessToken),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
// Pipe the paginated searchBy results and return a single item request
|
|
||||||
return this.searchBy('byAccessToken', findListOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a sanitized item request using the searchBy method and the access token sent to the original requester.
|
* Get a sanitized item request using the searchBy method and the access token sent to the original requester.
|
||||||
*
|
*
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Display access periods if more than one was bound to input. The parent component (grant-request-copy)
|
<!-- Display access periods if more than one was bound to input. The parent component (grant-request-copy)
|
||||||
sends an empty list if the feature is not enabled or applicable to this request. -->
|
sends an empty list if the feature is not enabled or applicable to this request. -->
|
||||||
@if (hasValue(validAccessPeriods) && validAccessPeriods.length > 0) {
|
@if (hasValue(validAccessPeriods$ | async) && (validAccessPeriods$ | async).length > 0) {
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="accessPeriod">{{ 'grant-request-copy.access-period.header' | translate }}</label>
|
<label for="accessPeriod">{{ 'grant-request-copy.access-period.header' | translate }}</label>
|
||||||
<div ngbDropdown class="d-block">
|
<div ngbDropdown class="d-block">
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<!-- Access period dropdown -->
|
<!-- Access period dropdown -->
|
||||||
<div ngbDropdownMenu aria-labelledby="accessPeriod">
|
<div ngbDropdownMenu aria-labelledby="accessPeriod">
|
||||||
@for (accessPeriod of validAccessPeriods; track accessPeriod) {
|
@for (accessPeriod of (validAccessPeriods$ | async); track accessPeriod) {
|
||||||
<button
|
<button
|
||||||
ngbDropdownItem
|
ngbDropdownItem
|
||||||
class="list-element"
|
class="list-element"
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import 'altcha';
|
import 'altcha';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AsyncPipe,
|
||||||
Location,
|
Location,
|
||||||
NgClass,
|
NgClass,
|
||||||
} from '@angular/common';
|
} from '@angular/common';
|
||||||
@@ -8,12 +9,18 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
Observable,
|
||||||
|
Subject,
|
||||||
|
} from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
|
import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
@@ -24,16 +31,20 @@ import { RequestCopyEmail } from './request-copy-email.model';
|
|||||||
styleUrls: ['./email-request-copy.component.scss'],
|
styleUrls: ['./email-request-copy.component.scss'],
|
||||||
templateUrl: './email-request-copy.component.html',
|
templateUrl: './email-request-copy.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, NgbDropdownModule],
|
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, NgbDropdownModule, AsyncPipe],
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* A form component for an email to send back to the user requesting an item
|
* A form component for an email to send back to the user requesting an item
|
||||||
*/
|
*/
|
||||||
export class EmailRequestCopyComponent implements OnInit {
|
export class EmailRequestCopyComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Event emitter for sending the email
|
* Event emitter for sending the email
|
||||||
*/
|
*/
|
||||||
@Output() send: EventEmitter<RequestCopyEmail> = new EventEmitter<RequestCopyEmail>();
|
@Output() send: EventEmitter<RequestCopyEmail> = new EventEmitter<RequestCopyEmail>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected access period emmitter, sending the new period up to the parent component
|
||||||
|
*/
|
||||||
@Output() selectedAccessPeriod: EventEmitter<string> = new EventEmitter();
|
@Output() selectedAccessPeriod: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +60,7 @@ export class EmailRequestCopyComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* A list of valid access periods to render in a drop-down menu
|
* A list of valid access periods to render in a drop-down menu
|
||||||
*/
|
*/
|
||||||
@Input() validAccessPeriods: string [] = [];
|
@Input() validAccessPeriods$: Observable<string[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selected access period, e.g. +7DAYS, +12MONTHS, FOREVER. These will be
|
* The selected access period, e.g. +7DAYS, +12MONTHS, FOREVER. These will be
|
||||||
@@ -57,16 +68,37 @@ export class EmailRequestCopyComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
accessPeriod = 'FOREVER';
|
accessPeriod = 'FOREVER';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy subject for unsubscribing from observables
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
protected readonly hasValue = hasValue;
|
protected readonly hasValue = hasValue;
|
||||||
|
|
||||||
constructor(protected location: Location) {
|
constructor(protected location: Location) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise subscription to async valid access periods (from configuration service)
|
||||||
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// If access periods are present, set the default to the first in the array
|
this.validAccessPeriods$.pipe(
|
||||||
if (hasValue(this.validAccessPeriods) && this.validAccessPeriods.length > 0) {
|
takeUntil(this.destroy$),
|
||||||
this.selectAccessPeriod(this.validAccessPeriods[0]);
|
).subscribe((validAccessPeriods) => {
|
||||||
}
|
if (hasValue(validAccessPeriods) && validAccessPeriods.length > 0) {
|
||||||
|
this.selectAccessPeriod(validAccessPeriods[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up subscriptions and selectors
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.selectedAccessPeriod.complete();
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -4,6 +4,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
|
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
|
||||||
|
|
||||||
import { EmailRequestCopyComponent } from './email-request-copy.component';
|
import { EmailRequestCopyComponent } from './email-request-copy.component';
|
||||||
@@ -28,7 +29,7 @@ export class ThemedEmailRequestCopyComponent extends ThemedComponent<EmailReques
|
|||||||
/**
|
/**
|
||||||
* Event emitter for a selected / changed access period
|
* Event emitter for a selected / changed access period
|
||||||
*/
|
*/
|
||||||
@Output() selectedAccessPeriod: EventEmitter<number> = new EventEmitter();
|
@Output() selectedAccessPeriod: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The subject of the email
|
* The subject of the email
|
||||||
@@ -43,10 +44,10 @@ export class ThemedEmailRequestCopyComponent extends ThemedComponent<EmailReques
|
|||||||
/**
|
/**
|
||||||
* A list of valid access periods, if configured
|
* A list of valid access periods, if configured
|
||||||
*/
|
*/
|
||||||
@Input() validAccessPeriods: number[];
|
@Input() validAccessPeriods$: Observable<string[]>;
|
||||||
|
|
||||||
|
|
||||||
protected inAndOutputNames: (keyof EmailRequestCopyComponent & keyof this)[] = ['send', 'subject', 'message', 'validAccessPeriods', 'selectedAccessPeriod'];
|
protected inAndOutputNames: (keyof EmailRequestCopyComponent & keyof this)[] = ['send', 'subject', 'message', 'selectedAccessPeriod', 'validAccessPeriods$'];
|
||||||
|
|
||||||
protected getComponentName(): string {
|
protected getComponentName(): string {
|
||||||
return 'EmailRequestCopyComponent';
|
return 'EmailRequestCopyComponent';
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
<!-- Only send access periods for display if an access token was present -->
|
<!-- Only send access periods for display if an access token was present -->
|
||||||
<ds-email-request-copy [subject]="subject$ | async"
|
<ds-email-request-copy [subject]="subject$ | async"
|
||||||
[message]="message$ | async"
|
[message]="message$ | async"
|
||||||
[validAccessPeriods]="(hasValue(itemRequestRD.payload.accessToken) ? (validAccessPeriods$ | async) : [])"
|
|
||||||
(send)="grant($event)"
|
(send)="grant($event)"
|
||||||
(selectedAccessPeriod)="selectAccessPeriod($event)"
|
(selectedAccessPeriod)="selectAccessPeriod($event)"
|
||||||
|
[validAccessPeriods$]="validAccessPeriods$"
|
||||||
>
|
>
|
||||||
<p>{{ 'grant-deny-request-copy.email.permissions.info' | translate }}</p>
|
<p>{{ 'grant-deny-request-copy.email.permissions.info' | translate }}</p>
|
||||||
<form class="mb-3">
|
<form class="mb-3">
|
||||||
|
@@ -81,7 +81,7 @@ export class GrantRequestCopyComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The currently selected access period
|
* The currently selected access period
|
||||||
*/
|
*/
|
||||||
accessPeriod: any = 0;
|
accessPeriod: string = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will this email attach file(s) directly, or send a secure link with an access token to provide temporary access?
|
* Will this email attach file(s) directly, or send a secure link with an access token to provide temporary access?
|
||||||
@@ -113,6 +113,9 @@ export class GrantRequestCopyComponent implements OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the component - get the item request from route data an duse it to populate the form
|
||||||
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Get item request data via the router (async)
|
// Get item request data via the router (async)
|
||||||
this.itemRequestRD$ = this.route.data.pipe(
|
this.itemRequestRD$ = this.route.data.pipe(
|
||||||
@@ -157,7 +160,7 @@ export class GrantRequestCopyComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAccessPeriod(accessPeriod: number) {
|
selectAccessPeriod(accessPeriod: string) {
|
||||||
this.accessPeriod = accessPeriod;
|
this.accessPeriod = accessPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import { NgClass } from '@angular/common';
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
NgClass,
|
||||||
|
} from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@@ -13,7 +16,7 @@ import { BtnDisabledDirective } from '../../../../../app/shared/btn-disabled.dir
|
|||||||
// templateUrl: './email-request-copy.component.html',
|
// templateUrl: './email-request-copy.component.html',
|
||||||
templateUrl: './../../../../../app/request-copy/email-request-copy/email-request-copy.component.html',
|
templateUrl: './../../../../../app/request-copy/email-request-copy/email-request-copy.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective],
|
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, AsyncPipe],
|
||||||
})
|
})
|
||||||
export class EmailRequestCopyComponent
|
export class EmailRequestCopyComponent
|
||||||
extends BaseComponent {
|
extends BaseComponent {
|
||||||
|
Reference in New Issue
Block a user