Skip to content

Commit 1fff3b5

Browse files
committed
Request-a-copy: Changes to support access expiry as delta/date storage - psql max
1 parent 57b618c commit 1fff3b5

7 files changed

Lines changed: 58 additions & 39 deletions

File tree

src/app/core/data/item-request-data.service.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
125125
* @param suggestOpenAccess Whether or not to suggest the item to become open access
126126
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
127127
*/
128-
grant(token: string, email: RequestCopyEmail, suggestOpenAccess = false, accessPeriod = 0): Observable<RemoteData<ItemRequest>> {
128+
grant(token: string, email: RequestCopyEmail, suggestOpenAccess = false, accessPeriod: string = null): Observable<RemoteData<ItemRequest>> {
129129
return this.process(token, email, true, suggestOpenAccess, accessPeriod);
130130
}
131131

@@ -137,7 +137,7 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
137137
* @param suggestOpenAccess Whether or not to suggest the item to become open access
138138
* @param accessPeriod How long in seconds to grant access, from the decision date (only applies to links, not attachments)
139139
*/
140-
process(token: string, email: RequestCopyEmail, grant: boolean, suggestOpenAccess = false, accessPeriod = 0): Observable<RemoteData<ItemRequest>> {
140+
process(token: string, email: RequestCopyEmail, grant: boolean, suggestOpenAccess = false, accessPeriod: string = null): Observable<RemoteData<ItemRequest>> {
141141
const requestId = this.requestService.generateRequestId();
142142

143143
this.getItemRequestEndpointByToken(token).pipe(
@@ -161,26 +161,6 @@ export class ItemRequestDataService extends IdentifiableDataService<ItemRequest>
161161
return this.rdbService.buildFromRequestUUID(requestId);
162162
}
163163

164-
// TODO: Remove this, after discussion about implications and compare to bitstream data service byItemHandle
165-
// Reviewers may ask that we instead just wrap the REST response in pagination even though we only expect one obj
166-
/**
167-
* Get a sanitized item request using the searchBy method and the access token sent to the original requester.
168-
*
169-
* @param accessToken access token contained in the secure link sent to a requester
170-
*/
171-
getSanitizedRequestByAccessTokenPaged(accessToken: string): Observable<RemoteData<PaginatedList<ItemRequest>>> {
172-
// We only expect / want one result as access tokens are unique
173-
const findListOptions = Object.assign({}, new FindListOptions(), {
174-
elementsPerPage: 1,
175-
currentPage: 1,
176-
searchParams: [
177-
new RequestParam('accessToken', accessToken),
178-
],
179-
});
180-
// Pipe the paginated searchBy results and return a single item request
181-
return this.searchBy('byAccessToken', findListOptions);
182-
}
183-
184164
/**
185165
* Get a sanitized item request using the searchBy method and the access token sent to the original requester.
186166
*

src/app/request-copy/email-request-copy/email-request-copy.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</div>
1616
<!-- Display access periods if more than one was bound to input. The parent component (grant-request-copy)
1717
sends an empty list if the feature is not enabled or applicable to this request. -->
18-
@if (hasValue(validAccessPeriods) && validAccessPeriods.length > 0) {
18+
@if (hasValue(validAccessPeriods$ | async) && (validAccessPeriods$ | async).length > 0) {
1919
<div class="form-group">
2020
<label for="accessPeriod">{{ 'grant-request-copy.access-period.header' | translate }}</label>
2121
<div ngbDropdown class="d-block">
@@ -28,7 +28,7 @@
2828
</button>
2929
<!-- Access period dropdown -->
3030
<div ngbDropdownMenu aria-labelledby="accessPeriod">
31-
@for (accessPeriod of validAccessPeriods; track accessPeriod) {
31+
@for (accessPeriod of (validAccessPeriods$ | async); track accessPeriod) {
3232
<button
3333
ngbDropdownItem
3434
class="list-element"

src/app/request-copy/email-request-copy/email-request-copy.component.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import 'altcha';
22

33
import {
4+
AsyncPipe,
45
Location,
56
NgClass,
67
} from '@angular/common';
78
import {
89
Component,
910
EventEmitter,
1011
Input,
12+
OnDestroy,
1113
OnInit,
1214
Output,
1315
} from '@angular/core';
1416
import { FormsModule } from '@angular/forms';
1517
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
1618
import { TranslateModule } from '@ngx-translate/core';
19+
import {
20+
Observable,
21+
Subject,
22+
} from 'rxjs';
23+
import { takeUntil } from 'rxjs/operators';
1724

1825
import { BtnDisabledDirective } from '../../shared/btn-disabled.directive';
1926
import { hasValue } from '../../shared/empty.util';
@@ -24,16 +31,20 @@ import { RequestCopyEmail } from './request-copy-email.model';
2431
styleUrls: ['./email-request-copy.component.scss'],
2532
templateUrl: './email-request-copy.component.html',
2633
standalone: true,
27-
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, NgbDropdownModule],
34+
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, NgbDropdownModule, AsyncPipe],
2835
})
2936
/**
3037
* A form component for an email to send back to the user requesting an item
3138
*/
32-
export class EmailRequestCopyComponent implements OnInit {
39+
export class EmailRequestCopyComponent implements OnInit, OnDestroy {
3340
/**
3441
* Event emitter for sending the email
3542
*/
3643
@Output() send: EventEmitter<RequestCopyEmail> = new EventEmitter<RequestCopyEmail>();
44+
45+
/**
46+
* Selected access period emmitter, sending the new period up to the parent component
47+
*/
3748
@Output() selectedAccessPeriod: EventEmitter<string> = new EventEmitter();
3849

3950
/**
@@ -49,24 +60,45 @@ export class EmailRequestCopyComponent implements OnInit {
4960
/**
5061
* A list of valid access periods to render in a drop-down menu
5162
*/
52-
@Input() validAccessPeriods: string [] = [];
63+
@Input() validAccessPeriods$: Observable<string[]>;
5364

5465
/**
5566
* The selected access period, e.g. +7DAYS, +12MONTHS, FOREVER. These will be
5667
* calculated as a timestamp to store as the access expiry date for the requested item
5768
*/
5869
accessPeriod = 'FOREVER';
5970

71+
/**
72+
* Destroy subject for unsubscribing from observables
73+
* @private
74+
*/
75+
private destroy$ = new Subject<void>();
76+
6077
protected readonly hasValue = hasValue;
6178

6279
constructor(protected location: Location) {
6380
}
6481

82+
/**
83+
* Initialise subscription to async valid access periods (from configuration service)
84+
*/
6585
ngOnInit(): void {
66-
// If access periods are present, set the default to the first in the array
67-
if (hasValue(this.validAccessPeriods) && this.validAccessPeriods.length > 0) {
68-
this.selectAccessPeriod(this.validAccessPeriods[0]);
69-
}
86+
this.validAccessPeriods$.pipe(
87+
takeUntil(this.destroy$),
88+
).subscribe((validAccessPeriods) => {
89+
if (hasValue(validAccessPeriods) && validAccessPeriods.length > 0) {
90+
this.selectAccessPeriod(validAccessPeriods[0]);
91+
}
92+
});
93+
}
94+
95+
/**
96+
* Clean up subscriptions and selectors
97+
*/
98+
ngOnDestroy(): void {
99+
this.selectedAccessPeriod.complete();
100+
this.destroy$.next();
101+
this.destroy$.complete();
70102
}
71103

72104
/**

src/app/request-copy/email-request-copy/themed-email-request-copy.component.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Input,
55
Output,
66
} from '@angular/core';
7+
import { Observable } from 'rxjs';
78
import { ThemedComponent } from 'src/app/shared/theme-support/themed.component';
89

910
import { EmailRequestCopyComponent } from './email-request-copy.component';
@@ -28,7 +29,7 @@ export class ThemedEmailRequestCopyComponent extends ThemedComponent<EmailReques
2829
/**
2930
* Event emitter for a selected / changed access period
3031
*/
31-
@Output() selectedAccessPeriod: EventEmitter<number> = new EventEmitter();
32+
@Output() selectedAccessPeriod: EventEmitter<string> = new EventEmitter();
3233

3334
/**
3435
* The subject of the email
@@ -43,10 +44,10 @@ export class ThemedEmailRequestCopyComponent extends ThemedComponent<EmailReques
4344
/**
4445
* A list of valid access periods, if configured
4546
*/
46-
@Input() validAccessPeriods: number[];
47+
@Input() validAccessPeriods$: Observable<string[]>;
4748

4849

49-
protected inAndOutputNames: (keyof EmailRequestCopyComponent & keyof this)[] = ['send', 'subject', 'message', 'validAccessPeriods', 'selectedAccessPeriod'];
50+
protected inAndOutputNames: (keyof EmailRequestCopyComponent & keyof this)[] = ['send', 'subject', 'message', 'selectedAccessPeriod', 'validAccessPeriods$'];
5051

5152
protected getComponentName(): string {
5253
return 'EmailRequestCopyComponent';

src/app/request-copy/grant-request-copy/grant-request-copy.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ <h3 class="mb-4">{{ 'grant-request-copy.header' | translate }}</h3>
2121
<!-- Only send access periods for display if an access token was present -->
2222
<ds-email-request-copy [subject]="subject$ | async"
2323
[message]="message$ | async"
24-
[validAccessPeriods]="(hasValue(itemRequestRD.payload.accessToken) ? (validAccessPeriods$ | async) : [])"
2524
(send)="grant($event)"
2625
(selectedAccessPeriod)="selectAccessPeriod($event)"
26+
[validAccessPeriods$]="validAccessPeriods$"
2727
>
2828
<p>{{ 'grant-deny-request-copy.email.permissions.info' | translate }}</p>
2929
<form class="mb-3">

src/app/request-copy/grant-request-copy/grant-request-copy.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class GrantRequestCopyComponent implements OnInit {
8181
/**
8282
* The currently selected access period
8383
*/
84-
accessPeriod: any = 0;
84+
accessPeriod: string = null;
8585

8686
/**
8787
* 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 {
113113

114114
}
115115

116+
/**
117+
* Initialize the component - get the item request from route data an duse it to populate the form
118+
*/
116119
ngOnInit(): void {
117120
// Get item request data via the router (async)
118121
this.itemRequestRD$ = this.route.data.pipe(
@@ -157,7 +160,7 @@ export class GrantRequestCopyComponent implements OnInit {
157160
});
158161
}
159162

160-
selectAccessPeriod(accessPeriod: number) {
163+
selectAccessPeriod(accessPeriod: string) {
161164
this.accessPeriod = accessPeriod;
162165
}
163166

src/themes/custom/app/request-copy/email-request-copy/email-request-copy.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { NgClass } from '@angular/common';
1+
import {
2+
AsyncPipe,
3+
NgClass,
4+
} from '@angular/common';
25
import { Component } from '@angular/core';
36
import { FormsModule } from '@angular/forms';
47
import { TranslateModule } from '@ngx-translate/core';
@@ -13,7 +16,7 @@ import { BtnDisabledDirective } from '../../../../../app/shared/btn-disabled.dir
1316
// templateUrl: './email-request-copy.component.html',
1417
templateUrl: './../../../../../app/request-copy/email-request-copy/email-request-copy.component.html',
1518
standalone: true,
16-
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective],
19+
imports: [FormsModule, NgClass, TranslateModule, BtnDisabledDirective, AsyncPipe],
1720
})
1821
export class EmailRequestCopyComponent
1922
extends BaseComponent {

0 commit comments

Comments
 (0)