mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[CST-12109] request reinstate for normal users
This commit is contained in:
@@ -6,7 +6,10 @@
|
|||||||
<ds-alert [type]="AlertTypeEnum.Warning">
|
<ds-alert [type]="AlertTypeEnum.Warning">
|
||||||
<div class="d-flex justify-content-between flex-wrap">
|
<div class="d-flex justify-content-between flex-wrap">
|
||||||
<span class="align-self-center">{{'item.alerts.withdrawn' | translate}}</span>
|
<span class="align-self-center">{{'item.alerts.withdrawn' | translate}}</span>
|
||||||
<a routerLink="/home" class="btn btn-primary btn-sm">{{"404.link.home-page" | translate}}</a>
|
<div class="gap-2 d-flex">
|
||||||
|
<a routerLink="/home" class="btn btn-primary btn-sm">{{"404.link.home-page" | translate}}</a>
|
||||||
|
<a *ngIf="showReinstateButton$() | async" class="btn btn-primary btn-sm" (click)="openReinstateModal()">{{ 'item.alerts.reinstate-request' | translate}}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ds-alert>
|
</ds-alert>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,16 +4,37 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { By } from '@angular/platform-browser';
|
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', () => {
|
describe('ItemAlertsComponent', () => {
|
||||||
let component: ItemAlertsComponent;
|
let component: ItemAlertsComponent;
|
||||||
let fixture: ComponentFixture<ItemAlertsComponent>;
|
let fixture: ComponentFixture<ItemAlertsComponent>;
|
||||||
let item: Item;
|
let item: Item;
|
||||||
|
let authorizationService;
|
||||||
|
let dsoWithdrawnReinstateModalService;
|
||||||
|
let correctionTypeDataService;
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||||
|
isAuthorized: of(true)
|
||||||
|
});
|
||||||
|
dsoWithdrawnReinstateModalService = jasmine.createSpyObj('dsoWithdrawnReinstateModalService', {
|
||||||
|
openCreateWithdrawnReinstateModal: {}
|
||||||
|
});
|
||||||
|
correctionTypeDataService = jasmine.createSpyObj('correctionTypeDataService', {
|
||||||
|
findByItem: of({})
|
||||||
|
});
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ItemAlertsComponent],
|
declarations: [ItemAlertsComponent],
|
||||||
imports: [TranslateModule.forRoot()],
|
imports: [TranslateModule.forRoot()],
|
||||||
|
providers: [
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||||
|
{ provide: DsoWithdrawnReinstateModalService, useValue: dsoWithdrawnReinstateModalService },
|
||||||
|
{ provide: CorrectionTypeDataService, useValue: correctionTypeDataService }
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@@ -1,6 +1,12 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { AlertType } from '../../shared/alert/alert-type';
|
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({
|
@Component({
|
||||||
selector: 'ds-item-alerts',
|
selector: 'ds-item-alerts',
|
||||||
@@ -21,4 +27,37 @@ export class ItemAlertsComponent {
|
|||||||
* @type {AlertType}
|
* @type {AlertType}
|
||||||
*/
|
*/
|
||||||
public AlertTypeEnum = 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<boolean> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ import { QualityAssuranceSourceService } from './qa/source/quality-assurance-sou
|
|||||||
import {
|
import {
|
||||||
QualityAssuranceSourceDataService
|
QualityAssuranceSourceDataService
|
||||||
} from '../core/notifications/qa/source/quality-assurance-source-data.service';
|
} from '../core/notifications/qa/source/quality-assurance-source-data.service';
|
||||||
|
import { GetEPersonDataPipe } from './qa/events/get-ePerson-data.pipe';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -55,7 +56,11 @@ const PROVIDERS = [
|
|||||||
QualityAssuranceSourceService,
|
QualityAssuranceSourceService,
|
||||||
QualityAssuranceTopicDataService,
|
QualityAssuranceTopicDataService,
|
||||||
QualityAssuranceSourceDataService,
|
QualityAssuranceSourceDataService,
|
||||||
QualityAssuranceEventDataService
|
QualityAssuranceEventDataService,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PIPES = [
|
||||||
|
GetEPersonDataPipe
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -65,7 +70,8 @@ const PROVIDERS = [
|
|||||||
declarations: [
|
declarations: [
|
||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
...DIRECTIVES,
|
...DIRECTIVES,
|
||||||
...ENTRY_COMPONENTS
|
...ENTRY_COMPONENTS,
|
||||||
|
...PIPES
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
...PROVIDERS
|
...PROVIDERS
|
||||||
@@ -75,7 +81,8 @@ const PROVIDERS = [
|
|||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
...COMPONENTS,
|
...COMPONENTS,
|
||||||
...DIRECTIVES
|
...DIRECTIVES,
|
||||||
|
...PIPES
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
26
src/app/notifications/qa/events/get-ePerson-data.pipe.ts
Normal file
26
src/app/notifications/qa/events/get-ePerson-data.pipe.ts
Normal file
@@ -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<EPerson> {
|
||||||
|
return this.ePersonDataService.findById(personId, true).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
getRemoteDataPayload(),
|
||||||
|
tap((ePerson: EPerson) => {console.log(ePerson)})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -36,9 +36,14 @@
|
|||||||
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col" class="content-col">
|
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col" class="content-col">
|
||||||
{{'quality-assurance.event.table.project-details' | translate}}
|
{{'quality-assurance.event.table.project-details' | translate}}
|
||||||
</th>
|
</th>
|
||||||
<th *ngIf="hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)" scope="col" class="content-col">
|
<ng-container *ngIf="hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)">
|
||||||
{{'quality-assurance.event.table.reasons' | translate}}
|
<th scope="col" class="content-col">
|
||||||
</th>
|
{{'quality-assurance.event.table.reasons' | translate}}
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="content-col">
|
||||||
|
{{'quality-assurance.event.table.person-who-requested' | translate}}
|
||||||
|
</th>
|
||||||
|
</ng-container>
|
||||||
<th scope="col" class="button-col">{{'quality-assurance.event.table.actions' | translate}}</th>
|
<th scope="col" class="button-col">{{'quality-assurance.event.table.actions' | translate}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -76,14 +81,24 @@
|
|||||||
{{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }}
|
{{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
<ng-container *ngIf="showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1">
|
||||||
<td *ngIf="showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1">
|
<td>
|
||||||
<p>
|
<p>
|
||||||
<span *ngIf="eventElement.event.message">
|
<span *ngIf="eventElement.event.message">
|
||||||
<span class="badge badge-info">{{eventElement.event.message.reason}}</span><br>
|
<span class="badge badge-info">{{eventElement.event.message.reason}}</span><br>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>
|
||||||
|
<span *ngIf="eventElement.event.originalId">
|
||||||
|
<a target="_blank" [routerLink]="getEntityPageRoute(eventElement.event.originalId)">
|
||||||
|
{{ (eventElement.event.originalId | dsGetEPersonData | async)?.name }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<td *ngIf="showTopic.indexOf('/PROJECT') !== -1">
|
<td *ngIf="showTopic.indexOf('/PROJECT') !== -1">
|
||||||
<p>
|
<p>
|
||||||
|
@@ -34,6 +34,7 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati
|
|||||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
import { NoContent } from '../../../core/shared/NoContent.model';
|
import { NoContent } from '../../../core/shared/NoContent.model';
|
||||||
import {environment} from '../../../../environments/environment';
|
import {environment} from '../../../../environments/environment';
|
||||||
|
import { getEntityPageRoute } from 'src/app/item-page/item-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to display the Quality Assurance event list.
|
* Component to display the Quality Assurance event list.
|
||||||
@@ -458,4 +459,8 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
|
|||||||
delete(qaEvent: QualityAssuranceEventData): Observable<RemoteData<NoContent>> {
|
delete(qaEvent: QualityAssuranceEventData): Observable<RemoteData<NoContent>> {
|
||||||
return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent);
|
return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEntityPageRoute(itemId: string): string {
|
||||||
|
return getEntityPageRoute('person', itemId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ import { getDSORoute } from '../../app-routing-paths';
|
|||||||
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
import { ResearcherProfileDataService } from '../../core/profile/researcher-profile-data.service';
|
||||||
import { NotificationsService } from '../notifications/notifications.service';
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
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 { AuthService } from '../../core/auth/auth.service';
|
||||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
import { RequestParam } from '../../core/cache/models/request-param.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',
|
id: 'withdrawn-item',
|
||||||
active: false,
|
active: false,
|
||||||
visible: dso.isArchived && correction?.totalElements > 0,
|
visible: dso.isArchived && correction?.page.some((c) => c.topic === REQUEST_WITHDRAWN),
|
||||||
model: {
|
model: {
|
||||||
type: MenuItemType.ONCLICK,
|
type: MenuItemType.ONCLICK,
|
||||||
text:'item.page.withdrawn',
|
text:'item.page.withdrawn',
|
||||||
@@ -207,7 +207,7 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
|||||||
{
|
{
|
||||||
id: 'reinstate-item',
|
id: 'reinstate-item',
|
||||||
active: false,
|
active: false,
|
||||||
visible: dso.isWithdrawn && correction?.totalElements > 0,
|
visible: dso.isWithdrawn && correction?.page.some((c) => c.topic === REQUEST_REINSTATE),
|
||||||
model: {
|
model: {
|
||||||
type: MenuItemType.ONCLICK,
|
type: MenuItemType.ONCLICK,
|
||||||
text:'item.page.reinstate',
|
text:'item.page.reinstate',
|
||||||
|
@@ -15,6 +15,10 @@ import { NotificationsService } from '../../notifications/notifications.service'
|
|||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
|
||||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -34,7 +38,7 @@ export class DsoWithdrawnReinstateModalService {
|
|||||||
/**
|
/**
|
||||||
* Open the create withdrawn modal for the provided dso
|
* 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;
|
const target = dso.id;
|
||||||
// Open modal
|
// Open modal
|
||||||
const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent);
|
const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent);
|
||||||
|
@@ -1916,6 +1916,10 @@
|
|||||||
|
|
||||||
"item.alerts.withdrawn": "This item has been withdrawn",
|
"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.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",
|
"item.edit.authorizations.title": "Edit item's Policies",
|
||||||
|
Reference in New Issue
Block a user