diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 74a3f7183e..3fa56698f7 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -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}`; +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 52a07b89f5..fa75b0b2f0 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -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 diff --git a/src/app/core/data/item-request-data.service.ts b/src/app/core/data/item-request-data.service.ts index dc11e2b0c5..a0a7fe6d03 100644 --- a/src/app/core/data/item-request-data.service.ts +++ b/src/app/core/data/item-request-data.service.ts @@ -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 { protected linkPath = 'itemrequests'; constructor( protected requestService: RequestService, protected rdbService: RemoteDataBuildService, - protected halService: HALEndpointService) { + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer, + ) { + super(); } getItemRequestEndpoint(): Observable { diff --git a/src/app/core/shared/item-request.model.ts b/src/app/core/shared/item-request.model.ts index b14ed07656..da9c4c9e08 100644 --- a/src/app/core/shared/item-request.model.ts +++ b/src/app/core/shared/item-request.model.ts @@ -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; + }; } 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 new file mode 100644 index 0000000000..226fac1f67 --- /dev/null +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.html @@ -0,0 +1,9 @@ +
+

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

+
+

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

+ + +
+ +
diff --git a/src/app/request-copy/deny-request-copy/deny-request-copy.component.scss b/src/app/request-copy/deny-request-copy/deny-request-copy.component.scss new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..4ffeb61605 --- /dev/null +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts @@ -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>; + + constructor( + private router: Router, + private route: ActivatedRoute, + private authService: AuthService, + ) { + + } + + ngOnInit(): void { + this.itemRequestRD$ = this.route.data.pipe( + map((data) => data.request as RemoteData), + getFirstCompletedRemoteData(), + redirectOn4xx(this.router, this.authService), + ); + } + +} 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 new file mode 100644 index 0000000000..b4c7b65aba --- /dev/null +++ b/src/app/request-copy/email-request-copy/email-request-copy.component.html @@ -0,0 +1,29 @@ +
+
+ + +
+ {{ 'grant-deny-request-copy.email.subject.empty' | translate }} +
+
+
+ + +
+ {{ 'grant-deny-request-copy.email.message.empty' | translate }} +
+
+
+ + +
+
diff --git a/src/app/request-copy/email-request-copy/email-request-copy.component.scss b/src/app/request-copy/email-request-copy/email-request-copy.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/request-copy/email-request-copy/email-request-copy.component.ts b/src/app/request-copy/email-request-copy/email-request-copy.component.ts new file mode 100644 index 0000000000..0bb2286b4d --- /dev/null +++ b/src/app/request-copy/email-request-copy/email-request-copy.component.ts @@ -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 = new EventEmitter(); + + @Input() subject: string; + @Input() message: string; + + constructor(protected location: Location) { + } + + submit() { + this.send.emit(new RequestCopyEmail(this.subject, this.message)); + } + + return() { + this.location.back(); + } +} diff --git a/src/app/request-copy/email-request-copy/request-copy-email.model.ts b/src/app/request-copy/email-request-copy/request-copy-email.model.ts new file mode 100644 index 0000000000..45cb44e1ce --- /dev/null +++ b/src/app/request-copy/email-request-copy/request-copy-email.model.ts @@ -0,0 +1,5 @@ +export class RequestCopyEmail { + constructor(public subject: string, + public message: string) { + } +} diff --git a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html index ae38a964e8..5be182c4cb 100644 --- a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html +++ b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html @@ -1,20 +1,22 @@ -
-

{{'grant-deny-request-copy.header' | translate}}

-

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

- +
+

{{'grant-deny-request-copy.header' | translate}}

+ \ No newline at end of file +
+ +
diff --git a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.ts b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.ts index f8134cdf96..81e5c8abac 100644 --- a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.ts +++ b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.ts @@ -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>; - + itemRequestRD$: Observable>; + itemRD$: Observable>; + itemName$: Observable; + + denyRoute$: Observable; + grantRoute$: Observable; constructor( - private translateService: TranslateService, - private ePersonDataService: EPersonDataService, - private store: Store, 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), 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)) + ); } } diff --git a/src/app/request-copy/request-copy-routing-paths.ts b/src/app/request-copy/request-copy-routing-paths.ts new file mode 100644 index 0000000000..1d0204a1b8 --- /dev/null +++ b/src/app/request-copy/request-copy-routing-paths.ts @@ -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(); +} diff --git a/src/app/request-copy/request-copy-routing.module.ts b/src/app/request-copy/request-copy-routing.module.ts index 734a661014..c25afcc459 100644 --- a/src/app/request-copy/request-copy-routing.module.ts +++ b/src/app/request-copy/request-copy-routing.module.ts @@ -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 { } diff --git a/src/app/request-copy/request-copy.module.ts b/src/app/request-copy/request-copy.module.ts index eef0ee4313..f3b29d5b77 100644 --- a/src/app/request-copy/request-copy.module.ts +++ b/src/app/request-copy/request-copy.module.ts @@ -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: [] }) diff --git a/src/app/request-copy/request-copy.resolver.ts b/src/app/request-copy/request-copy.resolver.ts index 0086d51e57..948445ee0f 100644 --- a/src/app/request-copy/request-copy.resolver.ts +++ b/src/app/request-copy/request-copy.resolver.ts @@ -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> { constructor( @@ -12,8 +15,9 @@ export class RequestCopyResolver implements Resolve> { } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> | Promise> | RemoteData { - // 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(), + ); } -} \ No newline at end of file +} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d83ef56a5a..1b7598a7d1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -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",