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() {
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,
REGISTER_PATH,
WORKFLOW_ITEM_MODULE_PATH,
LEGACY_BITSTREAM_MODULE_PATH,
LEGACY_BITSTREAM_MODULE_PATH, REQUEST_COPY_MODULE_PATH,
} from './app-routing-paths';
import { COLLECTION_MODULE_PATH } from './collection-page/collection-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,
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,
component: ThemedForbiddenComponent

View File

@@ -9,6 +9,13 @@ import { PostRequest } from './request.models';
import { RequestService } from './request.service';
import { ItemRequest } from '../shared/item-request.model';
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
@@ -18,14 +25,21 @@ import { hasValue, isNotEmpty } from '../../shared/empty.util';
providedIn: 'root',
}
)
export class ItemRequestDataService {
export class ItemRequestDataService extends DataService<ItemRequest> {
protected linkPath = 'itemrequests';
constructor(
protected requestService: RequestService,
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> {

View File

@@ -1,14 +1,16 @@
import { autoserialize } from 'cerialize';
import { autoserialize, deserialize } 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';
import { CacheableObject } from '../cache/object-cache.reducer';
import { HALLink } from './hal-link.model';
/**
* Model class for a Configuration Property
*/
@typedObject
export class ItemRequest {
export class ItemRequest implements CacheableObject {
static type = ITEM_REQUEST;
/**
@@ -75,5 +77,14 @@ export class ItemRequest {
@autoserialize
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">
<h3 class="mb-4">{{'grant-deny-request-copy.header' | translate}}</h3>
<p>{{'grant-deny-request-copy.intro' | translate}}</p>
<div class="container" *ngVar="(itemRequestRD$ | async) as itemRequestRD">
<h3 class="mb-4">{{'grant-deny-request-copy.header' | translate}}</h3>
<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 ">
<a [routerLink]="'/grant'"
class="btn btn-outline-danger"
title="{{'grant-deny-request-copy.grant' | translate }}">
{{'grant-deny-request-copy.grant' | translate }}
</a>
<a [routerLink]="grantRoute$ | async"
class="btn btn-outline-primary"
title="{{'grant-deny-request-copy.grant' | translate }}">
{{'grant-deny-request-copy.grant' | translate }}
</a>
<a [routerLink]="'/deny'"
class="btn btn-outline-primary"
title="{{'grant-deny-request-copy.deny' | translate }}">
{{'grant-deny-request-copy.deny' | translate }}
</a>
<a [routerLink]="denyRoute$ | async"
class="btn btn-outline-danger"
title="{{'grant-deny-request-copy.deny' | translate }}">
{{'grant-deny-request-copy.deny' | translate }}
</a>
</div>
</div>
</div>
<ds-loading *ngIf="!itemRequestRD || itemRequestRD?.isLoading"></ds-loading>
</div>

View File

@@ -7,13 +7,20 @@ import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder } from '@angular/forms';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { EndUserAgreementService } from '../../core/end-user-agreement/end-user-agreement.service';
import { map } from 'rxjs/operators';
import { Registration } from '../../core/shared/registration.model';
import { map, switchMap } 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 {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
redirectOn4xx
} from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
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({
selector: 'ds-grant-deny-request-copy',
@@ -21,30 +28,46 @@ import { AuthService } from '../../core/auth/auth.service';
templateUrl: './grant-deny-request-copy.component.html'
})
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(
private translateService: TranslateService,
private ePersonDataService: EPersonDataService,
private store: Store<CoreState>,
private router: Router,
private route: ActivatedRoute,
private formBuilder: FormBuilder,
private notificationsService: NotificationsService,
private endUserAgreementService: EndUserAgreementService,
private authService: AuthService
private authService: AuthService,
private itemDataService: ItemDataService,
private nameService: DSONameService,
) {
}
ngOnInit(): void {
this.itemRequest$ = this.route.data.pipe(
this.itemRequestRD$ = this.route.data.pipe(
map((data) => data.request as RemoteData<ItemRequest>),
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 { RouterModule } from '@angular/router';
import { EndUserAgreementCookieGuard } from '../core/end-user-agreement/end-user-agreement-cookie.guard';
import { RequestCopyResolver } from './request-copy.resolver';
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({
imports: [
RouterModule.forChild([
{
path: ':token',
component: GrantDenyRequestCopyComponent,
resolve: {request: RequestCopyResolver},
canActivate: [EndUserAgreementCookieGuard]
resolve: {
request: RequestCopyResolver
},
children: [
{
path: '',
component: GrantDenyRequestCopyComponent,
},
{
path: REQUEST_COPY_DENY_PATH,
component: DenyRequestCopyComponent,
}
]
}
])
],
@@ -20,8 +31,5 @@ import { GrantDenyRequestCopyComponent } from './grant-deny-request-copy/grant-d
GrantDenyRequestCopyComponent
]
})
/**
* Module related to the navigation to components used to register a new user
*/
export class RegisterPageRoutingModule {
export class RequestCopyRoutingModule {
}

View File

@@ -2,14 +2,20 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
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({
imports: [
CommonModule,
SharedModule,
RequestCopyRoutingModule
],
declarations: [
GrantDenyRequestCopyComponent
GrantDenyRequestCopyComponent,
DenyRequestCopyComponent,
EmailRequestCopyComponent,
],
providers: []
})

View File

@@ -3,7 +3,10 @@ import { RemoteData } from '../core/data/remote-data';
import { ItemRequest } from '../core/shared/item-request.model';
import { Observable } from 'rxjs/internal/Observable';
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>> {
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> {
// TODO add method after knowing whether they will change the rest object to be compatible with normal dataservice.
return undefined;
return this.itemRequestDataService.findById(route.params.token).pipe(
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",
@@ -1430,6 +1436,31 @@
"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.breadcrumbs": "Home",