mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
[CST-12109] implemented withdrawn-reinstate requests
This commit is contained in:
@@ -237,13 +237,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
|
||||
.then((m) => m.SubscriptionsPageRoutingModule),
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent },
|
||||
{
|
||||
path: 'items',
|
||||
loadChildren: () => import('./shared/correction-suggestion/correction-suggestion.module')
|
||||
.then((m) => m.CorrectionSuggestionModule),
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }
|
||||
]
|
||||
}
|
||||
], {
|
||||
|
@@ -185,7 +185,6 @@ import { FlatBrowseDefinition } from './shared/flat-browse-definition.model';
|
||||
import { ValueListBrowseDefinition } from './shared/value-list-browse-definition.model';
|
||||
import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-browse-definition';
|
||||
import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model';
|
||||
import { CorrectionTypeMode } from './submission/models/correction-type-mode.model';
|
||||
import { CorrectionTypeDataService } from './submission/correctiontype-data.service';
|
||||
|
||||
/**
|
||||
@@ -309,8 +308,8 @@ const PROVIDERS = [
|
||||
OrcidAuthService,
|
||||
OrcidQueueDataService,
|
||||
OrcidHistoryDataService,
|
||||
SupervisionOrderDataService
|
||||
CorrectionTypeDataService,
|
||||
SupervisionOrderDataService,
|
||||
CorrectionTypeDataService
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -390,7 +389,6 @@ export const models =
|
||||
Subscription,
|
||||
ItemRequest,
|
||||
BulkAccessConditionOptions
|
||||
CorrectionTypeMode
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -207,7 +207,7 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService<Qu
|
||||
* @param data the data to post
|
||||
* @returns the RestResponse as an Observable
|
||||
*/
|
||||
postData(data: string): Observable<RemoteData<OpenaireBrokerEventObject>> {
|
||||
postData(data: string): Observable<RemoteData<QualityAssuranceEventObject>> {
|
||||
const requestId = this.requestService.generateRequestId();
|
||||
const href$ = this.getBrowseEndpoint();
|
||||
|
||||
@@ -218,7 +218,7 @@ export class QualityAssuranceEventDataService extends IdentifiableDataService<Qu
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
}
|
||||
this.requestService.send(request);
|
||||
return this.rdbService.buildFromRequestUUID<OpenaireBrokerEventObject>(requestId);
|
||||
return this.rdbService.buildFromRequestUUID<QualityAssuranceEventObject>(requestId);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@ import { MenuItemType } from '../shared/menu/menu-item-type.model';
|
||||
import { VersionPageComponent } from './version-page/version-page/version-page.component';
|
||||
import { BitstreamRequestACopyPageComponent } from './bitstreams/request-a-copy/bitstream-request-a-copy-page.component';
|
||||
import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||
import { CORRECTION_TYPE_PATH, REQUEST_COPY_MODULE_PATH } from '../app-routing-paths';
|
||||
import { OrcidPageComponent } from './orcid-page/orcid-page.component';
|
||||
import { OrcidPageGuard } from './orcid-page/orcid-page.guard';
|
||||
import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||
@@ -42,18 +41,6 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver';
|
||||
path: 'full',
|
||||
component: ThemedFullItemPageComponent,
|
||||
},
|
||||
{
|
||||
path: CORRECTION_TYPE_PATH,
|
||||
loadChildren: () => import('../shared/correction-suggestion/correction-suggestion.module')
|
||||
.then((m) => m.CorrectionSuggestionModule),
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: `full/${CORRECTION_TYPE_PATH}` ,
|
||||
loadChildren: () => import('../shared/correction-suggestion/correction-suggestion.module')
|
||||
.then((m) => m.CorrectionSuggestionModule),
|
||||
canActivate: [AuthenticatedGuard]
|
||||
},
|
||||
{
|
||||
path: ITEM_EDIT_PATH,
|
||||
loadChildren: () => import('./edit-item-page/edit-item-page.module')
|
||||
|
@@ -16,10 +16,14 @@ import { RelatedItemsComponent } from './simple/related-items/related-items-comp
|
||||
import {
|
||||
ThemedMetadataRepresentationListComponent
|
||||
} from './simple/metadata-representation-list/themed-metadata-representation-list.component';
|
||||
import {
|
||||
ItemWithdrawnReinstateModalComponent
|
||||
} from '../shared/correction-suggestion/withdrawn-reinstate-modal.component';
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
ItemVersionsDeleteModalComponent,
|
||||
ItemVersionsSummaryModalComponent,
|
||||
ItemWithdrawnReinstateModalComponent
|
||||
|
||||
];
|
||||
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<ng-container *ngIf="(hasCorrectionTypeAvailable() | async)">
|
||||
<button ngbDropdownItem
|
||||
*ngFor="let type of (getCorrectionTypes() | async)"
|
||||
[routerLink]="getTypeRoute(type.id)"
|
||||
[innerHTML]="('context-menu.actions.correction-type.btn.' + type.id | translate)">
|
||||
</button>
|
||||
</ng-container>
|
@@ -1,116 +0,0 @@
|
||||
import { Item } from './../../../core/shared/item.model';
|
||||
import { DSpaceObject } from './../../../core/shared/dspace-object.model';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CorrectionTypeMenuComponent } from './correction-type-menu.component';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||
import { createPaginatedList } from '../../testing/utils.test';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service';
|
||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model';
|
||||
|
||||
describe('CorrectionTypeMenuComponent', () => {
|
||||
let component: CorrectionTypeMenuComponent;
|
||||
let fixture: ComponentFixture<CorrectionTypeMenuComponent>;
|
||||
let componentAsAny: any;
|
||||
|
||||
let correctionTypeService: any;
|
||||
let dso: DSpaceObject;
|
||||
const notificationService = new NotificationsServiceStub();
|
||||
const correctionType: CorrectionType = Object.assign(new CorrectionType(), {
|
||||
id: 'addpersonalpath',
|
||||
creationForm:'manageRelation',
|
||||
discoveryConfiguration: 'RELATION.PersonPath.Items',
|
||||
topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH'
|
||||
});
|
||||
|
||||
const correctionTypeObjRDList$ = createSuccessfulRemoteDataObject$(createPaginatedList([correctionType]));
|
||||
|
||||
beforeEach(async () => {
|
||||
dso = Object.assign(new Item(), {
|
||||
id: 'test-item',
|
||||
_links: {
|
||||
self: { href: 'test-item-selflink' }
|
||||
}
|
||||
});
|
||||
|
||||
correctionTypeService = jasmine.createSpyObj('CorrectionTypeDataService', {
|
||||
findByItem: jasmine.createSpy('findByItem')
|
||||
});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ CorrectionTypeMenuComponent ],
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
}),
|
||||
RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{ provide: CorrectionTypeDataService, useValue: correctionTypeService },
|
||||
{ provide: 'contextMenuObjectProvider', useValue: dso },
|
||||
{ provide: 'contextMenuObjectTypeProvider', useValue: DSpaceObjectType.ITEM },
|
||||
{ provide: NotificationsService, useValue: notificationService }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when correction types are available', () => {
|
||||
beforeEach(() => {
|
||||
correctionTypeService.findByItem.and.returnValue(correctionTypeObjRDList$);
|
||||
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
componentAsAny = fixture.componentInstance;
|
||||
component.contextMenuObject = dso;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should init properly', () => {
|
||||
expect(componentAsAny.correctionTypes$.value).toEqual([correctionType]);
|
||||
});
|
||||
|
||||
it('should render a button', () => {
|
||||
const link = fixture.debugElement.query(By.css('button'));
|
||||
expect(link).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when is no data are available', () => {
|
||||
beforeEach(() => {
|
||||
correctionTypeService.findByItem.and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([])));
|
||||
fixture = TestBed.createComponent(CorrectionTypeMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
componentAsAny = fixture.componentInstance;
|
||||
component.contextMenuObject = dso;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should init edit mode properly', () => {
|
||||
expect(componentAsAny.correctionTypes$.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('should render a button', () => {
|
||||
const link = fixture.debugElement.query(By.css('button'));
|
||||
expect(link).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,98 +0,0 @@
|
||||
import { getAllSucceededRemoteDataPayload, getPaginatedListPayload } from '../../../core/shared/operators';
|
||||
import { CorrectionTypeDataService } from '../../../core/submission/correctiontype-data.service';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ContextMenuEntryComponent } from '../context-menu-entry.component';
|
||||
import { ContextMenuEntryType } from '../context-menu-entry-type';
|
||||
import { BehaviorSubject, map, Observable, startWith, Subscription } from 'rxjs';
|
||||
import { CorrectionType } from '../../../core/submission/models/correction-type-mode.model';
|
||||
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
|
||||
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||
import { rendersContextMenuEntriesForType } from '../context-menu.decorator';
|
||||
import { CORRECTION_TYPE_PATH } from '../../../app-routing-paths';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-correction-type-menu',
|
||||
templateUrl: './correction-type-menu.component.html',
|
||||
})
|
||||
@rendersContextMenuEntriesForType(DSpaceObjectType.ITEM)
|
||||
export class CorrectionTypeMenuComponent extends ContextMenuEntryComponent implements OnInit, OnDestroy {
|
||||
|
||||
/**
|
||||
* The menu entry type
|
||||
*/
|
||||
public static menuEntryType: ContextMenuEntryType = ContextMenuEntryType.CorrectionType;
|
||||
|
||||
/**
|
||||
* List of Edit Modes available on this item
|
||||
* for the current user
|
||||
*/
|
||||
private correctionTypes$: BehaviorSubject<CorrectionType[]> = new BehaviorSubject<CorrectionType[]>([]);
|
||||
|
||||
/**
|
||||
* Variable to track subscription and unsubscribe it onDestroy
|
||||
*/
|
||||
private sub: Subscription;
|
||||
|
||||
isAuthorized$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
@Inject('contextMenuObjectProvider') protected injectedContextMenuObject: DSpaceObject,
|
||||
@Inject('contextMenuObjectTypeProvider') protected injectedContextMenuObjectType: DSpaceObjectType,
|
||||
private correctionTypeService: CorrectionTypeDataService,
|
||||
) {
|
||||
super(injectedContextMenuObject, injectedContextMenuObjectType, ContextMenuEntryType.CorrectionType);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if edit mode is available
|
||||
*/
|
||||
getCorrectionTypes(): Observable<CorrectionType[]> {
|
||||
return this.correctionTypes$.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any correction type are available
|
||||
*/
|
||||
hasCorrectionTypeAvailable(): Observable<boolean> {
|
||||
return this.correctionTypes$.asObservable().pipe(
|
||||
map((type) => isNotEmpty(type) && type.length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get correction types
|
||||
* useCachedVersionIfAvailable = false to force refreshing the list
|
||||
*/
|
||||
getData(): void {
|
||||
this.sub = this.correctionTypeService.findByItem(this.contextMenuObject.id, false).pipe(
|
||||
getAllSucceededRemoteDataPayload(),
|
||||
getPaginatedListPayload(),
|
||||
startWith([])
|
||||
).subscribe((types: CorrectionType[]) => {
|
||||
this.correctionTypes$.next(types);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route to the correction type page
|
||||
* @param typeId correction type id
|
||||
* @returns the route to the correction type page
|
||||
*/
|
||||
getTypeRoute(typeId: string): string[] {
|
||||
return ['./', CORRECTION_TYPE_PATH, typeId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the subscription is unsubscribed from when this component is destroyed
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (hasValue(this.sub)) {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
const pageMap = new Map();
|
||||
|
||||
export function renderCorrectionFor(creationMode: string) {
|
||||
return function decorator(component: any) {
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
pageMap.set(creationMode, component);
|
||||
};
|
||||
}
|
||||
|
||||
export function getCorrectionComponent(creationMode: string) {
|
||||
return pageMap.get(creationMode);
|
||||
}
|
||||
|
@@ -1,24 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CorrectionSuggestionComponent } from './correction-suggestion.component';
|
||||
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ':correctionType',
|
||||
component: CorrectionSuggestionComponent,
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver
|
||||
},
|
||||
data: { title: 'item-correction-suggestion.correction-type', breadcrumbKey: 'item-correction-suggestion.correction-type' },
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
providers: [
|
||||
I18nBreadcrumbResolver
|
||||
]
|
||||
})
|
||||
export class CorrectionSuggestionRoutingModule { }
|
@@ -1,3 +0,0 @@
|
||||
<div class="container">
|
||||
<ng-container *ngComponentOutlet="getComponent(); injector: objectInjector"></ng-container>
|
||||
</div>
|
@@ -1,80 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CorrectionSuggestionComponent } from './correction-suggestion.component';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CorrectionTypeDataService } from 'src/app/core/submission/correctiontype-data.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
describe('CorrectionSuggestionComponent', () => {
|
||||
let component: CorrectionSuggestionComponent;
|
||||
let fixture: ComponentFixture<CorrectionSuggestionComponent>;
|
||||
|
||||
const correctionTypeMock = {
|
||||
id: 'addpersonalpath',
|
||||
topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH',
|
||||
discoveryConfiguration: 'RELATION.PersonPath.Items',
|
||||
creationForm: 'manageRelation',
|
||||
type: 'correctiontype',
|
||||
_links: {
|
||||
self: {
|
||||
href: 'https://rest.api/discover/configurations/addpersonalpath',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const correctionTypeMockRD$ = createSuccessfulRemoteDataObject$(correctionTypeMock);
|
||||
|
||||
const mockActivatedRoute = {
|
||||
snapshot:{
|
||||
params: {
|
||||
correctionType: 'addpersonalpath'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let correctionTypeDataService: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
correctionTypeDataService = jasmine.createSpyObj('correctionTypeDataService', {
|
||||
getCorrectionTypeById: jasmine.createSpy('getCorrectionTypeById')
|
||||
});
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ CorrectionSuggestionComponent ],
|
||||
imports: [
|
||||
CommonModule,
|
||||
BrowserModule,
|
||||
TranslateModule.forRoot(),
|
||||
RouterTestingModule.withRoutes([]),
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||
{ provide: CorrectionTypeDataService, useValue: correctionTypeDataService },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CorrectionSuggestionComponent);
|
||||
component = fixture.componentInstance;
|
||||
correctionTypeDataService.getCorrectionTypeById.and.returnValue(correctionTypeMockRD$);
|
||||
spyOn(component, 'initComponent');
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should subscribe to route params and set correctionTypeId & initialize component', () => {
|
||||
expect((component as any).correctionTypeId).toEqual('addpersonalpath');
|
||||
expect(component.initComponent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@@ -1,94 +0,0 @@
|
||||
import { getRemoteDataPayload } from './../../core/shared/operators';
|
||||
import { CorrectionTypeDataService } from './../../core/submission/correctiontype-data.service';
|
||||
import { ChangeDetectorRef, Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core';
|
||||
import { CorrectionType } from '../../core/submission/models/correction-type-mode.model';
|
||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||
import { getCorrectionComponent } from './correction-suggestion-page.decorator';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { hasValue } from '../empty.util';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-correction-suggestion',
|
||||
templateUrl: './correction-suggestion.component.html',
|
||||
styleUrls: ['./correction-suggestion.component.scss']
|
||||
})
|
||||
export class CorrectionSuggestionComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The correction type object
|
||||
*/
|
||||
public correctionTypeObject: CorrectionType;
|
||||
|
||||
/**
|
||||
* The correction type id
|
||||
*/
|
||||
private correctionTypeId: string;
|
||||
|
||||
/**
|
||||
* The creation form
|
||||
*/
|
||||
private creationForm: string;
|
||||
|
||||
/**
|
||||
* The injector for the component
|
||||
*/
|
||||
public objectInjector: Injector;
|
||||
|
||||
constructor(
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private aroute: ActivatedRoute,
|
||||
private correctionTypeDataService: CorrectionTypeDataService,
|
||||
private injector: Injector,
|
||||
private chd: ChangeDetectorRef,
|
||||
) {
|
||||
this.correctionTypeId = this.aroute.snapshot.params.correctionType;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the component by fetching the correction type object
|
||||
* and rendering the correct component based on the creation form
|
||||
*/
|
||||
initComponent(): void {
|
||||
if (hasValue(this.correctionTypeId)) {
|
||||
this.correctionTypeDataService.getCorrectionTypeById(this.correctionTypeId)
|
||||
.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
)
|
||||
.subscribe((correctionType: CorrectionType) => {
|
||||
if (hasValue(correctionType)) {
|
||||
this.correctionTypeObject = correctionType;
|
||||
this.creationForm = correctionType.creationForm;
|
||||
this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
|
||||
this.injectData();
|
||||
this.chd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the data into the component
|
||||
*/
|
||||
private injectData(): void {
|
||||
this.objectInjector = Injector.create({
|
||||
providers: [
|
||||
{ provide: 'correctionTypeObjectProvider', useValue: this.correctionTypeObject, deps: [] },
|
||||
],
|
||||
parent: this.injector,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the component depending on the creation form
|
||||
* @returns {GenericConstructor<Component>}
|
||||
*/
|
||||
getComponent(): GenericConstructor<Component> {
|
||||
return getCorrectionComponent(this.creationForm);
|
||||
}
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CorrectionSuggestionComponent } from './correction-suggestion.component';
|
||||
import { SharedModule } from '../shared.module';
|
||||
import { CorrectionSuggestionRoutingModule } from './correction-suggestion-routing.module';
|
||||
import { ManageRelationCorrectionTypeComponent } from './correction-types/manage-relation-correction-type/manage-relation-correction-type.component';
|
||||
import { SearchModule } from '../search/search.module';
|
||||
|
||||
const COMPONENTS = [
|
||||
CorrectionSuggestionComponent,
|
||||
ManageRelationCorrectionTypeComponent,
|
||||
];
|
||||
|
||||
const ENTRY_COMPONENTS = [
|
||||
ManageRelationCorrectionTypeComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
COMPONENTS,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CorrectionSuggestionRoutingModule,
|
||||
SharedModule,
|
||||
SearchModule
|
||||
],
|
||||
exports: [
|
||||
COMPONENTS,
|
||||
]
|
||||
})
|
||||
export class CorrectionSuggestionModule {
|
||||
static withEntryComponents() {
|
||||
return {
|
||||
ngModule: CorrectionSuggestionModule,
|
||||
providers: ENTRY_COMPONENTS.map((component) => ({ provide: component }))
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
export enum CorrectionTypeForms {
|
||||
MANAGE_RELATION = 'manageRelation',
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
<div class="row justify-content-center">
|
||||
<h4>
|
||||
{{ ( 'correction-type.manage-relation.' + (correctionType.topic | lowercase) + '.searchHeader' | translate) }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="pt-3">
|
||||
<div id="project-entities" class="mb-3">
|
||||
|
||||
<div id="project-search" class="input-group mb-3">
|
||||
<input type="text" class="form-control" (keyup.enter)="search(projectTitle)" [(ngModel)]="projectTitle"
|
||||
[placeholder]="'correction-type.manage-relation.search.placeholder' | translate" aria-label="" aria-describedby="">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary" [disabled]="projectTitle === ''"
|
||||
(click)="projectTitle = ''; search('')">{{('correction-type.manage-relation.search.btn.clear' | translate)}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="projectTitle === ''"
|
||||
(click)="search(projectTitle)">{{('correction-type.manage-relation.search.btn.search' | translate)}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ds-loading *ngIf="(isLoading$ | async)" message="{{'loading.search-results' | translate}}"></ds-loading>
|
||||
|
||||
<ds-viewable-collection
|
||||
*ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0 && !(isLoading$ | async)"
|
||||
[config]="pagination"
|
||||
[sortConfig]="searchOptions?.sort"
|
||||
[objects]="(localEntitiesRD$ | async)"
|
||||
[hideGear]="true"
|
||||
[selectable]="true"
|
||||
[selectionConfig]="{ repeatable: false, listId: entityListId }"
|
||||
[linkType]="linkTypes.Link"
|
||||
[context]="context"
|
||||
[hidePaginationDetail]="false"
|
||||
(deselectObject)="deselectEntity()"
|
||||
(selectObject)="selectEntity($event)">
|
||||
</ds-viewable-collection>
|
||||
|
||||
<div *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length < 1 && !(isLoading$ | async)">
|
||||
<ds-alert [type]="'alert-info'">
|
||||
<p class="lead mb-0">{{( 'correction-type.manage-relation.search.notFound' | translate)}}</p>
|
||||
</ds-alert>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex-row d-flex justify-content-between">
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="back()">
|
||||
{{ ( 'correction-type.manage-relation.' + (correctionType.topic | lowercase) + '.cancel' | translate) }}
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary" [disabled]="selectedImportType === importType.None" (click)="performAction()">
|
||||
{{ ( 'correction-type.manage-relation.' + (correctionType.topic | lowercase) + '.submit' | translate) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,173 +0,0 @@
|
||||
import { ThemeService } from './../../../theme-support/theme.service';
|
||||
|
||||
import { SearchModule } from './../../../search/search.module';
|
||||
import { RouterMock } from './../../../mocks/router.mock';
|
||||
import { NotificationsService } from './../../../notifications/notifications.service';
|
||||
import { ItemDataService } from './../../../../core/data/item-data.service';
|
||||
import { OpenaireBrokerEventRestService } from './../../../../core/openaire/broker/events/openaire-broker-event-rest.service';
|
||||
import { Item } from './../../../../core/shared/item.model';
|
||||
import { SelectableListService } from './../../../object-list/selectable-list/selectable-list.service';
|
||||
import { OpenaireMockDspaceObject, getMockOpenaireBrokerEventRestService } from './../../../mocks/openaire.mock';
|
||||
import { PaginatedSearchOptions } from './../../../search/models/paginated-search-options.model';
|
||||
import { PaginationComponentOptions } from './../../../pagination/pagination-component-options.model';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ManageRelationCorrectionTypeComponent } from './manage-relation-correction-type.component';
|
||||
import { PageInfo } from './../../../../core/shared/page-info.model';
|
||||
import { buildPaginatedList } from './../../../../core/data/paginated-list.model';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from './../../../../shared/remote-data.utils';
|
||||
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { SearchService } from './../../../../core/shared/search/search.service';
|
||||
import { of } from 'rxjs';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MockActivatedRoute } from './../../../../shared/mocks/active-router.mock';
|
||||
import { NotificationsServiceStub } from './../../../../shared/testing/notifications-service.stub';
|
||||
import { getMockTranslateService } from './../../../../shared/mocks/translate.service.mock';
|
||||
import { TranslateLoaderMock } from './../../../../shared/mocks/translate-loader.mock';
|
||||
import { SearchServiceStub } from './../../../../shared/testing/search-service.stub';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { getMockThemeService } from './../../../../shared/mocks/theme-service.mock';
|
||||
import { ImportType } from './../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component';
|
||||
|
||||
describe('ManageRelationCorrectionTypeComponent', () => {
|
||||
let component: ManageRelationCorrectionTypeComponent;
|
||||
let compAsAny: any;
|
||||
let fixture: ComponentFixture<ManageRelationCorrectionTypeComponent>;
|
||||
|
||||
const correctionTypeMock = {
|
||||
id: 'addpersonalpath',
|
||||
topic: '/DSPACEUSERS/RELATIONADD/PERSONALPATH',
|
||||
discoveryConfiguration: 'RELATION.PersonPath.Items',
|
||||
creationForm: 'manageRelation',
|
||||
type: 'correctiontype',
|
||||
_links: {
|
||||
self: {
|
||||
href: 'https://rest.api/discover/configurations/addpersonalpath',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const pagination = Object.assign(
|
||||
new PaginationComponentOptions(), {
|
||||
id: 'correction-suggestion-manage-relation',
|
||||
pageSize: 3
|
||||
}
|
||||
);
|
||||
|
||||
const uuid = '123e4567-e89b-12d3-a456-426614174003';
|
||||
|
||||
const searchOptions = Object.assign(new PaginatedSearchOptions(
|
||||
{
|
||||
configuration: 'RELATION.PersonPath.Items',
|
||||
scope: '123e4567-e89b-12d3-a456-426614174013',
|
||||
pagination: pagination
|
||||
}
|
||||
));
|
||||
|
||||
const pageInfo = new PageInfo({
|
||||
elementsPerPage: 3,
|
||||
totalElements: 1,
|
||||
totalPages: 1,
|
||||
currentPage: 1
|
||||
});
|
||||
|
||||
const array = [
|
||||
OpenaireMockDspaceObject,
|
||||
];
|
||||
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||
|
||||
const searchServiceStub = Object.assign(new SearchServiceStub(), {
|
||||
search: () => of(paginatedListRD),
|
||||
});
|
||||
|
||||
const openaireBrokerEventRestServiceStub: any = getMockOpenaireBrokerEventRestService();
|
||||
|
||||
const itemDataService = {
|
||||
findById: (id: string) => createSuccessfulRemoteDataObject$(new Item())
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ManageRelationCorrectionTypeComponent, TestComponent ],
|
||||
imports: [
|
||||
CommonModule,
|
||||
BrowserModule,
|
||||
NoopAnimationsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
}),
|
||||
RouterTestingModule.withRoutes([]),
|
||||
SearchModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: 'correctionTypeObjectProvider', useValue: correctionTypeMock },
|
||||
{ provide: SearchService, useValue: searchServiceStub },
|
||||
{ provide: OpenaireBrokerEventRestService, useValue: openaireBrokerEventRestServiceStub },
|
||||
{ provide: SelectableListService, useValue: jasmine.createSpyObj('selectableListService', ['deselect', 'select', 'deselectAll']) },
|
||||
{ provide: ActivatedRoute, useValue: new MockActivatedRoute() },
|
||||
{ provide: ItemDataService, useValue: itemDataService },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
{ provide: Router, useValue: new RouterMock() },
|
||||
{ provide: TranslateService, useValue: getMockTranslateService() },
|
||||
{ provide: ThemeService, useValue: getMockThemeService() },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ManageRelationCorrectionTypeComponent);
|
||||
component = fixture.componentInstance;
|
||||
compAsAny = component as any;
|
||||
component.localEntitiesRD$ = createSuccessfulRemoteDataObject$(paginatedList);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('should call SearchService.search', () => {
|
||||
(searchServiceStub as any).search.and.returnValue(of(paginatedListRD));
|
||||
component.pagination = pagination;
|
||||
component.search('');
|
||||
expect(compAsAny.searchService.search).toHaveBeenCalledWith(searchOptions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectEntity', () => {
|
||||
const entity = Object.assign(new Item(), { uuid: uuid });
|
||||
beforeEach(() => {
|
||||
component.selectEntity(entity);
|
||||
});
|
||||
it('should set selected entity', () => {
|
||||
expect(component.selectedEntity).toBe(entity);
|
||||
});
|
||||
it('should set the import type to local entity', () => {
|
||||
expect(component.selectedImportType).toEqual(ImportType.LocalEntity);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
component = null;
|
||||
compAsAny = null;
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'ds-test-cmp',
|
||||
template: ``
|
||||
})
|
||||
class TestComponent {
|
||||
|
||||
}
|
@@ -1,278 +0,0 @@
|
||||
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||
import { OpenaireBrokerEventObject } from '../../../../core/openaire/broker/models/openaire-broker-event.model';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import {
|
||||
OpenaireBrokerEventRestService
|
||||
} from '../../../../core/openaire/broker/events/openaire-broker-event-rest.service';
|
||||
import { Context } from '../../../../core/shared/context.model';
|
||||
import { CollectionElementLinkType } from '../../../object-collection/collection-element-link.type';
|
||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||
import { SearchResult } from '../../../search/models/search-result.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||
import { PaginatedSearchOptions } from '../../../search/models/paginated-search-options.model';
|
||||
import { SelectableListService } from '../../../object-list/selectable-list/selectable-list.service';
|
||||
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||
import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model';
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { renderCorrectionFor } from '../../correction-suggestion-page.decorator';
|
||||
import { CorrectionType } from '../../../../core/submission/models/correction-type-mode.model';
|
||||
import { CorrectionTypeForms } from '../correction-type-forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { finalize, Observable, of as observableOf, Subscription, switchMap } from 'rxjs';
|
||||
import { hasValue, isNotEmpty } from '../../../empty.util';
|
||||
import { ListableObject } from '../../../object-collection/shared/listable-object.model';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import {
|
||||
ImportType
|
||||
} from '../../../../openaire/broker/project-entry-import-modal/project-entry-import-modal.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-manage-relation-correction-type',
|
||||
templateUrl: './manage-relation-correction-type.component.html',
|
||||
styleUrls: ['./manage-relation-correction-type.component.scss']
|
||||
})
|
||||
@renderCorrectionFor(CorrectionTypeForms.MANAGE_RELATION)
|
||||
export class ManageRelationCorrectionTypeComponent implements OnInit, OnDestroy {
|
||||
|
||||
/**
|
||||
* The correction type object
|
||||
*/
|
||||
correctionType: CorrectionType;
|
||||
|
||||
/**
|
||||
* The item uuid from the parent object
|
||||
*/
|
||||
itemUuid: string;
|
||||
|
||||
/**
|
||||
* The project title from the parent object
|
||||
*/
|
||||
projectTitle = '';
|
||||
|
||||
/**
|
||||
* Pagination options
|
||||
*/
|
||||
pagination: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||
id: 'csmr',
|
||||
pageSize: 10,
|
||||
currentPage: 1
|
||||
});
|
||||
|
||||
/**
|
||||
* Entities to show in the list
|
||||
*/
|
||||
localEntitiesRD$: Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>;
|
||||
|
||||
/**
|
||||
* Search options to use for fetching projects
|
||||
*/
|
||||
searchOptions: PaginatedSearchOptions;
|
||||
|
||||
/**
|
||||
* The list of subscriptions
|
||||
*/
|
||||
protected subs: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* Information about the data loading status
|
||||
*/
|
||||
isLoading$ = observableOf(true);
|
||||
|
||||
/**
|
||||
* The selected local entity
|
||||
*/
|
||||
selectedEntity: ListableObject;
|
||||
|
||||
/**
|
||||
* The type of link to render in listable elements
|
||||
*/
|
||||
linkTypes = CollectionElementLinkType;
|
||||
|
||||
/**
|
||||
* The context we're currently in (submission)
|
||||
*/
|
||||
context = Context.Search;
|
||||
|
||||
/**
|
||||
* List ID for selecting local entities
|
||||
*/
|
||||
entityListId = 'csmr';
|
||||
/**
|
||||
* List ID for selecting local authorities
|
||||
*/
|
||||
authorityListId = 'csmr-authority';
|
||||
|
||||
/**
|
||||
* ImportType enum
|
||||
*/
|
||||
importType = ImportType;
|
||||
|
||||
/**
|
||||
* The type of import the user currently has selected
|
||||
*/
|
||||
selectedImportType = ImportType.None;
|
||||
|
||||
constructor(
|
||||
@Inject('correctionTypeObjectProvider') private correctionTypeObject: CorrectionType,
|
||||
private searchService: SearchService,
|
||||
private selectService: SelectableListService,
|
||||
private aroute: ActivatedRoute,
|
||||
private openaireBrokerEventRestService: OpenaireBrokerEventRestService,
|
||||
private itemService: ItemDataService,
|
||||
private notificationsService: NotificationsService,
|
||||
private router: Router,
|
||||
private translateService: TranslateService,
|
||||
) {
|
||||
this.correctionType = correctionTypeObject;
|
||||
this.itemUuid = this.aroute.snapshot.params.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the search results
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
private getData(){
|
||||
this.localEntitiesRD$ = this.aroute.queryParams
|
||||
.pipe(
|
||||
switchMap((params) => {
|
||||
if (hasValue(params)) {
|
||||
this.pagination = Object.assign(new PaginationComponentOptions(),{
|
||||
...this.pagination,
|
||||
currentPage: params[`${this.pagination.id}.page`] || 1
|
||||
});
|
||||
}
|
||||
|
||||
this.searchOptions = Object.assign(new PaginatedSearchOptions(
|
||||
{
|
||||
configuration: this.correctionType.discoveryConfiguration,
|
||||
scope: this.itemUuid,
|
||||
pagination: this.pagination,
|
||||
}
|
||||
));
|
||||
|
||||
return this.searchService.search(this.searchOptions).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
finalize(() => this.isLoading$ = observableOf(false))
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a project search by title.
|
||||
*/
|
||||
public search(searchTitle): void {
|
||||
if (isNotEmpty(searchTitle)) {
|
||||
const filterRegEx = /[:]/g;
|
||||
this.isLoading$ = observableOf(true);
|
||||
this.searchOptions = Object.assign(new PaginatedSearchOptions(
|
||||
{
|
||||
configuration: this.correctionType.discoveryConfiguration,
|
||||
query: (searchTitle) ? searchTitle.replace(filterRegEx, '') : searchTitle,
|
||||
scope: this.itemUuid,
|
||||
pagination: this.pagination
|
||||
}
|
||||
));
|
||||
} else {
|
||||
this.searchOptions = Object.assign(new PaginatedSearchOptions(
|
||||
{
|
||||
configuration: this.correctionType.discoveryConfiguration,
|
||||
scope: this.itemUuid,
|
||||
pagination: this.pagination
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
this.localEntitiesRD$ = this.searchService.search(this.searchOptions);
|
||||
this.subs.push(
|
||||
this.localEntitiesRD$.pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
).subscribe(
|
||||
() => this.isLoading$ = observableOf(false)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselected a local entity
|
||||
*/
|
||||
public deselectEntity(): void {
|
||||
this.selectedEntity = undefined;
|
||||
if (this.selectedImportType === ImportType.LocalEntity) {
|
||||
this.selectedImportType = ImportType.None;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected a local entity
|
||||
* @param entity
|
||||
*/
|
||||
public selectEntity(entity): void {
|
||||
this.selectedEntity = entity;
|
||||
this.selectedImportType = ImportType.LocalEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselect every element from both entity and authority lists
|
||||
*/
|
||||
public deselectAllLists(): void {
|
||||
this.selectService.deselectAll(this.entityListId);
|
||||
this.selectService.deselectAll(this.authorityListId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action based on the correction type
|
||||
* by posting the data to the OpenAIRE Broker Event API.
|
||||
* Data is formatted as follows, in the exact order:
|
||||
* <item link>
|
||||
* <selected entity link>
|
||||
* <correction type link>
|
||||
*/
|
||||
performAction() {
|
||||
if (hasValue(this.selectedEntity)) {
|
||||
const selectedItemLink = (this.selectedEntity as SearchResult<DSpaceObject>).indexableObject._links.self.href;
|
||||
|
||||
this.itemService.findById(this.itemUuid).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
switchMap((item: Item) => {
|
||||
const data: string = this.correctionTypeObject._links.self.href + '\n' + item._links.self.href + '\n' + selectedItemLink;
|
||||
return this.openaireBrokerEventRestService.postData(data);
|
||||
}),
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe((res: RemoteData<OpenaireBrokerEventObject>) => {
|
||||
if (res.hasSucceeded) {
|
||||
this.selectedImportType = ImportType.None;
|
||||
const message = 'correction-type.manage-relation.' + this.correctionTypeObject.id + '.notification.success';
|
||||
this.notificationsService.success(this.translateService.get(message));
|
||||
this.deselectAllLists();
|
||||
this.back();
|
||||
} else {
|
||||
this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate back to the previous page
|
||||
*/
|
||||
back() {
|
||||
this.router.navigate(['../../'], { relativeTo: this.aroute });
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all subscriptions.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.deselectAllLists();
|
||||
this.subs
|
||||
.filter((sub) => hasValue(sub))
|
||||
.forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<div *ngIf="!(this.submitted$ | async); else waiting">
|
||||
<div *ngIf="(this.canWithdraw$ | async); else reinstateHeader" class="modal-header">
|
||||
{{'item.qa.withdrawn.modal.header' | translate}}
|
||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="summary">{{'item.qa-withdrawn-reinstate.create.modal.form.summary.label' | translate }}:</label>
|
||||
<input type="text" id="summary" class="form-control" [(ngModel)]="summary"
|
||||
(keyup.enter)="onModalSubmit()"
|
||||
placeholder="{{'item.qa.withdrown-reinstate.modal.form.summary.placeholder' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer space-children-mr">
|
||||
<button class="btn btn-outline-secondary btn-sm ml-0"
|
||||
type="button"
|
||||
(click)="onModalClose()"
|
||||
title="{{'item.qa.withdrawn-reinstate.create.modal.button.cancel.tooltip' | translate}}">
|
||||
<i class="fas fa-times fa-fw"></i> {{'item.qa.withdrawn-reinstate.create.modal.button.cancel' | translate}}
|
||||
</button>
|
||||
<button class="btn btn-success btn-sm ml-0"
|
||||
type="submit"
|
||||
(click)="onModalSubmit()"
|
||||
title="{{'item.qa.withdrawn-reinstate.modal.button.confirm.tooltip' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i> {{'item.qa.withdrown-reinstate.create.modal.button.confirm' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #waiting>
|
||||
<div class="modal-header">{{'item.qa.withdrawn.modal.submitted.header' | translate}}</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex justify-content-center">
|
||||
<ds-loading [showMessage]="false"></ds-loading>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #reinstateHeader>
|
||||
<div *ngIf="this.canReinstate$ | async" class="modal-header">
|
||||
{{'item.qa.reinstate.modal.header' | translate}}
|
||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
@@ -0,0 +1,47 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ModalBeforeDismiss } from '../interfaces/modal-before-dismiss.interface';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-withdrawn-reinstate-modal',
|
||||
templateUrl: './item-withdrawn-reinstate-modal.component.html',
|
||||
styleUrls: ['./item-withdrawn-reinstate-modal.component.scss']
|
||||
})
|
||||
export class ItemWithdrawnReinstateModalComponent implements ModalBeforeDismiss {
|
||||
|
||||
summary: string;
|
||||
|
||||
canWithdraw$: Observable<boolean> = of(false);
|
||||
canReinstate$: Observable<boolean> = of(false);
|
||||
submitted$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
@Output() createQAEvent: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
constructor(
|
||||
protected activeModal: NgbActiveModal,
|
||||
protected authorizationService: AuthorizationDataService
|
||||
) {}
|
||||
|
||||
onModalClose() {
|
||||
this.activeModal.close();
|
||||
}
|
||||
|
||||
beforeDismiss(): boolean {
|
||||
// prevent the modal from being dismissed after version creation is initiated
|
||||
return !this.submitted$.getValue();
|
||||
}
|
||||
|
||||
onModalSubmit() {
|
||||
this.submitted$.next(true);
|
||||
this.createQAEvent.emit(this.summary);
|
||||
}
|
||||
|
||||
public setDso(dso: DSpaceObject) {
|
||||
this.canWithdraw$ = this.authorizationService.isAuthorized(FeatureID.WithdrawItem, dso.self);
|
||||
this.canReinstate$ = this.authorizationService.isAuthorized(FeatureID.ReinstateItem, dso.self);
|
||||
}
|
||||
}
|
@@ -21,6 +21,8 @@ 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 { DsoWithdrawnModalService } from './dso-withdrawn-service/dso-withdrawn-modal.service';
|
||||
import { DsoReinstateModalService } from './dso-reinstate-service/dso-reinstate-modal.service';
|
||||
|
||||
/**
|
||||
* Creates the menus for the dspace object pages
|
||||
@@ -39,6 +41,8 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
protected researcherProfileService: ResearcherProfileDataService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
protected dsoWithdrawnModalService: DsoWithdrawnModalService,
|
||||
protected dsoReinstateModalService: DsoReinstateModalService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -125,8 +129,10 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
this.dsoVersioningModalService.getVersioningTooltipMessage(dso, 'item.page.version.hasDraft', 'item.page.version.create'),
|
||||
this.authorizationService.isAuthorized(FeatureID.CanSynchronizeWithORCID, dso.self),
|
||||
this.authorizationService.isAuthorized(FeatureID.CanClaimItem, dso.self),
|
||||
this.authorizationService.isAuthorized(FeatureID.WithdrawItem, dso.self),
|
||||
this.authorizationService.isAuthorized(FeatureID.ReinstateItem, dso.self),
|
||||
]).pipe(
|
||||
map(([canCreateVersion, disableVersioning, versionTooltip, canSynchronizeWithOrcid, canClaimItem]) => {
|
||||
map(([canCreateVersion, disableVersioning, versionTooltip, canSynchronizeWithOrcid, canClaimItem, canWithdrawItem, canReinstateItem]) => {
|
||||
const isPerson = this.getDsoType(dso) === 'person';
|
||||
return [
|
||||
{
|
||||
@@ -170,6 +176,32 @@ export class DSOEditMenuResolver implements Resolve<{ [key: string]: MenuSection
|
||||
icon: 'hand-paper',
|
||||
index: 3
|
||||
},
|
||||
{
|
||||
id: 'withdrawn-item',
|
||||
active: false,
|
||||
visible: canWithdrawItem,
|
||||
model: {
|
||||
type: MenuItemType.ONCLICK,
|
||||
function: () => {
|
||||
this.dsoWithdrawnModalService.openCreateWithdrawnModal(dso);
|
||||
}
|
||||
} as OnClickMenuItemModel,
|
||||
icon: 'lock',
|
||||
index: 4
|
||||
},
|
||||
{
|
||||
id: 'reinstate-item',
|
||||
active: false,
|
||||
visible: canReinstateItem,
|
||||
model: {
|
||||
type: MenuItemType.ONCLICK,
|
||||
function: () => {
|
||||
this.dsoReinstateModalService.openCreateReinstateModal(dso);
|
||||
}
|
||||
} as OnClickMenuItemModel,
|
||||
icon: 'unlock-keyhole',
|
||||
index: 5
|
||||
}
|
||||
];
|
||||
}),
|
||||
);
|
||||
|
@@ -0,0 +1,65 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
QualityAssuranceEventDataService
|
||||
} from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-data.service';
|
||||
import { ItemWithdrawnReinstateModalComponent } from '../../correction-suggestion/withdrawn-reinstate-modal.component';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import {
|
||||
QualityAssuranceEventObject
|
||||
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DsoReinstateModalService {
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
protected modalService: NgbModal,
|
||||
protected itemService: ItemDataService,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
protected qaEventDataService: QualityAssuranceEventDataService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Open the reinstate modal for the provided dso
|
||||
*/
|
||||
openCreateReinstateModal(dso): void {
|
||||
const selfHref = dso._links.self.href;
|
||||
|
||||
const data = 'https://localhost:8080/server/api/config/correctiontypes/reinstateRequest' + '\n' + selfHref;
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent);
|
||||
(activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).setDso(dso);
|
||||
(activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).submitted$
|
||||
.pipe(
|
||||
filter((val) => val)
|
||||
).subscribe(
|
||||
() => {
|
||||
this.sendQARequest(data);
|
||||
activeModal.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sendQARequest(data: string): void {
|
||||
this.qaEventDataService.postData(data)
|
||||
.subscribe((res: RemoteData<QualityAssuranceEventObject>) => {
|
||||
if (res.hasSucceeded) {
|
||||
const message = 'reinstate';
|
||||
this.notificationsService.success(this.translateService.get(message));
|
||||
} else {
|
||||
this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Router } from '@angular/router';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { ItemWithdrawnReinstateModalComponent } from '../../correction-suggestion/withdrawn-reinstate-modal.component';
|
||||
import {
|
||||
QualityAssuranceEventDataService
|
||||
} from '../../../core/suggestion-notifications/qa/events/quality-assurance-event-data.service';
|
||||
import {
|
||||
QualityAssuranceEventObject
|
||||
} from '../../../core/suggestion-notifications/qa/models/quality-assurance-event.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DsoWithdrawnModalService {
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
protected modalService: NgbModal,
|
||||
protected itemService: ItemDataService,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
protected qaEventDataService: QualityAssuranceEventDataService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Open the create withdrawn modal for the provided dso
|
||||
*/
|
||||
openCreateWithdrawnModal(dso): void {
|
||||
const selfHref = dso._links.self.href;
|
||||
|
||||
const data = 'https://localhost:8080/server/api/config/correctiontypes/withdrawnRequest' + '\n' + selfHref;
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemWithdrawnReinstateModalComponent);
|
||||
(activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).setDso(dso);
|
||||
(activeModal.componentInstance as ItemWithdrawnReinstateModalComponent).submitted$
|
||||
.pipe(
|
||||
filter((val) => val)
|
||||
).subscribe(
|
||||
() => {
|
||||
this.sendQARequest(data);
|
||||
activeModal.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sendQARequest(data: string): void {
|
||||
this.qaEventDataService.postData(data)
|
||||
.subscribe((res: RemoteData<QualityAssuranceEventObject>) => {
|
||||
if (res.hasSucceeded) {
|
||||
const message = 'withdrawn';
|
||||
this.notificationsService.success(this.translateService.get(message));
|
||||
} else {
|
||||
this.notificationsService.error(this.translateService.get('correction-type.manage-relation.action.notification.error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -284,6 +284,9 @@ import {
|
||||
} from '../item-page/simple/field-components/specific-field/title/themed-item-page-field.component';
|
||||
import { BitstreamListItemComponent } from './object-list/bitstream-list-item/bitstream-list-item.component';
|
||||
import { NgxPaginationModule } from 'ngx-pagination';
|
||||
import {
|
||||
QualityAssuranceEventDataService
|
||||
} from '../core/suggestion-notifications/qa/events/quality-assurance-event-data.service';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
@@ -471,7 +474,8 @@ const ENTRY_COMPONENTS = [
|
||||
const PROVIDERS = [
|
||||
TruncatableService,
|
||||
MockAdminGuard,
|
||||
AbstractTrackableComponent
|
||||
AbstractTrackableComponent,
|
||||
QualityAssuranceEventDataService
|
||||
];
|
||||
|
||||
const DIRECTIVES = [
|
||||
|
@@ -2562,6 +2562,12 @@
|
||||
|
||||
"item.version.create.modal.header": "New version",
|
||||
|
||||
"item.qa.withdrawn.modal.header": "Send Withdrawn Item request",
|
||||
|
||||
"item.qa.reinstate.modal.header": "Send Reinstate Item request",
|
||||
|
||||
"item.qa.reinstate.create.modal.header": "New version",
|
||||
|
||||
"item.version.create.modal.text": "Create a new version for this item",
|
||||
|
||||
"item.version.create.modal.text.startingFrom": "starting from version {{version}}",
|
||||
@@ -2570,16 +2576,30 @@
|
||||
|
||||
"item.version.create.modal.button.confirm.tooltip": "Create new version",
|
||||
|
||||
"item.qa.withdrawn-reinstate.modal.button.confirm.tooltip": "Send request",
|
||||
|
||||
"item.qa.withdrown-reinstate.create.modal.button.confirm": "Send Request",
|
||||
|
||||
"item.version.create.modal.button.cancel": "Cancel",
|
||||
|
||||
"item.qa.withdrawn-reinstate.create.modal.button.cancel": "Cancel",
|
||||
|
||||
"item.version.create.modal.button.cancel.tooltip": "Do not create new version",
|
||||
|
||||
"item.qa.withdrawn-reinstate.create.modal.button.cancel.tooltip": "Do not send request",
|
||||
|
||||
"item.version.create.modal.form.summary.label": "Summary",
|
||||
|
||||
"item.qa-withdrawn-reinstate.create.modal.form.summary.label": "Summary",
|
||||
|
||||
"item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version",
|
||||
|
||||
"item.qa.withdrown-reinstate.modal.form.summary.placeholder": "Indicates the reasons (optionaly)",
|
||||
|
||||
"item.version.create.modal.submitted.header": "Creating new version...",
|
||||
|
||||
"item.qa.withdrawn.modal.submitted.header": "Sending withdrawn request...",
|
||||
|
||||
"item.version.create.modal.submitted.text": "The new version is being created. This may take some time if the item has a lot of relationships.",
|
||||
|
||||
"item.version.create.notification.success": "New version has been created with version number {{version}}",
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { CorrectionSuggestionModule } from './../../app/shared/correction-suggestion/correction-suggestion.module';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AdminRegistriesModule } from '../../app/admin/admin-registries/admin-registries.module';
|
||||
@@ -299,8 +298,7 @@ const DECLARATIONS = [
|
||||
SystemWideAlertModule,
|
||||
NgxGalleryModule,
|
||||
FormModule,
|
||||
RequestCopyModule,
|
||||
CorrectionSuggestionModule,
|
||||
RequestCopyModule
|
||||
],
|
||||
declarations: DECLARATIONS,
|
||||
exports: [
|
||||
|
Reference in New Issue
Block a user