Skip to content

Commit

Permalink
[CST-12109] request reinstate for normal users
Browse files Browse the repository at this point in the history
  • Loading branch information
alisaismailati committed Jan 23, 2024
1 parent ad5e3af commit 8cc45e8
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 19 deletions.
5 changes: 4 additions & 1 deletion src/app/item-page/alerts/item-alerts.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
<ds-alert [type]="AlertTypeEnum.Warning">
<div class="d-flex justify-content-between flex-wrap">
<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>
</ds-alert>
</div>
Expand Down
21 changes: 21 additions & 0 deletions src/app/item-page/alerts/item-alerts.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ItemAlertsComponent>;
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();
Expand Down
39 changes: 39 additions & 0 deletions src/app/item-page/alerts/item-alerts.component.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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<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);
}
}
13 changes: 10 additions & 3 deletions src/app/notifications/notifications.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -55,7 +56,11 @@ const PROVIDERS = [
QualityAssuranceSourceService,
QualityAssuranceTopicDataService,
QualityAssuranceSourceDataService,
QualityAssuranceEventDataService
QualityAssuranceEventDataService,
];

const PIPES = [
GetEPersonDataPipe
];

@NgModule({
Expand All @@ -65,7 +70,8 @@ const PROVIDERS = [
declarations: [
...COMPONENTS,
...DIRECTIVES,
...ENTRY_COMPONENTS
...ENTRY_COMPONENTS,
...PIPES
],
providers: [
...PROVIDERS
Expand All @@ -75,7 +81,8 @@ const PROVIDERS = [
],
exports: [
...COMPONENTS,
...DIRECTIVES
...DIRECTIVES,
...PIPES
]
})

Expand Down
26 changes: 26 additions & 0 deletions src/app/notifications/qa/events/get-ePerson-data.pipe.ts
Original file line number Diff line number Diff line change
@@ -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)})
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ <h4 class="border-bottom pb-2">
<th *ngIf="hasDetailColumn() && showTopic.indexOf('/PROJECT') !== -1" scope="col" class="content-col">
{{'quality-assurance.event.table.project-details' | translate}}
</th>
<th *ngIf="hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)" scope="col" class="content-col">
{{'quality-assurance.event.table.reasons' | translate}}
</th>
<ng-container *ngIf="hasDetailColumn() && (showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1)">
<th scope="col" class="content-col">
{{'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>
</tr>
</thead>
Expand Down Expand Up @@ -76,14 +81,24 @@ <h4 class="border-bottom pb-2">
{{ (showMore ? 'quality-assurance.event.table.less': 'quality-assurance.event.table.more') | translate }}
</button>
</td>

<td *ngIf="showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1">
<p>
<span *ngIf="eventElement.event.message">
<span class="badge badge-info">{{eventElement.event.message.reason}}</span><br>
</span>
</p>
</td>
<ng-container *ngIf="showTopic.indexOf('/REINSTATE') !== -1 || showTopic.indexOf('/WITHDRAWN') !== -1">
<td>
<p>
<span *ngIf="eventElement.event.message">
<span class="badge badge-info">{{eventElement.event.message.reason}}</span><br>
</span>
</p>
</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">
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -458,4 +459,8 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
delete(qaEvent: QualityAssuranceEventData): Observable<RemoteData<NoContent>> {
return this.qualityAssuranceEventRestService.deleteQAEvent(qaEvent);
}

getEntityPageRoute(itemId: string): string {
return getEntityPageRoute('person', itemId);
}
}
6 changes: 3 additions & 3 deletions src/app/shared/dso-page/dso-edit-menu.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/assets/i18n/en.json5
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 8cc45e8

Please sign in to comment.