83635: Intermediate commit

This commit is contained in:
Kristof De Langhe
2021-10-04 17:53:49 +02:00
committed by Art Lowel
parent 4feccb9989
commit 11bf10cbde
18 changed files with 279 additions and 47 deletions

View File

@@ -94,3 +94,8 @@ export const ACCESS_CONTROL_MODULE_PATH = 'access-control';
export function getAccessControlModuleRoute() { export function getAccessControlModuleRoute() {
return `/${ACCESS_CONTROL_MODULE_PATH}`; return `/${ACCESS_CONTROL_MODULE_PATH}`;
} }
export const REQUEST_COPY_MODULE_PATH = 'request-a-copy';
export function getRequestCopyModulePath() {
return `/${REQUEST_COPY_MODULE_PATH}`;
}

View File

@@ -14,7 +14,7 @@ import {
PROFILE_MODULE_PATH, PROFILE_MODULE_PATH,
REGISTER_PATH, REGISTER_PATH,
WORKFLOW_ITEM_MODULE_PATH, WORKFLOW_ITEM_MODULE_PATH,
LEGACY_BITSTREAM_MODULE_PATH, LEGACY_BITSTREAM_MODULE_PATH, REQUEST_COPY_MODULE_PATH,
} from './app-routing-paths'; } from './app-routing-paths';
import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths'; import { COLLECTION_MODULE_PATH } from './collection-page/collection-page-routing-paths';
import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from './community-page/community-page-routing-paths';
@@ -180,6 +180,10 @@ import { GroupAdministratorGuard } from './core/data/feature-authorization/featu
path: INFO_MODULE_PATH, path: INFO_MODULE_PATH,
loadChildren: () => import('./info/info.module').then((m) => m.InfoModule), loadChildren: () => import('./info/info.module').then((m) => m.InfoModule),
}, },
{
path: REQUEST_COPY_MODULE_PATH,
loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule),
},
{ {
path: FORBIDDEN_PATH, path: FORBIDDEN_PATH,
component: ThemedForbiddenComponent component: ThemedForbiddenComponent

View File

@@ -9,6 +9,13 @@ import { PostRequest } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { ItemRequest } from '../shared/item-request.model'; import { ItemRequest } from '../shared/item-request.model';
import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { DataService } from './data.service';
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 { DefaultChangeAnalyzer } from './default-change-analyzer.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 bitstreamformats endpoint
@@ -18,14 +25,21 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util';
providedIn: 'root', providedIn: 'root',
} }
) )
export class ItemRequestDataService { export class ItemRequestDataService extends DataService<ItemRequest> {
protected linkPath = 'itemrequests'; protected linkPath = 'itemrequests';
constructor( constructor(
protected requestService: RequestService, protected requestService: RequestService,
protected rdbService: RemoteDataBuildService, protected rdbService: RemoteDataBuildService,
protected halService: HALEndpointService) { protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<ItemRequest>,
) {
super();
} }
getItemRequestEndpoint(): Observable<string> { getItemRequestEndpoint(): Observable<string> {

View File

@@ -1,14 +1,16 @@
import { autoserialize } from 'cerialize'; import { autoserialize, deserialize } from 'cerialize';
import { typedObject } from '../cache/builders/build-decorators'; import { typedObject } from '../cache/builders/build-decorators';
import { excludeFromEquals } from '../utilities/equals.decorators'; import { excludeFromEquals } from '../utilities/equals.decorators';
import { ResourceType } from './resource-type'; import { ResourceType } from './resource-type';
import { ITEM_REQUEST } from './item-request.resource-type'; import { ITEM_REQUEST } from './item-request.resource-type';
import { CacheableObject } from '../cache/object-cache.reducer';
import { HALLink } from './hal-link.model';
/** /**
* Model class for a Configuration Property * Model class for a Configuration Property
*/ */
@typedObject @typedObject
export class ItemRequest { export class ItemRequest implements CacheableObject {
static type = ITEM_REQUEST; static type = ITEM_REQUEST;
/** /**
@@ -75,5 +77,14 @@ export class ItemRequest {
@autoserialize @autoserialize
bitstreamId: string; bitstreamId: string;
/**
* The {@link HALLink}s for this ItemRequest
*/
@deserialize
_links: {
self: HALLink;
item: HALLink;
bitstream: HALLink;
};
} }

View File

@@ -0,0 +1,9 @@
<div class="container" *ngVar="(itemRequestRD$ | async) as itemRequestRD">
<h3 class="mb-4">{{'deny-request-copy.header' | translate}}</h3>
<div *ngIf="itemRequestRD && itemRequestRD.hasSucceeded">
<p>{{'deny-request-copy.intro' | translate}}</p>
<ds-email-request-copy></ds-email-request-copy>
</div>
<ds-loading *ngIf="!itemRequestRD || itemRequestRD?.isLoading"></ds-loading>
</div>

View File

@@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { map } from 'rxjs/operators';
import { ItemRequest } from '../../core/shared/item-request.model';
import { Observable } from 'rxjs/internal/Observable';
import {
getFirstCompletedRemoteData,
redirectOn4xx
} from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
import { AuthService } from '../../core/auth/auth.service';
@Component({
selector: 'ds-deny-request-copy',
styleUrls: ['./deny-request-copy.component.scss'],
templateUrl: './deny-request-copy.component.html'
})
export class DenyRequestCopyComponent implements OnInit {
itemRequestRD$: Observable<RemoteData<ItemRequest>>;
constructor(
private router: Router,
private route: ActivatedRoute,
private authService: AuthService,
) {
}
ngOnInit(): void {
this.itemRequestRD$ = this.route.data.pipe(
map((data) => data.request as RemoteData<ItemRequest>),
getFirstCompletedRemoteData(),
redirectOn4xx(this.router, this.authService),
);
}
}

View File

@@ -0,0 +1,29 @@
<form>
<div class="form-group">
<label for="subject">{{ 'grant-deny-request-copy.email.subject' | translate }}</label>
<input type="text" class="form-control" id="subject" [ngClass]="{'is-invalid': !subject || subject.length === 0}" [(ngModel)]="subject" name="subject">
<div *ngIf="!subject || subject.length === 0" class="invalid-feedback">
{{ 'grant-deny-request-copy.email.subject.empty' | translate }}
</div>
</div>
<div class="form-group">
<label for="message">{{ 'grant-deny-request-copy.email.message' | translate }}</label>
<textarea class="form-control" id="message" rows="3" [ngClass]="{'is-invalid': !message || message.length === 0}" [(ngModel)]="message" name="message"></textarea>
<div *ngIf="!message || message.length === 0" class="invalid-feedback">
{{ 'grant-deny-request-copy.email.message.empty' | translate }}
</div>
</div>
<div class="d-flex">
<button (click)="submit()"
[disabled]="!message || message.length === 0 || !subject || subject.length === 0"
class="btn btn-primary mr-auto"
title="{{'grant-deny-request-copy.email.send' | translate }}">
{{'grant-deny-request-copy.email.send' | translate }}
</button>
<button (click)="return()"
class="btn btn-outline-secondary"
title="{{'grant-deny-request-copy.email.back' | translate }}">
{{'grant-deny-request-copy.email.back' | translate }}
</button>
</div>
</form>

View File

@@ -0,0 +1,26 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { RequestCopyEmail } from './request-copy-email.model';
import { Location } from '@angular/common';
@Component({
selector: 'ds-email-request-copy',
styleUrls: ['./email-request-copy.component.scss'],
templateUrl: './email-request-copy.component.html'
})
export class EmailRequestCopyComponent {
@Output() send: EventEmitter<RequestCopyEmail> = new EventEmitter<RequestCopyEmail>();
@Input() subject: string;
@Input() message: string;
constructor(protected location: Location) {
}
submit() {
this.send.emit(new RequestCopyEmail(this.subject, this.message));
}
return() {
this.location.back();
}
}

View File

@@ -0,0 +1,5 @@
export class RequestCopyEmail {
constructor(public subject: string,
public message: string) {
}
}

View File

@@ -1,20 +1,22 @@
<div class="container"> <div class="container" *ngVar="(itemRequestRD$ | async) as itemRequestRD">
<h3 class="mb-4">{{'grant-deny-request-copy.header' | translate}}</h3> <h3 class="mb-4">{{'grant-deny-request-copy.header' | translate}}</h3>
<p>{{'grant-deny-request-copy.intro' | translate}}</p> <div *ngIf="itemRequestRD && itemRequestRD.hasSucceeded">
<p>{{'grant-deny-request-copy.intro1' | translate:{ name: (itemName$ | async) } }}</p>
<p>{{'grant-deny-request-copy.intro2' | translate}}</p>
<div class="btn-group "> <div class="btn-group ">
<a [routerLink]="'/grant'" <a [routerLink]="grantRoute$ | async"
class="btn btn-outline-danger" class="btn btn-outline-primary"
title="{{'grant-deny-request-copy.grant' | translate }}"> title="{{'grant-deny-request-copy.grant' | translate }}">
{{'grant-deny-request-copy.grant' | translate }} {{'grant-deny-request-copy.grant' | translate }}
</a> </a>
<a [routerLink]="'/deny'" <a [routerLink]="denyRoute$ | async"
class="btn btn-outline-primary" class="btn btn-outline-danger"
title="{{'grant-deny-request-copy.deny' | translate }}"> title="{{'grant-deny-request-copy.deny' | translate }}">
{{'grant-deny-request-copy.deny' | translate }} {{'grant-deny-request-copy.deny' | translate }}
</a> </a>
</div> </div>
</div>
<ds-loading *ngIf="!itemRequestRD || itemRequestRD?.isLoading"></ds-loading>
</div> </div>

View File

@@ -7,13 +7,20 @@ import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service'; import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service';
import { map } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { Registration } from '../../core/shared/registration.model';
import { ItemRequest } from '../../core/shared/item-request.model'; import { ItemRequest } from '../../core/shared/item-request.model';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { getFirstCompletedRemoteData, redirectOn4xx } from '../../core/shared/operators'; import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
redirectOn4xx
} from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { getRequestCopyDenyRoute, getRequestCopyGrantRoute } from '../request-copy-routing-paths';
import { Item } from '../../core/shared/item.model';
import { ItemDataService } from '../../core/data/item-data.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-grant-deny-request-copy', selector: 'ds-grant-deny-request-copy',
@@ -21,30 +28,46 @@ import { AuthService } from '../../core/auth/auth.service';
templateUrl: './grant-deny-request-copy.component.html' templateUrl: './grant-deny-request-copy.component.html'
}) })
export class GrantDenyRequestCopyComponent implements OnInit { export class GrantDenyRequestCopyComponent implements OnInit {
private itemRequest$: Observable<RemoteData<ItemRequest>>; itemRequestRD$: Observable<RemoteData<ItemRequest>>;
itemRD$: Observable<RemoteData<Item>>;
itemName$: Observable<string>;
denyRoute$: Observable<string>;
grantRoute$: Observable<string>;
constructor( constructor(
private translateService: TranslateService,
private ePersonDataService: EPersonDataService,
private store: Store<CoreState>,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private formBuilder: FormBuilder, private authService: AuthService,
private notificationsService: NotificationsService, private itemDataService: ItemDataService,
private endUserAgreementService: EndUserAgreementService, private nameService: DSONameService,
private authService: AuthService
) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
this.itemRequest$ = this.route.data.pipe( this.itemRequestRD$ = this.route.data.pipe(
map((data) => data.request as RemoteData<ItemRequest>), map((data) => data.request as RemoteData<ItemRequest>),
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
redirectOn4xx(this.router, this.authService) redirectOn4xx(this.router, this.authService),
);
this.itemRD$ = this.itemRequestRD$.pipe(
getFirstSucceededRemoteDataPayload(),
switchMap((itemRequest: ItemRequest) => this.itemDataService.findById(itemRequest.itemId)),
);
this.itemName$ = this.itemRD$.pipe(
getFirstSucceededRemoteDataPayload(),
map((item) => this.nameService.getName(item)),
); );
this.denyRoute$ = this.itemRequestRD$.pipe(
getFirstSucceededRemoteDataPayload(),
map((itemRequest: ItemRequest) => getRequestCopyDenyRoute(itemRequest.token))
);
this.grantRoute$ = this.itemRequestRD$.pipe(
getFirstSucceededRemoteDataPayload(),
map((itemRequest: ItemRequest) => getRequestCopyGrantRoute(itemRequest.token))
);
} }
} }

View File

@@ -0,0 +1,18 @@
import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getRequestCopyModulePath } from '../app-routing-paths';
export function getRequestCopyRoute(token: string) {
return new URLCombiner(getRequestCopyModulePath(), token).toString();
}
export const REQUEST_COPY_DENY_PATH = 'deny';
export function getRequestCopyDenyRoute(token: string) {
return new URLCombiner(getRequestCopyRoute(token), REQUEST_COPY_DENY_PATH).toString();
}
export const REQUEST_COPY_GRANT_PATH = 'grant';
export function getRequestCopyGrantRoute(token: string) {
return new URLCombiner(getRequestCopyRoute(token), REQUEST_COPY_GRANT_PATH).toString();
}

View File

@@ -1,17 +1,28 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { EndUserAgreementCookieGuard } from '../core/end-user-agreement/end-user-agreement-cookie.guard';
import { RequestCopyResolver } from './request-copy.resolver'; import { RequestCopyResolver } from './request-copy.resolver';
import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-deny-request-copy.component'; import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-deny-request-copy.component';
import { REQUEST_COPY_DENY_PATH, REQUEST_COPY_GRANT_PATH } from './request-copy-routing-paths';
import { DenyRequestCopyComponent } from './deny-request-copy/deny-request-copy.component';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forChild([ RouterModule.forChild([
{ {
path: ':token', path: ':token',
resolve: {
request: RequestCopyResolver
},
children: [
{
path: '',
component: GrantDenyRequestCopyComponent, component: GrantDenyRequestCopyComponent,
resolve: {request: RequestCopyResolver}, },
canActivate: [EndUserAgreementCookieGuard] {
path: REQUEST_COPY_DENY_PATH,
component: DenyRequestCopyComponent,
}
]
} }
]) ])
], ],
@@ -20,8 +31,5 @@ import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-d
GrantDenyRequestCopyComponent GrantDenyRequestCopyComponent
] ]
}) })
/** export class RequestCopyRoutingModule {
* Module related to the navigation to components used to register a new user
*/
export class RegisterPageRoutingModule {
} }

View File

@@ -2,14 +2,20 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from '../shared/shared.module';
import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-deny-request-copy.component'; import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-deny-request-copy.component';
import { RequestCopyRoutingModule } from './request-copy-routing.module';
import { DenyRequestCopyComponent } from './deny-request-copy/deny-request-copy.component';
import { EmailRequestCopyComponent } from './email-request-copy/email-request-copy.component';
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
SharedModule, SharedModule,
RequestCopyRoutingModule
], ],
declarations: [ declarations: [
GrantDenyRequestCopyComponent GrantDenyRequestCopyComponent,
DenyRequestCopyComponent,
EmailRequestCopyComponent,
], ],
providers: [] providers: []
}) })

View File

@@ -3,7 +3,10 @@ import { RemoteData } from '../core/data/remote-data';
import { ItemRequest } from '../core/shared/item-request.model'; import { ItemRequest } from '../core/shared/item-request.model';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { ItemRequestDataService } from '../core/data/item-request-data.service'; import { ItemRequestDataService } from '../core/data/item-request-data.service';
import { Injectable } from '@angular/core';
import { getFirstCompletedRemoteData } from '../core/shared/operators';
@Injectable()
export class RequestCopyResolver implements Resolve<RemoteData<ItemRequest>> { export class RequestCopyResolver implements Resolve<RemoteData<ItemRequest>> {
constructor( constructor(
@@ -12,8 +15,9 @@ export class RequestCopyResolver implements Resolve<RemoteData<ItemRequest>> {
} }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<ItemRequest>> | Promise<RemoteData<ItemRequest>> | RemoteData<ItemRequest> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<ItemRequest>> | Promise<RemoteData<ItemRequest>> | RemoteData<ItemRequest> {
// TODO add method after knowing whether they will change the rest object to be compatible with normal dataservice. return this.itemRequestDataService.findById(route.params.token).pipe(
return undefined; getFirstCompletedRemoteData(),
);
} }
} }

View File

@@ -1206,6 +1206,12 @@
"deny-request-copy.header": "Deny document copy request",
"deny-request-copy.intro": "This message will be sent to the applicant of the request",
"dso.name.untitled": "Untitled", "dso.name.untitled": "Untitled",
@@ -1430,6 +1436,31 @@
"form.repeatable.sort.tip": "Drop the item in the new position", "form.repeatable.sort.tip": "Drop the item in the new position",
"grant-deny-request-copy.deny": "Don't send copy",
"grant-deny-request-copy.email.back": "Back",
"grant-deny-request-copy.email.message": "Message",
"grant-deny-request-copy.email.message.empty": "Please enter a message",
"grant-deny-request-copy.email.send": "Send",
"grant-deny-request-copy.email.subject": "Subject",
"grant-deny-request-copy.email.subject.empty": "Please enter a subject",
"grant-deny-request-copy.grant": "Send copy",
"grant-deny-request-copy.header": "Document copy request",
"grant-deny-request-copy.intro1": "IF YOU ARE THE AUTHOR (OR AN AUTHOR) OF DOCUMENT \"{{ name }}\" use the buttons to answer the user's request.",
"grant-deny-request-copy.intro2": "This repository will propose an appropriate model reply, which you may edit.",
"home.description": "", "home.description": "",
"home.breadcrumbs": "Home", "home.breadcrumbs": "Home",