mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1250 from 4Science/CST-4319
Cannot map new items with Collection Admin
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
[key]="'map'"
|
[key]="'map'"
|
||||||
[dsoRD$]="mappedItemsRD$"
|
[dsoRD$]="mappedItemsRD$"
|
||||||
[paginationOptions]="(searchOptions$ | async)?.pagination"
|
[paginationOptions]="(searchOptions$ | async)?.pagination"
|
||||||
|
[featureId]="FeatureIds.CanManageMappings"
|
||||||
[confirmButton]="'collection.edit.item-mapper.confirm'"
|
[confirmButton]="'collection.edit.item-mapper.confirm'"
|
||||||
[cancelButton]="'collection.edit.item-mapper.cancel'"
|
[cancelButton]="'collection.edit.item-mapper.cancel'"
|
||||||
(confirm)="mapItems($event)"
|
(confirm)="mapItems($event)"
|
||||||
|
@@ -40,6 +40,7 @@ import {
|
|||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
} from '../../shared/remote-data.utils';
|
} from '../../shared/remote-data.utils';
|
||||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
describe('CollectionItemMapperComponent', () => {
|
describe('CollectionItemMapperComponent', () => {
|
||||||
let comp: CollectionItemMapperComponent;
|
let comp: CollectionItemMapperComponent;
|
||||||
@@ -136,6 +137,10 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
@@ -152,6 +157,7 @@ describe('CollectionItemMapperComponent', () => {
|
|||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||||
{ provide: RouteService, useValue: routeServiceStub },
|
{ provide: RouteService, useValue: routeServiceStub },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@@ -28,6 +28,7 @@ import { PaginatedSearchOptions } from '../../shared/search/paginated-search-opt
|
|||||||
import { SearchService } from '../../core/shared/search/search.service';
|
import { SearchService } from '../../core/shared/search/search.service';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { NoContent } from '../../core/shared/NoContent.model';
|
import { NoContent } from '../../core/shared/NoContent.model';
|
||||||
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-item-mapper',
|
selector: 'ds-collection-item-mapper',
|
||||||
@@ -50,6 +51,8 @@ import { NoContent } from '../../core/shared/NoContent.model';
|
|||||||
*/
|
*/
|
||||||
export class CollectionItemMapperComponent implements OnInit {
|
export class CollectionItemMapperComponent implements OnInit {
|
||||||
|
|
||||||
|
FeatureIds = FeatureID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view on the tabset element
|
* A view on the tabset element
|
||||||
* Used to switch tabs programmatically
|
* Used to switch tabs programmatically
|
||||||
|
@@ -40,6 +40,7 @@ import {
|
|||||||
createSuccessfulRemoteDataObject$
|
createSuccessfulRemoteDataObject$
|
||||||
} from '../../../shared/remote-data.utils';
|
} from '../../../shared/remote-data.utils';
|
||||||
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
describe('ItemCollectionMapperComponent', () => {
|
describe('ItemCollectionMapperComponent', () => {
|
||||||
let comp: ItemCollectionMapperComponent;
|
let comp: ItemCollectionMapperComponent;
|
||||||
@@ -110,6 +111,10 @@ describe('ItemCollectionMapperComponent', () => {
|
|||||||
onDefaultLangChange: new EventEmitter()
|
onDefaultLangChange: new EventEmitter()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
|
||||||
@@ -124,7 +129,8 @@ describe('ItemCollectionMapperComponent', () => {
|
|||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
|
||||||
{ provide: TranslateService, useValue: translateServiceStub },
|
{ provide: TranslateService, useValue: translateServiceStub },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: CollectionDataService, useValue: collectionDataServiceStub }
|
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
@@ -13,11 +13,10 @@ import { CollectionSelectComponent } from './collection-select.component';
|
|||||||
import { Collection } from '../../../core/shared/collection.model';
|
import { Collection } from '../../../core/shared/collection.model';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||||
import { createPaginatedList } from '../../testing/utils.test';
|
import { createPaginatedList } from '../../testing/utils.test';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
|
||||||
import { FindListOptions } from '../../../core/data/request.models';
|
|
||||||
import { of as observableOf } from 'rxjs';
|
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../testing/pagination-service.stub';
|
||||||
|
import { of as observableOf } from 'rxjs/internal/observable/of';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
describe('CollectionSelectComponent', () => {
|
describe('CollectionSelectComponent', () => {
|
||||||
let comp: CollectionSelectComponent;
|
let comp: CollectionSelectComponent;
|
||||||
@@ -41,6 +40,10 @@ describe('CollectionSelectComponent', () => {
|
|||||||
currentPage: 1
|
currentPage: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const authorizationDataService = jasmine.createSpyObj('authorizationDataService', {
|
||||||
|
isAuthorized: observableOf(true)
|
||||||
|
});
|
||||||
|
|
||||||
const paginationService = new PaginationServiceStub();
|
const paginationService = new PaginationServiceStub();
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -49,7 +52,8 @@ describe('CollectionSelectComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockCollectionList[1].id]) },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockCollectionList[1].id]) },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: PaginationService, useValue: paginationService }
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -3,6 +3,7 @@ import { Collection } from '../../../core/shared/collection.model';
|
|||||||
import { ObjectSelectComponent } from '../object-select/object-select.component';
|
import { ObjectSelectComponent } from '../object-select/object-select.component';
|
||||||
import { isNotEmpty } from '../../empty.util';
|
import { isNotEmpty } from '../../empty.util';
|
||||||
import { ObjectSelectService } from '../object-select.service';
|
import { ObjectSelectService } from '../object-select.service';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-select',
|
selector: 'ds-collection-select',
|
||||||
@@ -14,8 +15,9 @@ import { ObjectSelectService } from '../object-select.service';
|
|||||||
*/
|
*/
|
||||||
export class CollectionSelectComponent extends ObjectSelectComponent<Collection> {
|
export class CollectionSelectComponent extends ObjectSelectComponent<Collection> {
|
||||||
|
|
||||||
constructor(protected objectSelectService: ObjectSelectService) {
|
constructor(protected objectSelectService: ObjectSelectService,
|
||||||
super(objectSelectService);
|
protected authorizationService: AuthorizationDataService) {
|
||||||
|
super(objectSelectService, authorizationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let item of itemsRD?.payload?.page">
|
<tr *ngFor="let item of itemsRD?.payload?.page">
|
||||||
<td><input class="item-checkbox" [ngModel]="getSelected(item.id) | async" (change)="switch(item.id)" type="checkbox" name="{{item.id}}"></td>
|
<td><input [disabled]="!(canSelect(item) | async)" class="item-checkbox" [ngModel]="getSelected(item.id) | async" (change)="switch(item.id)" type="checkbox" name="{{item.id}}"></td>
|
||||||
<td *ngIf="!hideCollection">
|
<td *ngIf="!hideCollection">
|
||||||
<span *ngVar="(item.owningCollection | async)?.payload as collection">
|
<span *ngVar="(item.owningCollection | async)?.payload as collection">
|
||||||
<a *ngIf="collection" [routerLink]="['/collections', collection?.id]">{{collection?.name}}</a>
|
<a *ngIf="collection" [routerLink]="['/collections', collection?.id]">{{collection?.name}}</a>
|
||||||
|
@@ -11,13 +11,13 @@ import { HostWindowService } from '../../host-window.service';
|
|||||||
import { HostWindowServiceStub } from '../../testing/host-window-service.stub';
|
import { HostWindowServiceStub } from '../../testing/host-window-service.stub';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { of as observableOf, of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||||
import { createPaginatedList } from '../../testing/utils.test';
|
import { createPaginatedList } from '../../testing/utils.test';
|
||||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||||
import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
|
|
||||||
import { FindListOptions } from '../../../core/data/request.models';
|
|
||||||
import { PaginationServiceStub } from '../../testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../testing/pagination-service.stub';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
|
|
||||||
describe('ItemSelectComponent', () => {
|
describe('ItemSelectComponent', () => {
|
||||||
let comp: ItemSelectComponent;
|
let comp: ItemSelectComponent;
|
||||||
@@ -39,7 +39,8 @@ describe('ItemSelectComponent', () => {
|
|||||||
key: 'dc.type',
|
key: 'dc.type',
|
||||||
language: null,
|
language: null,
|
||||||
value: 'Article'
|
value: 'Article'
|
||||||
}]
|
}],
|
||||||
|
_links: { self: { href: 'selfId1' } }
|
||||||
}),
|
}),
|
||||||
Object.assign(new Item(), {
|
Object.assign(new Item(), {
|
||||||
id: 'id2',
|
id: 'id2',
|
||||||
@@ -54,7 +55,8 @@ describe('ItemSelectComponent', () => {
|
|||||||
key: 'dc.type',
|
key: 'dc.type',
|
||||||
language: null,
|
language: null,
|
||||||
value: 'Article'
|
value: 'Article'
|
||||||
}]
|
}],
|
||||||
|
_links: { self: { href: 'selfId2' } }
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
const mockItems = createSuccessfulRemoteDataObject$(createPaginatedList(mockItemList));
|
const mockItems = createSuccessfulRemoteDataObject$(createPaginatedList(mockItemList));
|
||||||
@@ -66,6 +68,7 @@ describe('ItemSelectComponent', () => {
|
|||||||
|
|
||||||
paginationService = new PaginationServiceStub(mockPaginationOptions);
|
paginationService = new PaginationServiceStub(mockPaginationOptions);
|
||||||
|
|
||||||
|
const authorizationDataService = new AuthorizationDataService(null, null, null, null, null, null, null, null, null, null);
|
||||||
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
@@ -75,7 +78,8 @@ describe('ItemSelectComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockItemList[1].id]) },
|
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub([mockItemList[1].id]) },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: PaginationService, useValue: paginationService }
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
|
{ provide: AuthorizationDataService, useValue: authorizationDataService }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -146,4 +150,21 @@ describe('ItemSelectComponent', () => {
|
|||||||
expect(comp.cancel.emit).toHaveBeenCalled();
|
expect(comp.cancel.emit).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the authorize feature is not authorized', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.featureId = FeatureID.CanManageMappings;
|
||||||
|
spyOn(authorizationDataService, 'isAuthorized').and.returnValue(of(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable the checkbox', waitForAsync(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
const checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement;
|
||||||
|
expect(authorizationDataService.isAuthorized).toHaveBeenCalled();
|
||||||
|
expect(checkbox.disabled).toBeTrue();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
|
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-select',
|
selector: 'ds-item-select',
|
||||||
@@ -33,8 +34,9 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
|
|||||||
[itemId: string]: string
|
[itemId: string]: string
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
constructor(protected objectSelectService: ObjectSelectService) {
|
constructor(protected objectSelectService: ObjectSelectService,
|
||||||
super(objectSelectService);
|
protected authorizationService: AuthorizationDataService ) {
|
||||||
|
super(objectSelectService, authorizationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { take } from 'rxjs/operators';
|
import { startWith, take } from 'rxjs/operators';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
|
||||||
import { ObjectSelectService } from '../object-select.service';
|
import { ObjectSelectService } from '../object-select.service';
|
||||||
import { SortOptions } from '../../../core/cache/models/sort-options.model';
|
import { SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||||
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract component used to select DSpaceObjects from a specific list and returning the UUIDs of the selected DSpaceObjects
|
* An abstract component used to select DSpaceObjects from a specific list and returning the UUIDs of the selected DSpaceObjects
|
||||||
@@ -47,6 +51,12 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
|
|||||||
@Input()
|
@Input()
|
||||||
confirmButton: string;
|
confirmButton: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize check to enable the selection when present.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
featureId: FeatureID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The message key used for the cancel button
|
* The message key used for the cancel button
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -79,7 +89,8 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
|
|||||||
*/
|
*/
|
||||||
selectedIds$: Observable<string[]>;
|
selectedIds$: Observable<string[]>;
|
||||||
|
|
||||||
constructor(protected objectSelectService: ObjectSelectService) {
|
constructor(protected objectSelectService: ObjectSelectService,
|
||||||
|
protected authorizationService: AuthorizationDataService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -107,6 +118,16 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
|
|||||||
return this.objectSelectService.getSelected(this.key, id);
|
return this.objectSelectService.getSelected(this.key, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the item can be selected or not due to authorization check.
|
||||||
|
*/
|
||||||
|
canSelect(item: DSpaceObject): Observable<boolean> {
|
||||||
|
if (!this.featureId) {
|
||||||
|
return of(true);
|
||||||
|
}
|
||||||
|
return this.authorizationService.isAuthorized(this.featureId, item.self).pipe(startWith(false));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the confirm button is pressed
|
* Called when the confirm button is pressed
|
||||||
* Sends the selected UUIDs to the parent component
|
* Sends the selected UUIDs to the parent component
|
||||||
|
Reference in New Issue
Block a user