diff --git a/src/app/item-page/alerts/item-alerts.component.html b/src/app/item-page/alerts/item-alerts.component.html index cd71d23a910..f6304340f3d 100644 --- a/src/app/item-page/alerts/item-alerts.component.html +++ b/src/app/item-page/alerts/item-alerts.component.html @@ -6,7 +6,10 @@
{{'item.alerts.withdrawn' | translate}} - {{"404.link.home-page" | translate}} +
diff --git a/src/app/item-page/alerts/item-alerts.component.spec.ts b/src/app/item-page/alerts/item-alerts.component.spec.ts index a933eb6a589..6dee6879b13 100644 --- a/src/app/item-page/alerts/item-alerts.component.spec.ts +++ b/src/app/item-page/alerts/item-alerts.component.spec.ts @@ -4,16 +4,37 @@ import { TranslateModule } from '@ngx-translate/core'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { Item } from '../../core/shared/item.model'; import { By } from '@angular/platform-browser'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { of } from 'rxjs'; +import { DsoWithdrawnReinstateModalService } from '../../shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service'; +import { CorrectionTypeDataService } from '../../core/submission/correctiontype-data.service'; describe('ItemAlertsComponent', () => { let component: ItemAlertsComponent; let fixture: ComponentFixture; let item: Item; + let authorizationService; + let dsoWithdrawnReinstateModalService; + let correctionTypeDataService; beforeEach(waitForAsync(() => { + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: of(true) + }); + dsoWithdrawnReinstateModalService = jasmine.createSpyObj('dsoWithdrawnReinstateModalService', { + openCreateWithdrawnReinstateModal: {} + }); + correctionTypeDataService = jasmine.createSpyObj('correctionTypeDataService', { + findByItem: of({}) + }); TestBed.configureTestingModule({ declarations: [ItemAlertsComponent], imports: [TranslateModule.forRoot()], + providers: [ + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: DsoWithdrawnReinstateModalService, useValue: dsoWithdrawnReinstateModalService }, + { provide: CorrectionTypeDataService, useValue: correctionTypeDataService } + ], schemas: [NO_ERRORS_SCHEMA] }) .compileComponents(); diff --git a/src/app/item-page/alerts/item-alerts.component.ts b/src/app/item-page/alerts/item-alerts.component.ts index 2b1df58c9f7..025dafb425e 100644 --- a/src/app/item-page/alerts/item-alerts.component.ts +++ b/src/app/item-page/alerts/item-alerts.component.ts @@ -1,6 +1,12 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../core/shared/item.model'; import { AlertType } from '../../shared/alert/alert-type'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { Observable, combineLatest, map } from 'rxjs'; +import { DsoWithdrawnReinstateModalService, REQUEST_REINSTATE } from '../../shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service'; +import { CorrectionTypeDataService } from '../../core/submission/correctiontype-data.service'; +import { getFirstCompletedRemoteData, getPaginatedListPayload, getRemoteDataPayload } from 'src/app/core/shared/operators'; @Component({ selector: 'ds-item-alerts', @@ -21,4 +27,37 @@ export class ItemAlertsComponent { * @type {AlertType} */ public AlertTypeEnum = AlertType; + + constructor( + private authService: AuthorizationDataService, + private dsoWithdrawnReinstateModalService: DsoWithdrawnReinstateModalService, + private correctionTypeDataService: CorrectionTypeDataService + ) { + } + + /** + * Determines whether to show the reinstate button. + * The button is shown if the user is not an admin and the item has a reinstate request. + * @returns An Observable that emits a boolean value indicating whether to show the reinstate button. + */ + showReinstateButton$(): Observable { + const correction$ = this.correctionTypeDataService.findByItem(this.item.uuid, true).pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + getPaginatedListPayload() + ); + const isAdmin$ = this.authService.isAuthorized(FeatureID.AdministratorOf); + return combineLatest([isAdmin$, correction$]).pipe( + map(([isAdmin, correction]) => { + return !isAdmin && correction.some((correctionType) => correctionType.topic === REQUEST_REINSTATE); + } + )); + } + + /** + * Opens the reinstate modal for the item. + */ + openReinstateModal() { + this.dsoWithdrawnReinstateModalService.openCreateWithdrawnReinstateModal(this.item, 'request-reinstate', this.item.isArchived); + } } diff --git a/src/app/notifications/notifications.module.ts b/src/app/notifications/notifications.module.ts index 7003ed3cc86..26ad81cb9cc 100644 --- a/src/app/notifications/notifications.module.ts +++ b/src/app/notifications/notifications.module.ts @@ -26,6 +26,7 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou import { QualityAssuranceSourceDataService } from '../core/notifications/qa/source/quality-assurance-source-data.service'; +import { GetEPersonDataPipe } from './qa/events/get-ePerson-data.pipe'; const MODULES = [ CommonModule, @@ -55,7 +56,11 @@ const PROVIDERS = [ QualityAssuranceSourceService, QualityAssuranceTopicDataService, QualityAssuranceSourceDataService, - QualityAssuranceEventDataService + QualityAssuranceEventDataService, +]; + +const PIPES = [ + GetEPersonDataPipe ]; @NgModule({ @@ -65,7 +70,8 @@ const PROVIDERS = [ declarations: [ ...COMPONENTS, ...DIRECTIVES, - ...ENTRY_COMPONENTS + ...ENTRY_COMPONENTS, + ...PIPES ], providers: [ ...PROVIDERS @@ -75,7 +81,8 @@ const PROVIDERS = [ ], exports: [ ...COMPONENTS, - ...DIRECTIVES + ...DIRECTIVES, + ...PIPES ] }) diff --git a/src/app/notifications/qa/events/get-ePerson-data.pipe.ts b/src/app/notifications/qa/events/get-ePerson-data.pipe.ts new file mode 100644 index 00000000000..d84ff30c638 --- /dev/null +++ b/src/app/notifications/qa/events/get-ePerson-data.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Observable, tap } from 'rxjs'; +import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; +import { EPerson } from '../../../core/eperson/models/eperson.model'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators'; + +@Pipe({ + name: 'dsGetEPersonData' +}) +export class GetEPersonDataPipe implements PipeTransform { + constructor(private ePersonDataService: EPersonDataService) { } + + /** + * Transforms the personId into an Observable of EPerson. + * @param personId The ID of the person. + * @returns An Observable of EPerson. + */ + transform(personId: string): Observable { + return this.ePersonDataService.findById(personId, true).pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + tap((ePerson: EPerson) => {console.log(ePerson)}) + ); + } + +} diff --git a/src/app/notifications/qa/events/quality-assurance-events.component.html b/src/app/notifications/qa/events/quality-assurance-events.component.html index 0f7fc01d62e..e617d7b55e8 100644 --- a/src/app/notifications/qa/events/quality-assurance-events.component.html +++ b/src/app/notifications/qa/events/quality-assurance-events.component.html @@ -36,9 +36,14 @@

{{'quality-assurance.event.table.project-details' | translate}} - - {{'quality-assurance.event.table.reasons' | translate}} - + + + {{'quality-assurance.event.table.reasons' | translate}} + + + {{'quality-assurance.event.table.person-who-requested' | translate}} + + {{'quality-assurance.event.table.actions' | translate}} @@ -76,14 +81,24 @@

{{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }} - - -

- - {{eventElement.event.message.reason}}
-
-

- + + +

+ + {{eventElement.event.message.reason}}
+
+

+ + +

+ + + {{ (eventElement.event.originalId | dsGetEPersonData | async)?.name }} + + +

+ +

diff --git a/src/app/notifications/qa/events/quality-assurance-events.component.ts b/src/app/notifications/qa/events/quality-assurance-events.component.ts index bf0b1dfa804..24369c00fa6 100644 --- a/src/app/notifications/qa/events/quality-assurance-events.component.ts +++ b/src/app/notifications/qa/events/quality-assurance-events.component.ts @@ -34,6 +34,7 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { NoContent } from '../../../core/shared/NoContent.model'; import {environment} from '../../../../environments/environment'; +import { getEntityPageRoute } from 'src/app/item-page/item-page-routing-paths'; /** * Component to display the Quality Assurance event list. @@ -458,4 +459,8 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy { delete(qaEvent: QualityAssuranceEventData): Observable> { return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent); } + + getEntityPageRoute(itemId: string): string { + return getEntityPageRoute('person', itemId); + } } diff --git a/src/app/shared/dso-page/dso-edit-menu.resolver.ts b/src/app/shared/dso-page/dso-edit-menu.resolver.ts index ef0d10d6f83..bcded3acd5a 100644 --- a/src/app/shared/dso-page/dso-edit-menu.resolver.ts +++ b/src/app/shared/dso-page/dso-edit-menu.resolver.ts @@ -23,7 +23,7 @@ import { getDSORoute } from '../../app-routing-paths'; import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service'; import { NotificationsService } from '../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { DsoWithdrawnReinstateModalService } from './dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service'; +import { DsoWithdrawnReinstateModalService, REQUEST_REINSTATE, REQUEST_WITHDRAWN } from './dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service'; import { AuthService } from '../../core/auth/auth.service'; import { FindListOptions } from '../../core/data/find-list-options.model'; import { RequestParam } from '../../core/cache/models/request-param.model'; @@ -193,7 +193,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection { id: 'withdrawn-item', active: false, - visible: dso.isArchived && correction?.totalElements > 0, + visible: dso.isArchived && correction?.page.some((c) => c.topic === REQUEST_WITHDRAWN), model: { type: MenuItemType.ONCLICK, text:'item.page.withdrawn', @@ -207,7 +207,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection { id: 'reinstate-item', active: false, - visible: dso.isWithdrawn && correction?.totalElements > 0, + visible: dso.isWithdrawn && correction?.page.some((c) => c.topic === REQUEST_REINSTATE), model: { type: MenuItemType.ONCLICK, text:'item.page.reinstate', diff --git a/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts b/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts index a4214099a72..ebb304957e4 100644 --- a/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts +++ b/src/app/shared/dso-page/dso-withdrawn-reinstate-service/dso-withdrawn-reinstate-modal.service.ts @@ -15,6 +15,10 @@ import { NotificationsService } from '../../notifications/notifications.service' import { take } from 'rxjs/operators'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { Item } from 'src/app/core/shared/item.model'; + +export const REQUEST_WITHDRAWN = 'REQUEST/WITHDRAWN'; +export const REQUEST_REINSTATE = 'REQUEST/REINSTATE'; @Injectable({ providedIn: 'root' @@ -34,7 +38,7 @@ export class DsoWithdrawnReinstateModalService { /** * Open the create withdrawn modal for the provided dso */ - openCreateWithdrawnReinstateModal(dso, correctionType: string, state: boolean): void { + openCreateWithdrawnReinstateModal(dso: Item, correctionType: string, state: boolean): void { const target = dso.id; // Open modal const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 396e5db9a2e..164d384ea34 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1916,6 +1916,10 @@ "item.alerts.withdrawn": "This item has been withdrawn", + "item.alerts.reinstate-request": "Request reinstate", + + "quality-assurance.event.table.person-who-requested": "Requested by", + "item.edit.authorizations.heading": "With this editor you can view and alter the policies of an item, plus alter policies of individual item components: bundles and bitstreams. Briefly, an item is a container of bundles, and bundles are containers of bitstreams. Containers usually have ADD/REMOVE/READ/WRITE policies, while bitstreams only have READ/WRITE policies.", "item.edit.authorizations.title": "Edit item's Policies",