mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge remote-tracking branch 'origin/main' into CST-6685
This commit is contained in:
@@ -162,6 +162,9 @@ languages:
|
|||||||
- code: bn
|
- code: bn
|
||||||
label: বাংলা
|
label: বাংলা
|
||||||
active: true
|
active: true
|
||||||
|
- code: el
|
||||||
|
label: Ελληνικά
|
||||||
|
active: true
|
||||||
|
|
||||||
# Browse-By Pages
|
# Browse-By Pages
|
||||||
browseBy:
|
browseBy:
|
||||||
@@ -171,6 +174,25 @@ browseBy:
|
|||||||
fiveYearLimit: 30
|
fiveYearLimit: 30
|
||||||
# The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
# The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
||||||
defaultLowerLimit: 1900
|
defaultLowerLimit: 1900
|
||||||
|
# The number of entries in a paginated browse results list.
|
||||||
|
# Rounded to the nearest size in the list of selectable sizes on the
|
||||||
|
# settings menu.
|
||||||
|
pageSize: 20
|
||||||
|
|
||||||
|
communityList:
|
||||||
|
# No. of communities to list per expansion (show more)
|
||||||
|
pageSize: 20
|
||||||
|
|
||||||
|
homePage:
|
||||||
|
recentSubmissions:
|
||||||
|
# The number of item showing in recent submission components
|
||||||
|
pageSize: 5
|
||||||
|
# Sort record of recent submission
|
||||||
|
sortField: 'dc.date.accessioned'
|
||||||
|
topLevelCommunityList:
|
||||||
|
# No. of communities to list per page on the home page
|
||||||
|
# This will always round to the nearest number from the list of page sizes. e.g. if you set it to 7 it'll use 10
|
||||||
|
pageSize: 5
|
||||||
|
|
||||||
# Item Config
|
# Item Config
|
||||||
item:
|
item:
|
||||||
@@ -261,10 +283,3 @@ mediaViewer:
|
|||||||
info:
|
info:
|
||||||
enableEndUserAgreement: true
|
enableEndUserAgreement: true
|
||||||
enablePrivacyStatement: true
|
enablePrivacyStatement: true
|
||||||
# Home Page
|
|
||||||
homePage:
|
|
||||||
recentSubmissions:
|
|
||||||
# The number of item showing in recent submission components
|
|
||||||
pageSize: 5
|
|
||||||
# Sort record of recent submission
|
|
||||||
sortField: 'dc.date.accessioned'
|
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
ObservedValueOf,
|
ObservedValueOf,
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { map, mergeMap, switchMap, take } from 'rxjs/operators';
|
import { defaultIfEmpty, map, mergeMap, switchMap, take } from 'rxjs/operators';
|
||||||
import {buildPaginatedList, PaginatedList} from '../../../../core/data/paginated-list.model';
|
import {buildPaginatedList, PaginatedList} from '../../../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
|
||||||
@@ -144,7 +144,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => {
|
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => {
|
||||||
const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => {
|
const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => {
|
||||||
const dto$: Observable<EpersonDtoModel> = observableCombineLatest(
|
const dto$: Observable<EpersonDtoModel> = observableCombineLatest(
|
||||||
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
|
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
|
||||||
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
|
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
|
||||||
@@ -153,8 +153,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
return epersonDtoModel;
|
return epersonDtoModel;
|
||||||
});
|
});
|
||||||
return dto$;
|
return dto$;
|
||||||
}));
|
})]);
|
||||||
return dtos$.pipe(map((dtos: EpersonDtoModel[]) => {
|
return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
|
||||||
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
|
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
|
||||||
}));
|
}));
|
||||||
}))
|
}))
|
||||||
@@ -174,7 +174,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
return this.ePersonDataService.findListByHref(group._links.epersons.href, {
|
return this.ePersonDataService.findListByHref(group._links.epersons.href, {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
elementsPerPage: 9999
|
elementsPerPage: 9999
|
||||||
}, false)
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstSucceededRemoteData(),
|
||||||
getRemoteDataPayload(),
|
getRemoteDataPayload(),
|
||||||
@@ -274,7 +274,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => {
|
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => {
|
||||||
const dtos$ = observableCombineLatest(...epersonListRD.payload.page.map((member: EPerson) => {
|
const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => {
|
||||||
const dto$: Observable<EpersonDtoModel> = observableCombineLatest(
|
const dto$: Observable<EpersonDtoModel> = observableCombineLatest(
|
||||||
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
|
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
|
||||||
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
|
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
|
||||||
@@ -283,8 +283,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
return epersonDtoModel;
|
return epersonDtoModel;
|
||||||
});
|
});
|
||||||
return dto$;
|
return dto$;
|
||||||
}));
|
})]);
|
||||||
return dtos$.pipe(map((dtos: EpersonDtoModel[]) => {
|
return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
|
||||||
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
|
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
|
||||||
}));
|
}));
|
||||||
}))
|
}))
|
||||||
|
@@ -23,6 +23,8 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
|
|||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
import { APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
describe('BrowseByDatePageComponent', () => {
|
describe('BrowseByDatePageComponent', () => {
|
||||||
let comp: BrowseByDatePageComponent;
|
let comp: BrowseByDatePageComponent;
|
||||||
@@ -83,7 +85,8 @@ describe('BrowseByDatePageComponent', () => {
|
|||||||
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
||||||
{ provide: Router, useValue: new RouterMock() },
|
{ provide: Router, useValue: new RouterMock() },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: ChangeDetectorRef, useValue: mockCdRef }
|
{ provide: ChangeDetectorRef, useValue: mockCdRef },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
BrowseByMetadataPageComponent,
|
BrowseByMetadataPageComponent,
|
||||||
browseParamsToOptions
|
browseParamsToOptions
|
||||||
@@ -19,6 +19,7 @@ import { map } from 'rxjs/operators';
|
|||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { isValidDate } from '../../shared/date.util';
|
import { isValidDate } from '../../shared/date.util';
|
||||||
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-browse-by-date-page',
|
selector: 'ds-browse-by-date-page',
|
||||||
@@ -43,8 +44,9 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
protected dsoService: DSpaceObjectDataService,
|
protected dsoService: DSpaceObjectDataService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
protected paginationService: PaginationService,
|
protected paginationService: PaginationService,
|
||||||
protected cdRef: ChangeDetectorRef) {
|
protected cdRef: ChangeDetectorRef,
|
||||||
super(route, browseService, dsoService, paginationService, router);
|
@Inject(APP_CONFIG) protected appConfig: AppConfig) {
|
||||||
|
super(route, browseService, dsoService, paginationService, router, appConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -25,6 +25,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.util
|
|||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
|
import { APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
describe('BrowseByMetadataPageComponent', () => {
|
describe('BrowseByMetadataPageComponent', () => {
|
||||||
let comp: BrowseByMetadataPageComponent;
|
let comp: BrowseByMetadataPageComponent;
|
||||||
@@ -97,7 +99,8 @@ describe('BrowseByMetadataPageComponent', () => {
|
|||||||
{ provide: BrowseService, useValue: mockBrowseService },
|
{ provide: BrowseService, useValue: mockBrowseService },
|
||||||
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: Router, useValue: new RouterMock() }
|
{ provide: Router, useValue: new RouterMock() },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
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 '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
@@ -26,9 +27,10 @@ export const BBM_PAGINATION_ID = 'bbm';
|
|||||||
templateUrl: './browse-by-metadata-page.component.html'
|
templateUrl: './browse-by-metadata-page.component.html'
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Component for browsing (items) by metadata definition
|
* Component for browsing (items) by metadata definition.
|
||||||
* A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields.
|
* A metadata definition (a.k.a. browse id) is a short term used to describe one
|
||||||
* An example would be 'author' for 'dc.contributor.*'
|
* or multiple metadata fields. An example would be 'author' for
|
||||||
|
* 'dc.contributor.*'
|
||||||
*/
|
*/
|
||||||
@rendersBrowseBy(BrowseByDataType.Metadata)
|
@rendersBrowseBy(BrowseByDataType.Metadata)
|
||||||
export class BrowseByMetadataPageComponent implements OnInit {
|
export class BrowseByMetadataPageComponent implements OnInit {
|
||||||
@@ -51,11 +53,7 @@ export class BrowseByMetadataPageComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The pagination config used to display the values
|
* The pagination config used to display the values
|
||||||
*/
|
*/
|
||||||
paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
paginationConfig: PaginationComponentOptions;
|
||||||
id: BBM_PAGINATION_ID,
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 20
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pagination observable
|
* The pagination observable
|
||||||
@@ -115,8 +113,14 @@ export class BrowseByMetadataPageComponent implements OnInit {
|
|||||||
protected browseService: BrowseService,
|
protected browseService: BrowseService,
|
||||||
protected dsoService: DSpaceObjectDataService,
|
protected dsoService: DSpaceObjectDataService,
|
||||||
protected paginationService: PaginationService,
|
protected paginationService: PaginationService,
|
||||||
protected router: Router) {
|
protected router: Router,
|
||||||
}
|
@Inject(APP_CONFIG) protected appConfig: AppConfig) {
|
||||||
|
this.paginationConfig = Object.assign(new PaginationComponentOptions(), {
|
||||||
|
id: BBM_PAGINATION_ID,
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: this.appConfig.browseBy.pageSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
||||||
|
@@ -23,6 +23,8 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
|
|||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
import { FindListOptions } from '../../core/data/find-list-options.model';
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
||||||
|
import { APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
describe('BrowseByTitlePageComponent', () => {
|
describe('BrowseByTitlePageComponent', () => {
|
||||||
let comp: BrowseByTitlePageComponent;
|
let comp: BrowseByTitlePageComponent;
|
||||||
@@ -77,7 +79,8 @@ describe('BrowseByTitlePageComponent', () => {
|
|||||||
{ provide: BrowseService, useValue: mockBrowseService },
|
{ provide: BrowseService, useValue: mockBrowseService },
|
||||||
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: Router, useValue: new RouterMock() }
|
{ provide: Router, useValue: new RouterMock() },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Component } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import {
|
import {
|
||||||
@@ -14,6 +14,7 @@ import { BrowseByDataType, rendersBrowseBy } from '../browse-by-switcher/browse-
|
|||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-browse-by-title-page',
|
selector: 'ds-browse-by-title-page',
|
||||||
@@ -30,8 +31,9 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
protected browseService: BrowseService,
|
protected browseService: BrowseService,
|
||||||
protected dsoService: DSpaceObjectDataService,
|
protected dsoService: DSpaceObjectDataService,
|
||||||
protected paginationService: PaginationService,
|
protected paginationService: PaginationService,
|
||||||
protected router: Router) {
|
protected router: Router,
|
||||||
super(route, browseService, dsoService, paginationService, router);
|
@Inject(APP_CONFIG) protected appConfig: AppConfig) {
|
||||||
|
super(route, browseService, dsoService, paginationService, router, appConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -15,6 +15,8 @@ import { Collection } from '../core/shared/collection.model';
|
|||||||
import { PageInfo } from '../core/shared/page-info.model';
|
import { PageInfo } from '../core/shared/page-info.model';
|
||||||
import { FlatNode } from './flat-node.model';
|
import { FlatNode } from './flat-node.model';
|
||||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||||
|
import { APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
import { environment } from 'src/environments/environment.test';
|
||||||
|
|
||||||
describe('CommunityListService', () => {
|
describe('CommunityListService', () => {
|
||||||
let store: StoreMock<AppState>;
|
let store: StoreMock<AppState>;
|
||||||
@@ -191,13 +193,14 @@ describe('CommunityListService', () => {
|
|||||||
};
|
};
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [CommunityListService,
|
providers: [CommunityListService,
|
||||||
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
|
||||||
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
||||||
{ provide: Store, useValue: StoreMock },
|
{ provide: Store, useValue: StoreMock },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
store = TestBed.inject(Store as any);
|
store = TestBed.inject(Store as any);
|
||||||
service = new CommunityListService(communityDataServiceStub, collectionDataServiceStub, store);
|
service = new CommunityListService(environment, communityDataServiceStub, collectionDataServiceStub, store);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', inject([CommunityListService], (serviceIn: CommunityListService) => {
|
it('should create', inject([CommunityListService], (serviceIn: CommunityListService) => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
import { Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { createSelector, Store } from '@ngrx/store';
|
import { createSelector, Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||||
@@ -23,6 +23,7 @@ import { followLink } from '../shared/utils/follow-link-config.model';
|
|||||||
import { FlatNode } from './flat-node.model';
|
import { FlatNode } from './flat-node.model';
|
||||||
import { ShowMoreFlatNode } from './show-more-flat-node.model';
|
import { ShowMoreFlatNode } from './show-more-flat-node.model';
|
||||||
import { FindListOptions } from '../core/data/find-list-options.model';
|
import { FindListOptions } from '../core/data/find-list-options.model';
|
||||||
|
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
|
||||||
// Helper method to combine an flatten an array of observables of flatNode arrays
|
// Helper method to combine an flatten an array of observables of flatNode arrays
|
||||||
export const combineAndFlatten = (obsList: Observable<FlatNode[]>[]): Observable<FlatNode[]> =>
|
export const combineAndFlatten = (obsList: Observable<FlatNode[]>[]): Observable<FlatNode[]> =>
|
||||||
@@ -80,8 +81,6 @@ const communityListStateSelector = (state: AppState) => state.communityList;
|
|||||||
const expandedNodesSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.expandedNodes);
|
const expandedNodesSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.expandedNodes);
|
||||||
const loadingNodeSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.loadingNode);
|
const loadingNodeSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.loadingNode);
|
||||||
|
|
||||||
export const MAX_COMCOLS_PER_PAGE = 20;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
|
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
|
||||||
* and connection to the store to retrieve and save the state of the community list
|
* and connection to the store to retrieve and save the state of the community list
|
||||||
@@ -89,8 +88,15 @@ export const MAX_COMCOLS_PER_PAGE = 20;
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommunityListService {
|
export class CommunityListService {
|
||||||
|
|
||||||
constructor(private communityDataService: CommunityDataService, private collectionDataService: CollectionDataService,
|
private pageSize: number;
|
||||||
private store: Store<any>) {
|
|
||||||
|
constructor(
|
||||||
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
private communityDataService: CommunityDataService,
|
||||||
|
private collectionDataService: CollectionDataService,
|
||||||
|
private store: Store<any>
|
||||||
|
) {
|
||||||
|
this.pageSize = appConfig.communityList.pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private configOnePage: FindListOptions = Object.assign(new FindListOptions(), {
|
private configOnePage: FindListOptions = Object.assign(new FindListOptions(), {
|
||||||
@@ -145,7 +151,7 @@ export class CommunityListService {
|
|||||||
private getTopCommunities(options: FindListOptions): Observable<PaginatedList<Community>> {
|
private getTopCommunities(options: FindListOptions): Observable<PaginatedList<Community>> {
|
||||||
return this.communityDataService.findTop({
|
return this.communityDataService.findTop({
|
||||||
currentPage: options.currentPage,
|
currentPage: options.currentPage,
|
||||||
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
elementsPerPage: this.pageSize,
|
||||||
sort: {
|
sort: {
|
||||||
field: options.sort.field,
|
field: options.sort.field,
|
||||||
direction: options.sort.direction
|
direction: options.sort.direction
|
||||||
@@ -216,7 +222,7 @@ export class CommunityListService {
|
|||||||
let subcoms = [];
|
let subcoms = [];
|
||||||
for (let i = 1; i <= currentCommunityPage; i++) {
|
for (let i = 1; i <= currentCommunityPage; i++) {
|
||||||
const nextSetOfSubcommunitiesPage = this.communityDataService.findByParent(community.uuid, {
|
const nextSetOfSubcommunitiesPage = this.communityDataService.findByParent(community.uuid, {
|
||||||
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
elementsPerPage: this.pageSize,
|
||||||
currentPage: i
|
currentPage: i
|
||||||
},
|
},
|
||||||
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
followLink('subcommunities', { findListOptions: this.configOnePage }),
|
||||||
@@ -241,7 +247,7 @@ export class CommunityListService {
|
|||||||
let collections = [];
|
let collections = [];
|
||||||
for (let i = 1; i <= currentCollectionPage; i++) {
|
for (let i = 1; i <= currentCollectionPage; i++) {
|
||||||
const nextSetOfCollectionsPage = this.collectionDataService.findByParent(community.uuid, {
|
const nextSetOfCollectionsPage = this.collectionDataService.findByParent(community.uuid, {
|
||||||
elementsPerPage: MAX_COMCOLS_PER_PAGE,
|
elementsPerPage: this.pageSize,
|
||||||
currentPage: i
|
currentPage: i
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
|
@@ -25,12 +25,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="comcol-page-browse-section">
|
<section class="comcol-page-browse-section">
|
||||||
|
|
||||||
<!-- Browse-By Links -->
|
<!-- Browse-By Links -->
|
||||||
<ds-themed-comcol-page-browse-by [id]="communityPayload.id" [contentType]="communityPayload.type">
|
<ds-themed-comcol-page-browse-by [id]="communityPayload.id" [contentType]="communityPayload.type">
|
||||||
</ds-themed-comcol-page-browse-by>
|
</ds-themed-comcol-page-browse-by>
|
||||||
|
|
||||||
<ds-community-page-sub-community-list [community]="communityPayload"></ds-community-page-sub-community-list>
|
<ds-themed-community-page-sub-community-list [community]="communityPayload"></ds-themed-community-page-sub-community-list>
|
||||||
<ds-community-page-sub-collection-list [community]="communityPayload"></ds-community-page-sub-collection-list>
|
<ds-themed-community-page-sub-collection-list [community]="communityPayload"></ds-themed-community-page-sub-collection-list>
|
||||||
</section>
|
</section>
|
||||||
<footer *ngIf="communityPayload.copyrightText" class="border-top my-5 pt-4">
|
<footer *ngIf="communityPayload.copyrightText" class="border-top my-5 pt-4">
|
||||||
<!-- Copyright -->
|
<!-- Copyright -->
|
||||||
|
@@ -13,10 +13,18 @@ import { StatisticsModule } from '../statistics/statistics.module';
|
|||||||
import { CommunityFormModule } from './community-form/community-form.module';
|
import { CommunityFormModule } from './community-form/community-form.module';
|
||||||
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
import { ThemedCommunityPageComponent } from './themed-community-page.component';
|
||||||
import { ComcolModule } from '../shared/comcol/comcol.module';
|
import { ComcolModule } from '../shared/comcol/comcol.module';
|
||||||
|
import {
|
||||||
|
ThemedCommunityPageSubCommunityListComponent
|
||||||
|
} from './sub-community-list/themed-community-page-sub-community-list.component';
|
||||||
|
import {
|
||||||
|
ThemedCollectionPageSubCollectionListComponent
|
||||||
|
} from './sub-collection-list/themed-community-page-sub-collection-list.component';
|
||||||
|
|
||||||
const DECLARATIONS = [CommunityPageComponent,
|
const DECLARATIONS = [CommunityPageComponent,
|
||||||
ThemedCommunityPageComponent,
|
ThemedCommunityPageComponent,
|
||||||
|
ThemedCommunityPageSubCommunityListComponent,
|
||||||
CommunityPageSubCollectionListComponent,
|
CommunityPageSubCollectionListComponent,
|
||||||
|
ThemedCollectionPageSubCollectionListComponent,
|
||||||
CommunityPageSubCommunityListComponent,
|
CommunityPageSubCommunityListComponent,
|
||||||
CreateCommunityPageComponent,
|
CreateCommunityPageComponent,
|
||||||
DeleteCommunityPageComponent];
|
DeleteCommunityPageComponent];
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import { SortDirection, SortOptions } from '../../core/cache/models/sort-options
|
|||||||
import { CollectionDataService } from '../../core/data/collection-data.service';
|
import { CollectionDataService } from '../../core/data/collection-data.service';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-page-sub-collection-list',
|
selector: 'ds-community-page-sub-collection-list',
|
||||||
@@ -19,9 +20,15 @@ import { switchMap } from 'rxjs/operators';
|
|||||||
templateUrl: './community-page-sub-collection-list.component.html',
|
templateUrl: './community-page-sub-collection-list.component.html',
|
||||||
animations:[fadeIn]
|
animations:[fadeIn]
|
||||||
})
|
})
|
||||||
export class CommunityPageSubCollectionListComponent implements OnInit {
|
export class CommunityPageSubCollectionListComponent implements OnInit, OnDestroy {
|
||||||
@Input() community: Community;
|
@Input() community: Community;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional page size. Overrides communityList.pageSize configuration for this component.
|
||||||
|
* Value can be added in the themed version of the parent component.
|
||||||
|
*/
|
||||||
|
@Input() pageSize: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pagination configuration
|
* The pagination configuration
|
||||||
*/
|
*/
|
||||||
@@ -50,7 +57,9 @@ export class CommunityPageSubCollectionListComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.config = new PaginationComponentOptions();
|
this.config = new PaginationComponentOptions();
|
||||||
this.config.id = this.pageId;
|
this.config.id = this.pageId;
|
||||||
this.config.pageSize = 5;
|
if (hasValue(this.pageSize)) {
|
||||||
|
this.config.pageSize = this.pageSize;
|
||||||
|
}
|
||||||
this.config.currentPage = 1;
|
this.config.currentPage = 1;
|
||||||
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
||||||
this.initPage();
|
this.initPage();
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||||
|
import { CommunityPageSubCollectionListComponent } from './community-page-sub-collection-list.component';
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Community } from '../../core/shared/community.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-community-page-sub-collection-list',
|
||||||
|
styleUrls: [],
|
||||||
|
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||||
|
})
|
||||||
|
export class ThemedCollectionPageSubCollectionListComponent extends ThemedComponent<CommunityPageSubCollectionListComponent> {
|
||||||
|
@Input() community: Community;
|
||||||
|
@Input() pageSize: number;
|
||||||
|
protected inAndOutputNames: (keyof CommunityPageSubCollectionListComponent & keyof this)[] = ['community', 'pageSize'];
|
||||||
|
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return 'CommunityPageSubCollectionListComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return import(`../../../themes/${themeName}/app/community-page/sub-collection-list/community-page-sub-collection-list.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return import(`./community-page-sub-collection-list.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
|||||||
import { takeUntilCompletedRemoteData } from '../../core/shared/operators';
|
import { takeUntilCompletedRemoteData } from '../../core/shared/operators';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-community-page-sub-community-list',
|
selector: 'ds-community-page-sub-community-list',
|
||||||
@@ -22,9 +23,15 @@ import { PaginationService } from '../../core/pagination/pagination.service';
|
|||||||
/**
|
/**
|
||||||
* Component to render the sub-communities of a Community
|
* Component to render the sub-communities of a Community
|
||||||
*/
|
*/
|
||||||
export class CommunityPageSubCommunityListComponent implements OnInit {
|
export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy {
|
||||||
@Input() community: Community;
|
@Input() community: Community;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional page size. Overrides communityList.pageSize configuration for this component.
|
||||||
|
* Value can be added in the themed version of the parent component.
|
||||||
|
*/
|
||||||
|
@Input() pageSize: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pagination configuration
|
* The pagination configuration
|
||||||
*/
|
*/
|
||||||
@@ -53,7 +60,9 @@ export class CommunityPageSubCommunityListComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.config = new PaginationComponentOptions();
|
this.config = new PaginationComponentOptions();
|
||||||
this.config.id = this.pageId;
|
this.config.id = this.pageId;
|
||||||
this.config.pageSize = 5;
|
if (hasValue(this.pageSize)) {
|
||||||
|
this.config.pageSize = this.pageSize;
|
||||||
|
}
|
||||||
this.config.currentPage = 1;
|
this.config.currentPage = 1;
|
||||||
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
||||||
this.initPage();
|
this.initPage();
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||||
|
import { CommunityPageSubCommunityListComponent } from './community-page-sub-community-list.component';
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Community } from '../../core/shared/community.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-community-page-sub-community-list',
|
||||||
|
styleUrls: [],
|
||||||
|
templateUrl: '../../shared/theme-support/themed.component.html',
|
||||||
|
})
|
||||||
|
export class ThemedCommunityPageSubCommunityListComponent extends ThemedComponent<CommunityPageSubCommunityListComponent> {
|
||||||
|
|
||||||
|
@Input() community: Community;
|
||||||
|
@Input() pageSize: number;
|
||||||
|
protected inAndOutputNames: (keyof CommunityPageSubCommunityListComponent & keyof this)[] = ['community', 'pageSize'];
|
||||||
|
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return 'CommunityPageSubCommunityListComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return import(`../../../themes/${themeName}/app/community-page/sub-community-list/community-page-sub-community-list.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return import(`./community-page-sub-community-list.component`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
|
import { createFailedRemoteDataObject, createPendingRemoteDataObject, createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
|
||||||
import { buildPaginatedList, PaginatedList } from '../../data/paginated-list.model';
|
import { buildPaginatedList, PaginatedList } from '../../data/paginated-list.model';
|
||||||
import { Item } from '../../shared/item.model';
|
import { Item } from '../../shared/item.model';
|
||||||
import { PageInfo } from '../../shared/page-info.model';
|
import { PageInfo } from '../../shared/page-info.model';
|
||||||
@@ -18,6 +18,9 @@ import { take } from 'rxjs/operators';
|
|||||||
import { HALLink } from '../../shared/hal-link.model';
|
import { HALLink } from '../../shared/hal-link.model';
|
||||||
import { RequestEntryState } from '../../data/request-entry-state.model';
|
import { RequestEntryState } from '../../data/request-entry-state.model';
|
||||||
import { RequestEntry } from '../../data/request-entry.model';
|
import { RequestEntry } from '../../data/request-entry.model';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { fakeAsync, tick } from '@angular/core/testing';
|
||||||
|
|
||||||
describe('RemoteDataBuildService', () => {
|
describe('RemoteDataBuildService', () => {
|
||||||
let service: RemoteDataBuildService;
|
let service: RemoteDataBuildService;
|
||||||
@@ -646,4 +649,211 @@ describe('RemoteDataBuildService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('buildFromHref', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(objectCache.getRequestUUIDBySelfLink as jasmine.Spy).and.returnValue(cold('a', { a: 'request/uuid' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when both getRequestFromRequestHref and getRequestFromRequestUUID emit nothing', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(requestService.getByHref as jasmine.Spy).and.returnValue(cold('a', { a: undefined }));
|
||||||
|
(requestService.getByUUID as jasmine.Spy).and.returnValue(cold('a', { a: undefined }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit anything', () => {
|
||||||
|
expect(service.buildFromHref(cold('a', { a: 'rest/api/endpoint' }))).toBeObservable(cold(''));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when one of getRequestFromRequestHref or getRequestFromRequestUUID emits nothing', () => {
|
||||||
|
let requestEntry: RequestEntry;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
requestEntry = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.Success,
|
||||||
|
request: {},
|
||||||
|
});
|
||||||
|
(requestService.getByHref as jasmine.Spy).and.returnValue(cold('a', { a: undefined }));
|
||||||
|
(requestService.getByUUID as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry }));
|
||||||
|
spyOn((service as any), 'buildPayload').and.returnValue(cold('a', { a: {} }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create remote-data with the existing request-entry', () => {
|
||||||
|
expect(service.buildFromHref(cold('a', { a: 'rest/api/endpoint' }))).toBeObservable(cold('a', {
|
||||||
|
a: new RemoteData(undefined, undefined, undefined, RequestEntryState.Success, undefined, {}, undefined),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when one of getRequestFromRequestHref or getRequestFromRequestUUID is stale', () => {
|
||||||
|
let requestEntry1: RequestEntry;
|
||||||
|
let requestEntry2: RequestEntry;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
requestEntry1 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.Success,
|
||||||
|
request: {},
|
||||||
|
});
|
||||||
|
requestEntry2 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.SuccessStale,
|
||||||
|
request: {},
|
||||||
|
});
|
||||||
|
(requestService.getByHref as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry1 }));
|
||||||
|
(requestService.getByUUID as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry2 }));
|
||||||
|
spyOn((service as any), 'buildPayload').and.returnValue(cold('a', { a: {} }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create remote-data with the non-stale request-entry', () => {
|
||||||
|
expect(service.buildFromHref(cold('a', { a: 'rest/api/endpoint' }))).toBeObservable(cold('a', {
|
||||||
|
a: new RemoteData(undefined, undefined, undefined, RequestEntryState.Success, undefined, {}, undefined),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when both getRequestFromRequestHref and getRequestFromRequestUUID are stale', () => {
|
||||||
|
let requestEntry1: RequestEntry;
|
||||||
|
let requestEntry2: RequestEntry;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
requestEntry1 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.SuccessStale,
|
||||||
|
request: {},
|
||||||
|
lastUpdated: 20,
|
||||||
|
});
|
||||||
|
requestEntry2 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.SuccessStale,
|
||||||
|
request: {},
|
||||||
|
lastUpdated: 10,
|
||||||
|
});
|
||||||
|
(requestService.getByHref as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry1 }));
|
||||||
|
(requestService.getByUUID as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry2 }));
|
||||||
|
spyOn((service as any), 'buildPayload').and.returnValue(cold('a', { a: {} }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create remote-data with the most up-to-date request-entry', () => {
|
||||||
|
expect(service.buildFromHref(cold('a', { a: 'rest/api/endpoint' }))).toBeObservable(cold('a', {
|
||||||
|
a: new RemoteData(undefined, undefined, 20, RequestEntryState.SuccessStale, undefined, {}, undefined),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when both getRequestFromRequestHref and getRequestFromRequestUUID are not stale', () => {
|
||||||
|
let requestEntry1: RequestEntry;
|
||||||
|
let requestEntry2: RequestEntry;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
requestEntry1 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.Success,
|
||||||
|
request: {},
|
||||||
|
lastUpdated: 25,
|
||||||
|
});
|
||||||
|
requestEntry2 = Object.assign(new RequestEntry(), {
|
||||||
|
state: RequestEntryState.Success,
|
||||||
|
request: {},
|
||||||
|
lastUpdated: 5,
|
||||||
|
});
|
||||||
|
(requestService.getByHref as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry1 }));
|
||||||
|
(requestService.getByUUID as jasmine.Spy).and.returnValue(cold('a', { a: requestEntry2 }));
|
||||||
|
spyOn((service as any), 'buildPayload').and.returnValue(cold('a', { a: {} }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create remote-data with the most up-to-date request-entry', () => {
|
||||||
|
expect(service.buildFromHref(cold('a', { a: 'rest/api/endpoint' }))).toBeObservable(cold('a', {
|
||||||
|
a: new RemoteData(undefined, undefined, 25, RequestEntryState.Success, undefined, {}, undefined),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('buildFromRequestUUIDAndAwait', () => {
|
||||||
|
let testScheduler;
|
||||||
|
|
||||||
|
let callback: jasmine.Spy;
|
||||||
|
let buildFromRequestUUIDSpy;
|
||||||
|
|
||||||
|
const BOOLEAN = { t: true, f: false };
|
||||||
|
|
||||||
|
const MOCK_PENDING_RD = createPendingRemoteDataObject();
|
||||||
|
const MOCK_SUCCEEDED_RD = createSuccessfulRemoteDataObject({});
|
||||||
|
const MOCK_FAILED_RD = createFailedRemoteDataObject('failed');
|
||||||
|
|
||||||
|
const RDs = {
|
||||||
|
p: MOCK_PENDING_RD,
|
||||||
|
s: MOCK_SUCCEEDED_RD,
|
||||||
|
f: MOCK_FAILED_RD,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testScheduler = new TestScheduler((actual, expected) => {
|
||||||
|
expect(actual).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
callback = jasmine.createSpy('callback');
|
||||||
|
callback.and.returnValue(observableOf(undefined));
|
||||||
|
buildFromRequestUUIDSpy = spyOn(service, 'buildFromRequestUUID').and.callThrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should patch through href & followLinks to buildFromRequestUUID', () => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
service.buildFromRequestUUIDAndAwait('some-href', callback, ...linksToFollow);
|
||||||
|
expect(buildFromRequestUUIDSpy).toHaveBeenCalledWith('some-href', ...linksToFollow);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger the callback on successful RD', (done) => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
|
||||||
|
service.buildFromRequestUUIDAndAwait('some-href', callback).subscribe(rd => {
|
||||||
|
expect(rd).toBe(MOCK_SUCCEEDED_RD);
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger the callback on successful RD even if nothing subscribes to the returned Observable', fakeAsync(() => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
|
||||||
|
service.buildFromRequestUUIDAndAwait('some-href', callback);
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not trigger the callback on pending RD', (done) => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_PENDING_RD));
|
||||||
|
|
||||||
|
service.buildFromRequestUUIDAndAwait('some-href', callback).subscribe(rd => {
|
||||||
|
expect(rd).toBe(MOCK_PENDING_RD);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not trigger the callback on failed RD', (done) => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_FAILED_RD));
|
||||||
|
|
||||||
|
service.buildFromRequestUUIDAndAwait('some-href', callback).subscribe(rd => {
|
||||||
|
expect(rd).toBe(MOCK_FAILED_RD);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only emit after the callback is done', () => {
|
||||||
|
testScheduler.run(({ cold: tsCold, expectObservable }) => {
|
||||||
|
buildFromRequestUUIDSpy.and.returnValue(
|
||||||
|
tsCold('-p----s', RDs)
|
||||||
|
);
|
||||||
|
callback.and.returnValue(
|
||||||
|
tsCold(' --t', BOOLEAN)
|
||||||
|
);
|
||||||
|
|
||||||
|
const done$ = service.buildFromRequestUUIDAndAwait('some-href', callback);
|
||||||
|
expectObservable(done$).toBe(
|
||||||
|
' -p------s', RDs // resulting duration between pending & successful includes the callback
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
AsyncSubject,
|
||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
Observable,
|
Observable,
|
||||||
of as observableOf,
|
of as observableOf,
|
||||||
race as observableRace
|
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { map, switchMap, filter, distinctUntilKeyChanged } from 'rxjs/operators';
|
import { map, switchMap, filter, distinctUntilKeyChanged, startWith } from 'rxjs/operators';
|
||||||
import { hasValue, isEmpty, isNotEmpty, hasNoValue, isUndefined } from '../../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty, hasNoValue, isUndefined } from '../../../shared/empty.util';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig, followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
@@ -21,10 +21,11 @@ import { HALResource } from '../../shared/hal-resource.model';
|
|||||||
import { PAGINATED_LIST } from '../../data/paginated-list.resource-type';
|
import { PAGINATED_LIST } from '../../data/paginated-list.resource-type';
|
||||||
import { getUrlWithoutEmbedParams } from '../../index/index.selectors';
|
import { getUrlWithoutEmbedParams } from '../../index/index.selectors';
|
||||||
import { getResourceTypeValueFor } from '../object-cache.reducer';
|
import { getResourceTypeValueFor } from '../object-cache.reducer';
|
||||||
import { hasSucceeded, RequestEntryState } from '../../data/request-entry-state.model';
|
import { hasSucceeded, isStale, RequestEntryState } from '../../data/request-entry-state.model';
|
||||||
import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/request.operators';
|
import { getRequestFromRequestHref, getRequestFromRequestUUID } from '../../shared/request.operators';
|
||||||
import { RequestEntry } from '../../data/request-entry.model';
|
import { RequestEntry } from '../../data/request-entry.model';
|
||||||
import { ResponseState } from '../../data/response-state.model';
|
import { ResponseState } from '../../data/response-state.model';
|
||||||
|
import { getFirstCompletedRemoteData } from '../../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RemoteDataBuildService {
|
export class RemoteDataBuildService {
|
||||||
@@ -189,6 +190,49 @@ export class RemoteDataBuildService {
|
|||||||
return this.toRemoteDataObservable<T>(requestEntry$, payload$);
|
return this.toRemoteDataObservable<T>(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link RemoteData} object for a rest request and its response
|
||||||
|
* and emits it only after the callback function is completed.
|
||||||
|
*
|
||||||
|
* @param requestUUID$ The UUID of the request we want to retrieve
|
||||||
|
* @param callback A function that returns an Observable. It will only be called once the request has succeeded.
|
||||||
|
* Then, the response will only be emitted after this callback function has emitted.
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
|
||||||
|
*/
|
||||||
|
buildFromRequestUUIDAndAwait<T>(requestUUID$: string | Observable<string>, callback: (rd?: RemoteData<T>) => Observable<unknown>, ...linksToFollow: FollowLinkConfig<any>[]): Observable<RemoteData<T>> {
|
||||||
|
const response$ = this.buildFromRequestUUID(requestUUID$, ...linksToFollow);
|
||||||
|
|
||||||
|
const callbackDone$ = new AsyncSubject<boolean>();
|
||||||
|
response$.pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
switchMap((rd: RemoteData<any>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
// if the request succeeded, execute the callback
|
||||||
|
return callback(rd);
|
||||||
|
} else {
|
||||||
|
// otherwise, emit right away so the subscription doesn't stick around
|
||||||
|
return [true];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
).subscribe(() => {
|
||||||
|
callbackDone$.next(true);
|
||||||
|
callbackDone$.complete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return response$.pipe(
|
||||||
|
switchMap((rd: RemoteData<any>) => {
|
||||||
|
if (rd.hasSucceeded) {
|
||||||
|
// if the request succeeded, wait for the callback to finish
|
||||||
|
return callbackDone$.pipe(
|
||||||
|
map(() => rd),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [rd];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link RemoteData} object for a rest request and its response
|
* Creates a {@link RemoteData} object for a rest request and its response
|
||||||
*
|
*
|
||||||
@@ -207,10 +251,27 @@ export class RemoteDataBuildService {
|
|||||||
this.objectCache.getRequestUUIDBySelfLink(href)),
|
this.objectCache.getRequestUUIDBySelfLink(href)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const requestEntry$ = observableRace(
|
const requestEntry$ = observableCombineLatest([
|
||||||
href$.pipe(getRequestFromRequestHref(this.requestService)),
|
href$.pipe(getRequestFromRequestHref(this.requestService), startWith(undefined)),
|
||||||
requestUUID$.pipe(getRequestFromRequestUUID(this.requestService)),
|
requestUUID$.pipe(getRequestFromRequestUUID(this.requestService), startWith(undefined)),
|
||||||
).pipe(
|
]).pipe(
|
||||||
|
filter(([r1, r2]) => hasValue(r1) || hasValue(r2)),
|
||||||
|
map(([r1, r2]) => {
|
||||||
|
// If one of the two requests has no value, return the other (both is impossible due to the filter above)
|
||||||
|
if (hasNoValue(r2)) {
|
||||||
|
return r1;
|
||||||
|
} else if (hasNoValue(r1)) {
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((isStale(r1.state) && isStale(r2.state)) || (!isStale(r1.state) && !isStale(r2.state))) {
|
||||||
|
// Neither or both are stale, pick the most recent request
|
||||||
|
return r1.lastUpdated >= r2.lastUpdated ? r1 : r2;
|
||||||
|
} else {
|
||||||
|
// One of the two is stale, return the not stale request
|
||||||
|
return isStale(r2.state) ? r1 : r2;
|
||||||
|
}
|
||||||
|
}),
|
||||||
distinctUntilKeyChanged('lastUpdated')
|
distinctUntilKeyChanged('lastUpdated')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@ import { RequestEntryState } from '../request-entry-state.model';
|
|||||||
import { DeleteData, DeleteDataImpl } from './delete-data';
|
import { DeleteData, DeleteDataImpl } from './delete-data';
|
||||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
import { fakeAsync, tick } from '@angular/core/testing';
|
import { RestRequestMethod } from '../rest-request-method';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests whether calls to `DeleteData` methods are correctly patched through in a concrete data service that implements it
|
* Tests whether calls to `DeleteData` methods are correctly patched through in a concrete data service that implements it
|
||||||
@@ -63,8 +63,6 @@ export function testDeleteDataImplementation(serviceFactory: () => DeleteData<an
|
|||||||
|
|
||||||
const endpoint = 'https://rest.api/core';
|
const endpoint = 'https://rest.api/core';
|
||||||
|
|
||||||
const BOOLEAN = { f: false, t: true };
|
|
||||||
|
|
||||||
class TestService extends DeleteDataImpl<any> {
|
class TestService extends DeleteDataImpl<any> {
|
||||||
constructor(
|
constructor(
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
@@ -155,13 +153,13 @@ describe('DeleteDataImpl', () => {
|
|||||||
let MOCK_FAILED_RD;
|
let MOCK_FAILED_RD;
|
||||||
|
|
||||||
let invalidateByHrefSpy: jasmine.Spy;
|
let invalidateByHrefSpy: jasmine.Spy;
|
||||||
let buildFromRequestUUIDSpy: jasmine.Spy;
|
let buildFromRequestUUIDAndAwaitSpy: jasmine.Spy;
|
||||||
let getIDHrefObsSpy: jasmine.Spy;
|
let getIDHrefObsSpy: jasmine.Spy;
|
||||||
let deleteByHrefSpy: jasmine.Spy;
|
let deleteByHrefSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
invalidateByHrefSpy = spyOn(service, 'invalidateByHref').and.returnValue(observableOf(true));
|
invalidateByHrefSpy = spyOn(service, 'invalidateByHref').and.returnValue(observableOf(true));
|
||||||
buildFromRequestUUIDSpy = spyOn(rdbService, 'buildFromRequestUUID').and.callThrough();
|
buildFromRequestUUIDAndAwaitSpy = spyOn(rdbService, 'buildFromRequestUUIDAndAwait').and.callThrough();
|
||||||
getIDHrefObsSpy = spyOn(service, 'getIDHrefObs').and.callThrough();
|
getIDHrefObsSpy = spyOn(service, 'getIDHrefObs').and.callThrough();
|
||||||
deleteByHrefSpy = spyOn(service, 'deleteByHref').and.callThrough();
|
deleteByHrefSpy = spyOn(service, 'deleteByHref').and.callThrough();
|
||||||
|
|
||||||
@@ -171,7 +169,7 @@ describe('DeleteDataImpl', () => {
|
|||||||
|
|
||||||
it('should retrieve href by ID and call deleteByHref', () => {
|
it('should retrieve href by ID and call deleteByHref', () => {
|
||||||
getIDHrefObsSpy.and.returnValue(observableOf('some-href'));
|
getIDHrefObsSpy.and.returnValue(observableOf('some-href'));
|
||||||
buildFromRequestUUIDSpy.and.returnValue(createSuccessfulRemoteDataObject$({}));
|
buildFromRequestUUIDAndAwaitSpy.and.returnValue(createSuccessfulRemoteDataObject$({}));
|
||||||
|
|
||||||
service.delete('some-id', ['a', 'b', 'c']).subscribe(rd => {
|
service.delete('some-id', ['a', 'b', 'c']).subscribe(rd => {
|
||||||
expect(getIDHrefObsSpy).toHaveBeenCalledWith('some-id');
|
expect(getIDHrefObsSpy).toHaveBeenCalledWith('some-id');
|
||||||
@@ -180,66 +178,53 @@ describe('DeleteDataImpl', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('deleteByHref', () => {
|
describe('deleteByHref', () => {
|
||||||
it('should call invalidateByHref if the DELETE request succeeds', (done) => {
|
it('should send a DELETE request', (done) => {
|
||||||
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
buildFromRequestUUIDAndAwaitSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
|
||||||
|
service.deleteByHref('some-href').subscribe(() => {
|
||||||
|
expect(requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||||
|
method: RestRequestMethod.DELETE,
|
||||||
|
href: 'some-href',
|
||||||
|
}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should include the virtual metadata to be copied in the DELETE request', (done) => {
|
||||||
|
buildFromRequestUUIDAndAwaitSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
|
||||||
|
service.deleteByHref('some-href', ['a', 'b', 'c']).subscribe(() => {
|
||||||
|
expect(requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||||
|
method: RestRequestMethod.DELETE,
|
||||||
|
href: 'some-href?copyVirtualMetadata=a©VirtualMetadata=b©VirtualMetadata=c',
|
||||||
|
}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate the currently cached object', (done) => {
|
||||||
|
service.deleteByHref('some-href').subscribe(() => {
|
||||||
|
expect(buildFromRequestUUIDAndAwaitSpy).toHaveBeenCalledWith(
|
||||||
|
requestService.generateRequestId(),
|
||||||
|
jasmine.anything(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const callback = (rdbService.buildFromRequestUUIDAndAwait as jasmine.Spy).calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
expect(service.invalidateByHref).toHaveBeenCalledWith('some-href');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the RemoteData of the response', (done) => {
|
||||||
|
buildFromRequestUUIDAndAwaitSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
||||||
|
|
||||||
service.deleteByHref('some-href').subscribe(rd => {
|
service.deleteByHref('some-href').subscribe(rd => {
|
||||||
expect(rd).toBe(MOCK_SUCCEEDED_RD);
|
expect(rd).toBe(MOCK_SUCCEEDED_RD);
|
||||||
expect(invalidateByHrefSpy).toHaveBeenCalled();
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call invalidateByHref even if not subscribing to returned Observable', fakeAsync(() => {
|
|
||||||
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_SUCCEEDED_RD));
|
|
||||||
|
|
||||||
service.deleteByHref('some-href');
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(invalidateByHrefSpy).toHaveBeenCalled();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should not call invalidateByHref if the DELETE request fails', (done) => {
|
|
||||||
buildFromRequestUUIDSpy.and.returnValue(observableOf(MOCK_FAILED_RD));
|
|
||||||
|
|
||||||
service.deleteByHref('some-href').subscribe(rd => {
|
|
||||||
expect(rd).toBe(MOCK_FAILED_RD);
|
|
||||||
expect(invalidateByHrefSpy).not.toHaveBeenCalled();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should wait for invalidateByHref before emitting', () => {
|
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
|
||||||
buildFromRequestUUIDSpy.and.returnValue(
|
|
||||||
cold('(r|)', { r: MOCK_SUCCEEDED_RD}) // RD emits right away
|
|
||||||
);
|
|
||||||
invalidateByHrefSpy.and.returnValue(
|
|
||||||
cold('----(t|)', BOOLEAN) // but we pretend that setting requests to stale takes longer
|
|
||||||
);
|
|
||||||
|
|
||||||
const done$ = service.deleteByHref('some-href');
|
|
||||||
expectObservable(done$).toBe(
|
|
||||||
'----(r|)', { r: MOCK_SUCCEEDED_RD} // ...and expect the returned Observable to wait until that's done
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should wait for the DELETE request to resolve before emitting', () => {
|
|
||||||
testScheduler.run(({ cold, expectObservable }) => {
|
|
||||||
buildFromRequestUUIDSpy.and.returnValue(
|
|
||||||
cold('----(r|)', { r: MOCK_SUCCEEDED_RD}) // the request takes a while
|
|
||||||
);
|
|
||||||
invalidateByHrefSpy.and.returnValue(
|
|
||||||
cold('(t|)', BOOLEAN) // but we pretend that setting to stale happens sooner
|
|
||||||
); // e.g.: maybe already stale before this call?
|
|
||||||
|
|
||||||
const done$ = service.deleteByHref('some-href');
|
|
||||||
expectObservable(done$).toBe(
|
|
||||||
'----(r|)', { r: MOCK_SUCCEEDED_RD} // ...and expect the returned Observable to wait for the request
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -6,13 +6,12 @@
|
|||||||
* http://www.dspace.org/license/
|
* http://www.dspace.org/license/
|
||||||
*/
|
*/
|
||||||
import { CacheableObject } from '../../cache/cacheable-object.model';
|
import { CacheableObject } from '../../cache/cacheable-object.model';
|
||||||
import { AsyncSubject, combineLatest, Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RemoteData } from '../remote-data';
|
import { RemoteData } from '../remote-data';
|
||||||
import { NoContent } from '../../shared/NoContent.model';
|
import { NoContent } from '../../shared/NoContent.model';
|
||||||
import { filter, map, switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { DeleteRequest } from '../request.models';
|
import { DeleteRequest } from '../request.models';
|
||||||
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
||||||
import { getFirstCompletedRemoteData } from '../../shared/operators';
|
|
||||||
import { RequestService } from '../request.service';
|
import { RequestService } from '../request.service';
|
||||||
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||||
import { HALEndpointService } from '../../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../../shared/hal-endpoint.service';
|
||||||
@@ -83,26 +82,6 @@ export class DeleteDataImpl<T extends CacheableObject> extends IdentifiableDataS
|
|||||||
}
|
}
|
||||||
this.requestService.send(request);
|
this.requestService.send(request);
|
||||||
|
|
||||||
const response$ = this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => this.invalidateByHref(href));
|
||||||
|
|
||||||
const invalidated$ = new AsyncSubject<boolean>();
|
|
||||||
response$.pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
switchMap((rd: RemoteData<NoContent>) => {
|
|
||||||
if (rd.hasSucceeded) {
|
|
||||||
return this.invalidateByHref(href);
|
|
||||||
} else {
|
|
||||||
return [true];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
).subscribe(() => {
|
|
||||||
invalidated$.next(true);
|
|
||||||
invalidated$.complete();
|
|
||||||
});
|
|
||||||
|
|
||||||
return combineLatest([response$, invalidated$]).pipe(
|
|
||||||
filter(([_, invalidated]) => invalidated),
|
|
||||||
map(([response, _]) => response),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,8 @@ describe('BitstreamFormatDataService', () => {
|
|||||||
function initTestService(halService) {
|
function initTestService(halService) {
|
||||||
rd = createSuccessfulRemoteDataObject({});
|
rd = createSuccessfulRemoteDataObject({});
|
||||||
rdbService = jasmine.createSpyObj('rdbService', {
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
buildFromRequestUUID: observableOf(rd)
|
buildFromRequestUUID: observableOf(rd),
|
||||||
|
buildFromRequestUUIDAndAwait: observableOf(rd),
|
||||||
});
|
});
|
||||||
|
|
||||||
return new BitstreamFormatDataService(
|
return new BitstreamFormatDataService(
|
||||||
|
@@ -26,6 +26,10 @@ import { EPersonMock, EPersonMock2 } from '../../shared/testing/eperson.mock';
|
|||||||
import { createPaginatedList, createRequestEntry$ } from '../../shared/testing/utils.test';
|
import { createPaginatedList, createRequestEntry$ } from '../../shared/testing/utils.test';
|
||||||
import { CoreState } from '../core-state.model';
|
import { CoreState } from '../core-state.model';
|
||||||
import { FindListOptions } from '../data/find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
import { getMockLinkService } from '../../shared/mocks/link-service.mock';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { ObjectCacheEntry } from '../cache/object-cache.reducer';
|
||||||
|
|
||||||
describe('GroupDataService', () => {
|
describe('GroupDataService', () => {
|
||||||
let service: GroupDataService;
|
let service: GroupDataService;
|
||||||
@@ -38,7 +42,7 @@ describe('GroupDataService', () => {
|
|||||||
let groups$;
|
let groups$;
|
||||||
let halService;
|
let halService;
|
||||||
let rdbService;
|
let rdbService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
function init() {
|
function init() {
|
||||||
restEndpointURL = 'https://dspace.4science.it/dspace-spring-rest/api/eperson';
|
restEndpointURL = 'https://dspace.4science.it/dspace-spring-rest/api/eperson';
|
||||||
groupsEndpoint = `${restEndpointURL}/groups`;
|
groupsEndpoint = `${restEndpointURL}/groups`;
|
||||||
@@ -46,6 +50,7 @@ describe('GroupDataService', () => {
|
|||||||
groups$ = createSuccessfulRemoteDataObject$(createPaginatedList(groups));
|
groups$ = createSuccessfulRemoteDataObject$(createPaginatedList(groups));
|
||||||
rdbService = getMockRemoteDataBuildServiceHrefMap(undefined, { 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups': groups$ });
|
rdbService = getMockRemoteDataBuildServiceHrefMap(undefined, { 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups': groups$ });
|
||||||
halService = new HALEndpointServiceStub(restEndpointURL);
|
halService = new HALEndpointServiceStub(restEndpointURL);
|
||||||
|
objectCache = new ObjectCacheService(store, getMockLinkService());
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -67,7 +72,7 @@ describe('GroupDataService', () => {
|
|||||||
return new GroupDataService(
|
return new GroupDataService(
|
||||||
requestService,
|
requestService,
|
||||||
rdbService,
|
rdbService,
|
||||||
null,
|
objectCache,
|
||||||
halService,
|
halService,
|
||||||
new DummyChangeAnalyzer() as any,
|
new DummyChangeAnalyzer() as any,
|
||||||
null,
|
null,
|
||||||
@@ -82,6 +87,7 @@ describe('GroupDataService', () => {
|
|||||||
store = new Store<CoreState>(undefined, undefined, undefined);
|
store = new Store<CoreState>(undefined, undefined, undefined);
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
|
spyOn(rdbService, 'buildFromRequestUUIDAndAwait').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('searchGroups', () => {
|
describe('searchGroups', () => {
|
||||||
@@ -108,6 +114,10 @@ describe('GroupDataService', () => {
|
|||||||
|
|
||||||
describe('addSubGroupToGroup', () => {
|
describe('addSubGroupToGroup', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spyOn(objectCache, 'getByHref').and.returnValue(observableOf({
|
||||||
|
requestUUIDs: ['request1', 'request2']
|
||||||
|
} as ObjectCacheEntry));
|
||||||
|
spyOn((service as any).deleteData, 'invalidateByHref');
|
||||||
service.addSubGroupToGroup(GroupMock, GroupMock2).subscribe();
|
service.addSubGroupToGroup(GroupMock, GroupMock2).subscribe();
|
||||||
});
|
});
|
||||||
it('should send PostRequest to eperson/groups/group-id/subgroups endpoint with new subgroup link in body', () => {
|
it('should send PostRequest to eperson/groups/group-id/subgroups endpoint with new subgroup link in body', () => {
|
||||||
@@ -118,20 +128,50 @@ describe('GroupDataService', () => {
|
|||||||
const expected = new PostRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.subgroupsEndpoint, GroupMock2.self, options);
|
const expected = new PostRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.subgroupsEndpoint, GroupMock2.self, options);
|
||||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||||
});
|
});
|
||||||
|
it('should invalidate the previous requests of the parent group', () => {
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait).toHaveBeenCalled();
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[0]).toBe(requestService.generateRequestId());
|
||||||
|
const callback = rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(GroupMock._links.self.href);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledTimes(2);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request1');
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('deleteSubGroupFromGroup', () => {
|
describe('deleteSubGroupFromGroup', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spyOn(objectCache, 'getByHref').and.returnValue(observableOf({
|
||||||
|
requestUUIDs: ['request1', 'request2']
|
||||||
|
} as ObjectCacheEntry));
|
||||||
|
spyOn((service as any).deleteData, 'invalidateByHref');
|
||||||
service.deleteSubGroupFromGroup(GroupMock, GroupMock2).subscribe();
|
service.deleteSubGroupFromGroup(GroupMock, GroupMock2).subscribe();
|
||||||
});
|
});
|
||||||
it('should send DeleteRequest to eperson/groups/group-id/subgroups/group-id endpoint', () => {
|
it('should send DeleteRequest to eperson/groups/group-id/subgroups/group-id endpoint', () => {
|
||||||
const expected = new DeleteRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.subgroupsEndpoint + '/' + GroupMock2.id);
|
const expected = new DeleteRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.subgroupsEndpoint + '/' + GroupMock2.id);
|
||||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||||
});
|
});
|
||||||
|
it('should invalidate the previous requests of the parent group\'', () => {
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait).toHaveBeenCalled();
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[0]).toBe(requestService.generateRequestId());
|
||||||
|
const callback = rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(GroupMock._links.self.href);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledTimes(2);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request1');
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addMemberToGroup', () => {
|
describe('addMemberToGroup', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spyOn(objectCache, 'getByHref').and.returnValue(observableOf({
|
||||||
|
requestUUIDs: ['request1', 'request2']
|
||||||
|
} as ObjectCacheEntry));
|
||||||
|
spyOn((service as any).deleteData, 'invalidateByHref');
|
||||||
service.addMemberToGroup(GroupMock, EPersonMock2).subscribe();
|
service.addMemberToGroup(GroupMock, EPersonMock2).subscribe();
|
||||||
});
|
});
|
||||||
it('should send PostRequest to eperson/groups/group-id/epersons endpoint with new eperson member in body', () => {
|
it('should send PostRequest to eperson/groups/group-id/epersons endpoint with new eperson member in body', () => {
|
||||||
@@ -142,20 +182,48 @@ describe('GroupDataService', () => {
|
|||||||
const expected = new PostRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.ePersonsEndpoint, EPersonMock2.self, options);
|
const expected = new PostRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.ePersonsEndpoint, EPersonMock2.self, options);
|
||||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||||
});
|
});
|
||||||
|
it('should invalidate the previous requests of the EPerson and the group', () => {
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait).toHaveBeenCalled();
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[0]).toBe(requestService.generateRequestId());
|
||||||
|
const callback = rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(EPersonMock2._links.self.href);
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(GroupMock._links.self.href);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledTimes(4);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request1');
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('deleteMemberFromGroup', () => {
|
describe('deleteMemberFromGroup', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spyOn(objectCache, 'getByHref').and.returnValue(observableOf({
|
||||||
|
requestUUIDs: ['request1', 'request2']
|
||||||
|
} as ObjectCacheEntry));
|
||||||
|
spyOn((service as any).deleteData, 'invalidateByHref');
|
||||||
service.deleteMemberFromGroup(GroupMock, EPersonMock).subscribe();
|
service.deleteMemberFromGroup(GroupMock, EPersonMock).subscribe();
|
||||||
});
|
});
|
||||||
it('should send DeleteRequest to eperson/groups/group-id/epersons/eperson-id endpoint', () => {
|
it('should send DeleteRequest to eperson/groups/group-id/epersons/eperson-id endpoint', () => {
|
||||||
const expected = new DeleteRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.ePersonsEndpoint + '/' + EPersonMock.id);
|
const expected = new DeleteRequest(requestService.generateRequestId(), GroupMock.self + '/' + service.ePersonsEndpoint + '/' + EPersonMock.id);
|
||||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||||
});
|
});
|
||||||
|
it('should invalidate the previous requests of the EPerson and the group', () => {
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait).toHaveBeenCalled();
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[0]).toBe(requestService.generateRequestId());
|
||||||
|
const callback = rdbService.buildFromRequestUUIDAndAwait.calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(EPersonMock._links.self.href);
|
||||||
|
expect(objectCache.getByHref).toHaveBeenCalledWith(GroupMock._links.self.href);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledTimes(4);
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request1');
|
||||||
|
expect(requestService.setStaleByUUID).toHaveBeenCalledWith('request2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('editGroup', () => {
|
describe('editGroup', () => {
|
||||||
it('should dispatch a EDIT_GROUP action with the groupp to start editing', () => {
|
it('should dispatch a EDIT_GROUP action with the group to start editing', () => {
|
||||||
service.editGroup(GroupMock);
|
service.editGroup(GroupMock);
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(new GroupRegistryEditGroupAction(GroupMock));
|
expect(store.dispatch).toHaveBeenCalledWith(new GroupRegistryEditGroupAction(GroupMock));
|
||||||
});
|
});
|
||||||
|
@@ -2,7 +2,7 @@ import { HttpHeaders } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { createSelector, select, Store } from '@ngrx/store';
|
import { createSelector, select, Store } from '@ngrx/store';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, zip as observableZip } from 'rxjs';
|
||||||
import { filter, map, take } from 'rxjs/operators';
|
import { filter, map, take } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
GroupRegistryCancelGroupAction,
|
GroupRegistryCancelGroupAction,
|
||||||
@@ -124,7 +124,8 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds given subgroup as a subgroup to the given active group
|
* Adds given subgroup as a subgroup to the given active group and waits until the {@link activeGroup} and
|
||||||
|
* the {@link subgroup} are invalidated.
|
||||||
* @param activeGroup Group we want to add subgroup to
|
* @param activeGroup Group we want to add subgroup to
|
||||||
* @param subgroup Group we want to add as subgroup to activeGroup
|
* @param subgroup Group we want to add as subgroup to activeGroup
|
||||||
*/
|
*/
|
||||||
@@ -137,11 +138,16 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint, subgroup.self, options);
|
const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint, subgroup.self, options);
|
||||||
this.requestService.send(postRequest);
|
this.requestService.send(postRequest);
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => observableZip(
|
||||||
|
this.invalidateByHref(activeGroup._links.self.href),
|
||||||
|
this.requestService.setStaleByHrefSubstring(activeGroup._links.subgroups.href).pipe(take(1)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a given subgroup from the subgroups of the given active group
|
* Deletes a given subgroup from the subgroups of the given active group and waits until the {@link activeGroup} and
|
||||||
|
* the {@link subgroup} are invalidated.
|
||||||
|
* are invalidated.
|
||||||
* @param activeGroup Group we want to delete subgroup from
|
* @param activeGroup Group we want to delete subgroup from
|
||||||
* @param subgroup Subgroup we want to delete from activeGroup
|
* @param subgroup Subgroup we want to delete from activeGroup
|
||||||
*/
|
*/
|
||||||
@@ -150,11 +156,15 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint + '/' + subgroup.id);
|
const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.subgroupsEndpoint + '/' + subgroup.id);
|
||||||
this.requestService.send(deleteRequest);
|
this.requestService.send(deleteRequest);
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => observableZip(
|
||||||
|
this.invalidateByHref(activeGroup._links.self.href),
|
||||||
|
this.requestService.setStaleByHrefSubstring(activeGroup._links.subgroups.href).pipe(take(1)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds given ePerson as member to given group
|
* Adds given ePerson as member to a given group and invalidates the ePerson and waits until the {@link ePerson} and
|
||||||
|
* the {@link activeGroup} are invalidated.
|
||||||
* @param activeGroup Group we want to add member to
|
* @param activeGroup Group we want to add member to
|
||||||
* @param ePerson EPerson we want to add as member to given activeGroup
|
* @param ePerson EPerson we want to add as member to given activeGroup
|
||||||
*/
|
*/
|
||||||
@@ -167,11 +177,17 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint, ePerson.self, options);
|
const postRequest = new PostRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint, ePerson.self, options);
|
||||||
this.requestService.send(postRequest);
|
this.requestService.send(postRequest);
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => observableZip(
|
||||||
|
this.invalidateByHref(ePerson._links.self.href),
|
||||||
|
this.invalidateByHref(activeGroup._links.self.href),
|
||||||
|
this.requestService.setStaleByHrefSubstring(ePerson._links.groups.href).pipe(take(1)),
|
||||||
|
this.requestService.setStaleByHrefSubstring(activeGroup._links.epersons.href).pipe(take(1)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a given ePerson from the members of the given active group
|
* Deletes a given ePerson from the members of the given active group and waits until the {@link ePerson} and the
|
||||||
|
* {@link activeGroup} are invalidated.
|
||||||
* @param activeGroup Group we want to delete member from
|
* @param activeGroup Group we want to delete member from
|
||||||
* @param ePerson EPerson we want to delete from members of given activeGroup
|
* @param ePerson EPerson we want to delete from members of given activeGroup
|
||||||
*/
|
*/
|
||||||
@@ -180,7 +196,12 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint + '/' + ePerson.id);
|
const deleteRequest = new DeleteRequest(requestId, activeGroup.self + '/' + this.ePersonsEndpoint + '/' + ePerson.id);
|
||||||
this.requestService.send(deleteRequest);
|
this.requestService.send(deleteRequest);
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => observableZip(
|
||||||
|
this.invalidateByHref(ePerson._links.self.href),
|
||||||
|
this.invalidateByHref(activeGroup._links.self.href),
|
||||||
|
this.requestService.setStaleByHrefSubstring(ePerson._links.groups.href).pipe(take(1)),
|
||||||
|
this.requestService.setStaleByHrefSubstring(activeGroup._links.epersons.href).pipe(take(1)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -276,7 +297,7 @@ export class GroupDataService extends IdentifiableDataService<Group> implements
|
|||||||
* @param role The name of the role for which to create a group
|
* @param role The name of the role for which to create a group
|
||||||
* @param link The REST endpoint to create the group
|
* @param link The REST endpoint to create the group
|
||||||
*/
|
*/
|
||||||
createComcolGroup(dso: Community|Collection, role: string, link: string): Observable<RemoteData<Group>> {
|
createComcolGroup(dso: Community | Collection, role: string, link: string): Observable<RemoteData<Group>> {
|
||||||
|
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
const group = Object.assign(new Group(), {
|
const group = Object.assign(new Group(), {
|
||||||
|
@@ -19,6 +19,7 @@ import { RequestEntry } from '../data/request-entry.model';
|
|||||||
import { FindListOptions } from '../data/find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
import { EPersonDataService } from '../eperson/eperson-data.service';
|
import { EPersonDataService } from '../eperson/eperson-data.service';
|
||||||
import { GroupDataService } from '../eperson/group-data.service';
|
import { GroupDataService } from '../eperson/group-data.service';
|
||||||
|
import { RestRequestMethod } from '../data/rest-request-method';
|
||||||
|
|
||||||
describe('ResourcePolicyService', () => {
|
describe('ResourcePolicyService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
@@ -120,12 +121,23 @@ describe('ResourcePolicyService', () => {
|
|||||||
}),
|
}),
|
||||||
buildFromRequestUUID: hot('a|', {
|
buildFromRequestUUID: hot('a|', {
|
||||||
a: resourcePolicyRD
|
a: resourcePolicyRD
|
||||||
|
}),
|
||||||
|
buildFromRequestUUIDAndAwait: hot('a|', {
|
||||||
|
a: resourcePolicyRD
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
ePersonService = jasmine.createSpyObj('ePersonService', {
|
ePersonService = jasmine.createSpyObj('ePersonService', {
|
||||||
getBrowseEndpoint: hot('a', {
|
getBrowseEndpoint: hot('a', {
|
||||||
a: ePersonEndpoint
|
a: ePersonEndpoint
|
||||||
}),
|
}),
|
||||||
|
getIDHrefObs: cold('a', {
|
||||||
|
a: 'https://rest.api/rest/api/eperson/epersons/' + epersonUUID
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
groupService = jasmine.createSpyObj('groupService', {
|
||||||
|
getIDHrefObs: cold('a', {
|
||||||
|
a: 'https://rest.api/rest/api/eperson/groups/' + groupUUID
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
objectCache = {} as ObjectCacheService;
|
objectCache = {} as ObjectCacheService;
|
||||||
const notificationsService = {} as NotificationsService;
|
const notificationsService = {} as NotificationsService;
|
||||||
@@ -142,11 +154,12 @@ describe('ResourcePolicyService', () => {
|
|||||||
groupService,
|
groupService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
spyOn(service, 'findById').and.callThrough();
|
||||||
|
spyOn(service, 'findByHref').and.callThrough();
|
||||||
|
spyOn(service, 'invalidateByHref').and.returnValue(observableOf(true));
|
||||||
spyOn((service as any).createData, 'create').and.callThrough();
|
spyOn((service as any).createData, 'create').and.callThrough();
|
||||||
spyOn((service as any).deleteData, 'delete').and.callThrough();
|
spyOn((service as any).deleteData, 'delete').and.callThrough();
|
||||||
spyOn((service as any).patchData, 'update').and.callThrough();
|
spyOn((service as any).patchData, 'update').and.callThrough();
|
||||||
spyOn((service as any), 'findById').and.callThrough();
|
|
||||||
spyOn((service as any), 'findByHref').and.callThrough();
|
|
||||||
spyOn((service as any).searchData, 'searchBy').and.callThrough();
|
spyOn((service as any).searchData, 'searchBy').and.callThrough();
|
||||||
spyOn((service as any).searchData, 'getSearchByHref').and.returnValue(observableOf(requestURL));
|
spyOn((service as any).searchData, 'getSearchByHref').and.returnValue(observableOf(requestURL));
|
||||||
});
|
});
|
||||||
@@ -318,14 +331,32 @@ describe('ResourcePolicyService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('updateTarget', () => {
|
describe('updateTarget', () => {
|
||||||
it('should create a new PUT request for eperson', () => {
|
beforeEach(() => {
|
||||||
const targetType = 'eperson';
|
scheduler.schedule(() => service.create(resourcePolicy, resourceUUID, epersonUUID));
|
||||||
|
});
|
||||||
|
|
||||||
const result = service.updateTarget(resourcePolicyId, requestURL, epersonUUID, targetType);
|
it('should send a PUT request to update the EPerson', () => {
|
||||||
const expected = cold('a|', {
|
service.updateTarget(resourcePolicyId, requestURL, epersonUUID, 'eperson');
|
||||||
a: resourcePolicyRD
|
scheduler.flush();
|
||||||
});
|
|
||||||
expect(result).toBeObservable(expected);
|
expect(requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||||
|
method: RestRequestMethod.PUT,
|
||||||
|
uuid: requestUUID,
|
||||||
|
href: `${resourcePolicy._links.self.href}/eperson`,
|
||||||
|
body: 'https://rest.api/rest/api/eperson/epersons/' + epersonUUID,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invalidate the ResourcePolicy', () => {
|
||||||
|
service.updateTarget(resourcePolicyId, requestURL, epersonUUID, 'eperson');
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(rdbService.buildFromRequestUUIDAndAwait).toHaveBeenCalled();
|
||||||
|
expect((rdbService.buildFromRequestUUIDAndAwait as jasmine.Spy).calls.argsFor(0)[0]).toBe(requestService.generateRequestId());
|
||||||
|
const callback = (rdbService.buildFromRequestUUIDAndAwait as jasmine.Spy).calls.argsFor(0)[1];
|
||||||
|
callback();
|
||||||
|
|
||||||
|
expect(service.invalidateByHref).toHaveBeenCalledWith(resourcePolicy._links.self.href);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ import { PaginatedList } from '../data/paginated-list.model';
|
|||||||
import { ActionType } from './models/action-type.model';
|
import { ActionType } from './models/action-type.model';
|
||||||
import { RequestParam } from '../cache/models/request-param.model';
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { first, map } from 'rxjs/operators';
|
||||||
import { NoContent } from '../shared/NoContent.model';
|
import { NoContent } from '../shared/NoContent.model';
|
||||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||||
import { FindListOptions } from '../data/find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
@@ -194,13 +194,8 @@ export class ResourcePolicyDataService extends IdentifiableDataService<ResourceP
|
|||||||
* @param targetType the type of the target (eperson or group) to which the permission is being granted
|
* @param targetType the type of the target (eperson or group) to which the permission is being granted
|
||||||
*/
|
*/
|
||||||
updateTarget(resourcePolicyId: string, resourcePolicyHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
|
updateTarget(resourcePolicyId: string, resourcePolicyHref: string, targetUUID: string, targetType: string): Observable<RemoteData<any>> {
|
||||||
|
|
||||||
const targetService = targetType === 'eperson' ? this.ePersonService : this.groupService;
|
const targetService = targetType === 'eperson' ? this.ePersonService : this.groupService;
|
||||||
|
const targetEndpoint$ = targetService.getIDHrefObs(targetUUID);
|
||||||
const targetEndpoint$ = targetService.getBrowseEndpoint().pipe(
|
|
||||||
take(1),
|
|
||||||
map((endpoint: string) =>`${endpoint}/${targetUUID}`),
|
|
||||||
);
|
|
||||||
|
|
||||||
const options: HttpOptions = Object.create({});
|
const options: HttpOptions = Object.create({});
|
||||||
let headers = new HttpHeaders();
|
let headers = new HttpHeaders();
|
||||||
@@ -209,9 +204,9 @@ export class ResourcePolicyDataService extends IdentifiableDataService<ResourceP
|
|||||||
|
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
this.requestService.setStaleByHrefSubstring(`${this.getLinkPath()}/${resourcePolicyId}/${targetType}`);
|
targetEndpoint$.pipe(
|
||||||
|
first(),
|
||||||
targetEndpoint$.subscribe((targetEndpoint) => {
|
).subscribe((targetEndpoint) => {
|
||||||
const resourceEndpoint = resourcePolicyHref + '/' + targetType;
|
const resourceEndpoint = resourcePolicyHref + '/' + targetType;
|
||||||
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
|
const request = new PutRequest(requestId, resourceEndpoint, targetEndpoint, options);
|
||||||
Object.assign(request, {
|
Object.assign(request, {
|
||||||
@@ -222,8 +217,7 @@ export class ResourcePolicyDataService extends IdentifiableDataService<ResourceP
|
|||||||
this.requestService.send(request);
|
this.requestService.send(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.rdbService.buildFromRequestUUID(requestId);
|
return this.rdbService.buildFromRequestUUIDAndAwait(requestId, () => this.invalidateByHref(resourcePolicyHref));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ import { PaginationService } from '../../pagination/pagination.service';
|
|||||||
import { SearchConfigurationService } from './search-configuration.service';
|
import { SearchConfigurationService } from './search-configuration.service';
|
||||||
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
|
||||||
import { RequestEntry } from '../../data/request-entry.model';
|
import { RequestEntry } from '../../data/request-entry.model';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
@Component({ template: '' })
|
@Component({ template: '' })
|
||||||
class DummyComponent {
|
class DummyComponent {
|
||||||
@@ -57,6 +58,7 @@ describe('SearchService', () => {
|
|||||||
{ provide: DSpaceObjectDataService, useValue: {} },
|
{ provide: DSpaceObjectDataService, useValue: {} },
|
||||||
{ provide: PaginationService, useValue: {} },
|
{ provide: PaginationService, useValue: {} },
|
||||||
{ provide: SearchConfigurationService, useValue: searchConfigService },
|
{ provide: SearchConfigurationService, useValue: searchConfigService },
|
||||||
|
{ provide: Angulartics2, useValue: {} },
|
||||||
SearchService
|
SearchService
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -124,6 +126,7 @@ describe('SearchService', () => {
|
|||||||
{ provide: DSpaceObjectDataService, useValue: {} },
|
{ provide: DSpaceObjectDataService, useValue: {} },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: SearchConfigurationService, useValue: searchConfigService },
|
{ provide: SearchConfigurationService, useValue: searchConfigService },
|
||||||
|
{ provide: Angulartics2, useValue: {} },
|
||||||
SearchService
|
SearchService
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@@ -33,6 +33,7 @@ import { SearchConfigurationService } from './search-configuration.service';
|
|||||||
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||||
import { RestRequest } from '../../data/rest-request.model';
|
import { RestRequest } from '../../data/rest-request.model';
|
||||||
import { BaseDataService } from '../../data/base/base-data.service';
|
import { BaseDataService } from '../../data/base/base-data.service';
|
||||||
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A limited data service implementation for the 'discover' endpoint
|
* A limited data service implementation for the 'discover' endpoint
|
||||||
@@ -96,6 +97,7 @@ export class SearchService implements OnDestroy {
|
|||||||
private dspaceObjectService: DSpaceObjectDataService,
|
private dspaceObjectService: DSpaceObjectDataService,
|
||||||
private paginationService: PaginationService,
|
private paginationService: PaginationService,
|
||||||
private searchConfigurationService: SearchConfigurationService,
|
private searchConfigurationService: SearchConfigurationService,
|
||||||
|
private angulartics2: Angulartics2,
|
||||||
) {
|
) {
|
||||||
this.searchDataService = new SearchDataService();
|
this.searchDataService = new SearchDataService();
|
||||||
}
|
}
|
||||||
@@ -320,6 +322,37 @@ export class SearchService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send search event to rest api using angularitics
|
||||||
|
* @param config Paginated search options used
|
||||||
|
* @param searchQueryResponse The response objects of the performed search
|
||||||
|
*/
|
||||||
|
trackSearch(config: PaginatedSearchOptions, searchQueryResponse: SearchObjects<DSpaceObject>) {
|
||||||
|
const filters: { filter: string, operator: string, value: string, label: string; }[] = [];
|
||||||
|
const appliedFilters = searchQueryResponse.appliedFilters || [];
|
||||||
|
for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
|
||||||
|
const appliedFilter = appliedFilters[i];
|
||||||
|
filters.push(appliedFilter);
|
||||||
|
}
|
||||||
|
this.angulartics2.eventTrack.next({
|
||||||
|
action: 'search',
|
||||||
|
properties: {
|
||||||
|
searchOptions: config,
|
||||||
|
page: {
|
||||||
|
size: config.pagination.size, // same as searchQueryResponse.page.elementsPerPage
|
||||||
|
totalElements: searchQueryResponse.pageInfo.totalElements,
|
||||||
|
totalPages: searchQueryResponse.pageInfo.totalPages,
|
||||||
|
number: config.pagination.currentPage, // same as searchQueryResponse.page.currentPage
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
by: config.sort.field,
|
||||||
|
order: config.sort.direction
|
||||||
|
},
|
||||||
|
filters: filters,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string} The base path to the search page
|
* @returns {string} The base path to the search page
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { SubmissionCcLicenseDataService } from './submission-cc-license-data.service';
|
||||||
|
import { testFindAllDataImplementation } from '../data/base/find-all-data.spec';
|
||||||
|
|
||||||
|
describe('SubmissionCcLicenseDataService', () => {
|
||||||
|
|
||||||
|
describe('composition', () => {
|
||||||
|
const initService = () => new SubmissionCcLicenseDataService(null, null, null, null);
|
||||||
|
testFindAllDataImplementation(initService);
|
||||||
|
});
|
||||||
|
});
|
@@ -6,7 +6,7 @@ import { RequestService } from '../data/request.service';
|
|||||||
import { SUBMISSION_CC_LICENSE } from './models/submission-cc-licence.resource-type';
|
import { SUBMISSION_CC_LICENSE } from './models/submission-cc-licence.resource-type';
|
||||||
import { SubmissionCcLicence } from './models/submission-cc-license.model';
|
import { SubmissionCcLicence } from './models/submission-cc-license.model';
|
||||||
import { BaseDataService } from '../data/base/base-data.service';
|
import { BaseDataService } from '../data/base/base-data.service';
|
||||||
import { FindAllData } from '../data/base/find-all-data';
|
import {FindAllData, FindAllDataImpl} from '../data/base/find-all-data';
|
||||||
import { FindListOptions } from '../data/find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
@@ -19,6 +19,7 @@ import { dataService } from '../data/base/data-service.decorator';
|
|||||||
export class SubmissionCcLicenseDataService extends BaseDataService<SubmissionCcLicence> implements FindAllData<SubmissionCcLicence> {
|
export class SubmissionCcLicenseDataService extends BaseDataService<SubmissionCcLicence> implements FindAllData<SubmissionCcLicence> {
|
||||||
|
|
||||||
protected linkPath = 'submissioncclicenses';
|
protected linkPath = 'submissioncclicenses';
|
||||||
|
private findAllData: FindAllData<SubmissionCcLicence>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
@@ -27,6 +28,8 @@ export class SubmissionCcLicenseDataService extends BaseDataService<SubmissionCc
|
|||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
) {
|
) {
|
||||||
super('submissioncclicenses', requestService, rdbService, objectCache, halService);
|
super('submissioncclicenses', requestService, rdbService, objectCache, halService);
|
||||||
|
|
||||||
|
this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +47,6 @@ export class SubmissionCcLicenseDataService extends BaseDataService<SubmissionCc
|
|||||||
* Return an observable that emits object list
|
* Return an observable that emits object list
|
||||||
*/
|
*/
|
||||||
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<SubmissionCcLicence>[]): Observable<RemoteData<PaginatedList<SubmissionCcLicence>>> {
|
public findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<SubmissionCcLicence>[]): Observable<RemoteData<PaginatedList<SubmissionCcLicence>>> {
|
||||||
return undefined;
|
return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { RouterStub } from '../../shared/testing/router.stub';
|
import { RouterStub } from '../../shared/testing/router.stub';
|
||||||
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
|
||||||
@@ -108,6 +108,7 @@ describe('ForgotPasswordFormComponent', () => {
|
|||||||
expect(router.navigate).not.toHaveBeenCalled();
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
expect(notificationsService.error).toHaveBeenCalled();
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should submit a patch request for the user uuid when the form is invalid', () => {
|
it('should submit a patch request for the user uuid when the form is invalid', () => {
|
||||||
|
|
||||||
comp.password = 'password';
|
comp.password = 'password';
|
||||||
|
@@ -10,10 +10,7 @@ import { AuthenticateAction } from '../../core/auth/auth.actions';
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
import {
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload, } from '../../core/shared/operators';
|
||||||
getFirstCompletedRemoteData,
|
|
||||||
getFirstSucceededRemoteDataPayload,
|
|
||||||
} from '../../core/shared/operators';
|
|
||||||
import { CoreState } from '../../core/core-state.model';
|
import { CoreState } from '../../core/core-state.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@@ -32,6 +32,8 @@ import { SearchConfigurationService } from '../../core/shared/search/search-conf
|
|||||||
import { ConfigurationProperty } from '../../core/shared/configuration-property.model';
|
import { ConfigurationProperty } from '../../core/shared/configuration-property.model';
|
||||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||||
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub';
|
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub';
|
||||||
|
import { APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
import { environment } from 'src/environments/environment.test';
|
||||||
|
|
||||||
describe('TopLevelCommunityList Component', () => {
|
describe('TopLevelCommunityList Component', () => {
|
||||||
let comp: TopLevelCommunityListComponent;
|
let comp: TopLevelCommunityListComponent;
|
||||||
@@ -151,6 +153,7 @@ describe('TopLevelCommunityList Component', () => {
|
|||||||
],
|
],
|
||||||
declarations: [TopLevelCommunityListComponent],
|
declarations: [TopLevelCommunityListComponent],
|
||||||
providers: [
|
providers: [
|
||||||
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
{ provide: CommunityDataService, useValue: communityDataServiceStub },
|
||||||
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, OnDestroy } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, OnDestroy, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription } from 'rxjs';
|
import { BehaviorSubject, combineLatest as observableCombineLatest, Subscription } from 'rxjs';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
|||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
|
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this component renders the Top-Level Community list
|
* this component renders the Top-Level Community list
|
||||||
@@ -50,11 +51,14 @@ export class TopLevelCommunityListComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
currentPageSubscription: Subscription;
|
currentPageSubscription: Subscription;
|
||||||
|
|
||||||
constructor(private cds: CommunityDataService,
|
constructor(
|
||||||
private paginationService: PaginationService) {
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
private cds: CommunityDataService,
|
||||||
|
private paginationService: PaginationService
|
||||||
|
) {
|
||||||
this.config = new PaginationComponentOptions();
|
this.config = new PaginationComponentOptions();
|
||||||
this.config.id = this.pageId;
|
this.config.id = this.pageId;
|
||||||
this.config.pageSize = 5;
|
this.config.pageSize = appConfig.homePage.topLevelCommunityList.pageSize;
|
||||||
this.config.currentPage = 1;
|
this.config.currentPage = 1;
|
||||||
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,5 @@
|
|||||||
[displaySubmit]="false"
|
[displaySubmit]="false"
|
||||||
[displayCancel]="false">
|
[displayCancel]="false">
|
||||||
</ds-form>
|
</ds-form>
|
||||||
<div id="notLongEnough" class="container-fluid text-danger" *ngIf="formGroup.hasError('notLongEnough')">{{FORM_PREFIX + 'error.password-length' | translate}}</div>
|
|
||||||
<div id="notSame" class="container-fluid text-danger" *ngIf="formGroup.hasError('notSame')">{{FORM_PREFIX + 'error.matching-passwords' | translate}}</div>
|
<div id="notSame" class="container-fluid text-danger" *ngIf="formGroup.hasError('notSame')">{{FORM_PREFIX + 'error.matching-passwords' | translate}}</div>
|
||||||
<div id="emptyPassword" class="container-fluid text-danger" *ngIf="(formGroup.dirty || formGroup.touched) && formGroup.hasError('emptyPassword')">{{FORM_PREFIX + 'error.empty-password' | translate}}</div>
|
<div id="emptyPassword" class="container-fluid text-danger" *ngIf="(formGroup.dirty || formGroup.touched) && formGroup.hasError('emptyPassword')">{{FORM_PREFIX + 'error.empty-password' | translate}}</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { waitForAsync, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
@@ -60,6 +60,7 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
FORM_PREFIX: string;
|
FORM_PREFIX: string;
|
||||||
|
|
||||||
private subs: Subscription[] = [];
|
private subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(protected formService: DynamicFormService,
|
constructor(protected formService: DynamicFormService,
|
||||||
@@ -71,10 +72,10 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.passwordCanBeEmpty) {
|
if (this.passwordCanBeEmpty) {
|
||||||
this.formGroup = this.formService.createFormGroup(this.formModel,
|
this.formGroup = this.formService.createFormGroup(this.formModel,
|
||||||
{validators: [this.checkPasswordsEqual, this.checkPasswordLength]});
|
{ validators: [this.checkPasswordsEqual] });
|
||||||
} else {
|
} else {
|
||||||
this.formGroup = this.formService.createFormGroup(this.formModel,
|
this.formGroup = this.formService.createFormGroup(this.formModel,
|
||||||
{validators: [this.checkPasswordsEqual, this.checkPasswordLength, this.checkPasswordEmpty]});
|
{ validators: [this.checkPasswordsEqual, this.checkPasswordEmpty] });
|
||||||
}
|
}
|
||||||
this.updateFieldTranslations();
|
this.updateFieldTranslations();
|
||||||
this.translate.onLangChange
|
this.translate.onLangChange
|
||||||
@@ -82,15 +83,11 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
this.updateFieldTranslations();
|
this.updateFieldTranslations();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subs.push(this.formGroup.statusChanges.pipe(
|
this.subs.push(
|
||||||
debounceTime(300),
|
this.formGroup.statusChanges.pipe(
|
||||||
map((status: string) => {
|
debounceTime(300),
|
||||||
if (status !== 'VALID') {
|
map((status: string) => status !== 'VALID')
|
||||||
return true;
|
).subscribe((status) => this.isInvalid.emit(status))
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})).subscribe((status) => this.isInvalid.emit(status))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subs.push(this.formGroup.valueChanges.pipe(
|
this.subs.push(this.formGroup.valueChanges.pipe(
|
||||||
@@ -119,17 +116,7 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
const pass = group.get('password').value;
|
const pass = group.get('password').value;
|
||||||
const repeatPass = group.get('passwordrepeat').value;
|
const repeatPass = group.get('passwordrepeat').value;
|
||||||
|
|
||||||
return pass === repeatPass ? null : {notSame: true};
|
return pass === repeatPass ? null : { notSame: true };
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the password is at least 6 characters long
|
|
||||||
* @param group The FormGroup to validate
|
|
||||||
*/
|
|
||||||
checkPasswordLength(group: FormGroup) {
|
|
||||||
const pass = group.get('password').value;
|
|
||||||
|
|
||||||
return isEmpty(pass) || pass.length >= 6 ? null : {notLongEnough: true};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,7 +125,7 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
checkPasswordEmpty(group: FormGroup) {
|
checkPasswordEmpty(group: FormGroup) {
|
||||||
const pass = group.get('password').value;
|
const pass = group.get('password').value;
|
||||||
return isEmpty(pass) ? {emptyPassword: true} : null;
|
return isEmpty(pass) ? { emptyPassword: true } : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -238,6 +238,7 @@ describe('CreateProfileComponent', () => {
|
|||||||
expect(router.navigate).not.toHaveBeenCalled();
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
expect(notificationsService.error).toHaveBeenCalled();
|
expect(notificationsService.error).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should submit not create an eperson when the user info form is invalid', () => {
|
it('should submit not create an eperson when the user info form is invalid', () => {
|
||||||
|
|
||||||
(ePersonDataService.createEPersonForToken as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Error', 500));
|
(ePersonDataService.createEPersonForToken as jasmine.Spy).and.returnValue(createFailedRemoteDataObject$('Error', 500));
|
||||||
|
@@ -1,2 +1 @@
|
|||||||
<ds-themed-search></ds-themed-search>
|
<ds-themed-search [trackStatistics]="true"></ds-themed-search>
|
||||||
<ds-search-tracker></ds-search-tracker>
|
|
||||||
|
@@ -11,7 +11,8 @@ export function getMockObjectCacheService(): ObjectCacheService {
|
|||||||
'getRequestHrefByUUID',
|
'getRequestHrefByUUID',
|
||||||
'getList',
|
'getList',
|
||||||
'hasByUUID',
|
'hasByUUID',
|
||||||
'hasByHref'
|
'hasByHref',
|
||||||
|
'getRequestUUIDBySelfLink',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observab
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}),
|
buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}),
|
||||||
|
buildFromRequestUUIDAndAwait: (id: string, callback: (rd?: RemoteData<any>) => Observable<any>) => createSuccessfulRemoteDataObject$({}),
|
||||||
buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({})
|
buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({})
|
||||||
} as RemoteDataBuildService;
|
} as RemoteDataBuildService;
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ export function getMockRemoteDataBuildServiceHrefMap(toRemoteDataObservable$?: O
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}),
|
buildFromRequestUUID: (id: string) => createSuccessfulRemoteDataObject$({}),
|
||||||
|
buildFromRequestUUIDAndAwait: (id: string, callback: (rd?: RemoteData<any>) => Observable<any>) => createSuccessfulRemoteDataObject$({}),
|
||||||
buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({})
|
buildFromHref: (href: string) => createSuccessfulRemoteDataObject$({})
|
||||||
} as RemoteDataBuildService;
|
} as RemoteDataBuildService;
|
||||||
|
|
||||||
|
@@ -0,0 +1,39 @@
|
|||||||
|
<td class="text-center">
|
||||||
|
<div class="custom-control custom-checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="custom-control-input"
|
||||||
|
[id]="entry.id"
|
||||||
|
[ngModel]="entry.checked"
|
||||||
|
(ngModelChange)="this.toggleCheckbox.emit($event);">
|
||||||
|
<label class="custom-control-label" [for]="entry.id"></label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<th scope="row">
|
||||||
|
{{entry.id}}
|
||||||
|
</th>
|
||||||
|
<td>{{entry.policy.name}}</td>
|
||||||
|
<td>{{entry.policy.policyType}}</td>
|
||||||
|
<td>{{entry.policy.action}}</td>
|
||||||
|
<td>
|
||||||
|
{{ epersonName$ | async }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ groupName$ | async }}
|
||||||
|
</td>
|
||||||
|
<td>{{formatDate(entry.policy.startDate)}}</td>
|
||||||
|
<td>{{formatDate(entry.policy.endDate)}}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
|
||||||
|
<div class="btn-group edit-field">
|
||||||
|
<button class="btn btn-outline-primary btn-sm"
|
||||||
|
[title]="'resource-policies.table.headers.edit.policy' | translate"
|
||||||
|
(click)="redirectToResourcePolicyEditPage()">
|
||||||
|
<i class="fas fa-edit fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="(groupName$ | async) !== undefined" class="btn btn-outline-primary btn-sm"
|
||||||
|
[title]="'resource-policies.table.headers.edit.group' | translate"
|
||||||
|
(click)="redirectToGroupEditPage()">
|
||||||
|
<i class="fas fa-users fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
@@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../remote-data.utils';
|
||||||
|
import { GroupMock } from '../../testing/group-mock';
|
||||||
|
import { PolicyType } from '../../../core/resource-policy/models/policy-type.model';
|
||||||
|
import { ActionType } from '../../../core/resource-policy/models/action-type.model';
|
||||||
|
import { EPersonMock } from '../../testing/eperson.mock';
|
||||||
|
import { ResourcePolicyEntryComponent } from './resource-policy-entry.component';
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { GroupDataService } from '../../../core/eperson/group-data.service';
|
||||||
|
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { RouterStub } from '../../testing/router.stub';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import createSpyObj = jasmine.createSpyObj;
|
||||||
|
|
||||||
|
const groupRP: any = {
|
||||||
|
id: '1',
|
||||||
|
name: null,
|
||||||
|
description: null,
|
||||||
|
policyType: PolicyType.TYPE_SUBMISSION,
|
||||||
|
action: ActionType.READ,
|
||||||
|
startDate: null,
|
||||||
|
endDate: null,
|
||||||
|
type: 'resourcepolicy',
|
||||||
|
uuid: 'resource-policy-1',
|
||||||
|
_links: {
|
||||||
|
eperson: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1/eperson'
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1/group'
|
||||||
|
},
|
||||||
|
self: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eperson: observableOf(createSuccessfulRemoteDataObject(undefined)),
|
||||||
|
group: observableOf(createSuccessfulRemoteDataObject(GroupMock))
|
||||||
|
};
|
||||||
|
|
||||||
|
const epersonRP: any = {
|
||||||
|
id: '1',
|
||||||
|
name: null,
|
||||||
|
description: null,
|
||||||
|
policyType: PolicyType.TYPE_SUBMISSION,
|
||||||
|
action: ActionType.READ,
|
||||||
|
startDate: null,
|
||||||
|
endDate: null,
|
||||||
|
type: 'resourcepolicy',
|
||||||
|
uuid: 'resource-policy-1',
|
||||||
|
_links: {
|
||||||
|
eperson: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1/eperson'
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1/group'
|
||||||
|
},
|
||||||
|
self: {
|
||||||
|
href: 'https://rest.api/rest/api/resourcepolicies/1'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eperson: observableOf(createSuccessfulRemoteDataObject(EPersonMock)),
|
||||||
|
group: observableOf(createSuccessfulRemoteDataObject(undefined))
|
||||||
|
};
|
||||||
|
|
||||||
|
const item = Object.assign(new Item(), {
|
||||||
|
uuid: 'itemUUID',
|
||||||
|
id: 'itemUUID',
|
||||||
|
_links: {
|
||||||
|
self: { href: 'item-selflink' }
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ResourcePolicyEntryComponent', () => {
|
||||||
|
let fixture: ComponentFixture<ResourcePolicyEntryComponent>;
|
||||||
|
let comp: ResourcePolicyEntryComponent;
|
||||||
|
let compAsAny: any;
|
||||||
|
|
||||||
|
let dsoNameService;
|
||||||
|
let groupService;
|
||||||
|
let routeStub;
|
||||||
|
let routerStub;
|
||||||
|
|
||||||
|
it('should pass', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dsoNameService = createSpyObj('dsoNameMock', {
|
||||||
|
getName: 'NAME'
|
||||||
|
});
|
||||||
|
groupService = jasmine.createSpyObj('groupService', {
|
||||||
|
findByHref: jasmine.createSpy('findByHref'),
|
||||||
|
});
|
||||||
|
routeStub = {
|
||||||
|
data: observableOf({
|
||||||
|
item: createSuccessfulRemoteDataObject(item)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
routerStub = Object.assign(new RouterStub(), {
|
||||||
|
url: `url/edit`
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
TranslateModule.forRoot()
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
ResourcePolicyEntryComponent,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: ActivatedRoute, useValue: routeStub },
|
||||||
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: GroupDataService, useValue: groupService },
|
||||||
|
{ provide: DSONameService, useValue: dsoNameService }
|
||||||
|
],
|
||||||
|
// schemas: [
|
||||||
|
// NO_ERRORS_SCHEMA
|
||||||
|
// ]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ResourcePolicyEntryComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
compAsAny = comp;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('target DSO is an EPerson', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.entry = {
|
||||||
|
id: 'test',
|
||||||
|
policy: epersonRP,
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
comp.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have a valid epersonName$', () => {
|
||||||
|
expect(compAsAny.epersonName$).toBeObservable(cold('(n|)', { u: undefined, n: 'NAME' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have an undefined groupName$', () => {
|
||||||
|
expect(compAsAny.groupName$).toBeObservable(cold('(u|)', { u: undefined, n: 'NAME' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('target DSO is a Group ', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.entry = {
|
||||||
|
id: 'test',
|
||||||
|
policy: groupRP,
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
comp.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have a valid groupName$', () => {
|
||||||
|
expect(compAsAny.groupName$).toBeObservable(cold('(n|)', { u: undefined, n: 'NAME' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have an undefined epersonName$', () => {
|
||||||
|
expect(compAsAny.epersonName$).toBeObservable(cold('(u|)', { u: undefined, n: 'NAME' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.entry = {
|
||||||
|
id: 'test',
|
||||||
|
policy: groupRP,
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
comp.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format date properly', () => {
|
||||||
|
expect(comp.formatDate('2020-04-14T12:00:00Z')).toBe('2020-04-14');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect to ResourcePolicy edit page', () => {
|
||||||
|
|
||||||
|
comp.redirectToResourcePolicyEditPage();
|
||||||
|
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should redirect to Group edit page', () => {
|
||||||
|
compAsAny.groupService.findByHref.and.returnValue(observableOf(createSuccessfulRemoteDataObject(GroupMock)));
|
||||||
|
|
||||||
|
comp.redirectToGroupEditPage();
|
||||||
|
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit new state when checkbox is toggled', () => {
|
||||||
|
spyOn(comp.toggleCheckbox, 'emit');
|
||||||
|
|
||||||
|
const checkbox = fixture.debugElement.query(By.css('input[type="checkbox"]'));
|
||||||
|
|
||||||
|
comp.entry.checked = false;
|
||||||
|
checkbox.triggerEventHandler('ngModelChange', true);
|
||||||
|
expect(comp.toggleCheckbox.emit).toHaveBeenCalledWith(true);
|
||||||
|
|
||||||
|
comp.entry.checked = true;
|
||||||
|
checkbox.triggerEventHandler('ngModelChange', false);
|
||||||
|
expect(comp.toggleCheckbox.emit).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
|
import { ResourcePolicy } from '../../../core/resource-policy/models/resource-policy.model';
|
||||||
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
|
import { dateToString, stringToNgbDateStruct } from '../../date.util';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { getAllSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
|
||||||
|
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Group } from '../../../core/eperson/models/group.model';
|
||||||
|
import { ACCESS_CONTROL_MODULE_PATH } from '../../../app-routing-paths';
|
||||||
|
import { GROUP_EDIT_PATH } from '../../../access-control/access-control-routing-paths';
|
||||||
|
import { GroupDataService } from '../../../core/eperson/group-data.service';
|
||||||
|
|
||||||
|
export interface ResourcePolicyCheckboxEntry {
|
||||||
|
id: string;
|
||||||
|
policy: ResourcePolicy;
|
||||||
|
checked: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
selector: 'tr[ds-resource-policy-entry]',
|
||||||
|
templateUrl: './resource-policy-entry.component.html',
|
||||||
|
})
|
||||||
|
export class ResourcePolicyEntryComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
entry: ResourcePolicyCheckboxEntry;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public toggleCheckbox: EventEmitter<boolean> = new EventEmitter();
|
||||||
|
|
||||||
|
epersonName$: Observable<string>;
|
||||||
|
groupName$: Observable<string>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected dsoNameService: DSONameService,
|
||||||
|
protected groupService: GroupDataService,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected router: Router,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.epersonName$ = this.getName$(this.entry.policy.eperson);
|
||||||
|
this.groupName$ = this.getName$(this.entry.policy.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getName$(dso$: Observable<RemoteData<DSpaceObject>>): Observable<string> {
|
||||||
|
return dso$.pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
map((rd: RemoteData<DSpaceObject>) => {
|
||||||
|
if (hasValue(rd?.payload)) {
|
||||||
|
return this.dsoNameService.getName(rd.payload);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a date in simplified format (YYYY-MM-DD).
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* @return a string with formatted date
|
||||||
|
*/
|
||||||
|
formatDate(date: string): string {
|
||||||
|
return isNotEmpty(date) ? dateToString(stringToNgbDateStruct(date)) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to resource policy editing page
|
||||||
|
*/
|
||||||
|
redirectToResourcePolicyEditPage(): void {
|
||||||
|
this.router.navigate([`./edit`], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: {
|
||||||
|
policyId: this.entry.policy.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to group edit page
|
||||||
|
*/
|
||||||
|
redirectToGroupEditPage(): void {
|
||||||
|
this.groupService.findByHref(this.entry.policy._links.group.href, false).pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
map((group: Group) => group.id),
|
||||||
|
).subscribe((groupUUID) => {
|
||||||
|
this.router.navigate([ACCESS_CONTROL_MODULE_PATH, GROUP_EDIT_PATH, groupUUID]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,7 @@
|
|||||||
({{resourceUUID}})
|
({{resourceUUID}})
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</span>
|
</span>
|
||||||
<div class="space-children-mr">
|
<div class="space-children-mr flex-shrink-0">
|
||||||
<button class="btn btn-danger p-1"
|
<button class="btn btn-danger p-1"
|
||||||
[disabled]="(!(canDelete() | async)) || (isProcessingDelete() | async)"
|
[disabled]="(!(canDelete() | async)) || (isProcessingDelete() | async)"
|
||||||
[title]="'resource-policies.delete.btn.title' | translate"
|
[title]="'resource-policies.delete.btn.title' | translate"
|
||||||
@@ -57,49 +57,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="(getResourcePolicies() | async)?.length > 0">
|
<tbody *ngIf="(getResourcePolicies() | async)?.length > 0">
|
||||||
<tr *ngFor="let entry of (getResourcePolicies() | async); trackById">
|
<tr ds-resource-policy-entry *ngFor="let entry of (getResourcePolicies() | async)"
|
||||||
<td class="text-center">
|
[entry]="entry"
|
||||||
<div class="custom-control custom-checkbox">
|
(toggleCheckbox)="selectCheckbox(entry, $event)"
|
||||||
<input type="checkbox"
|
></tr>
|
||||||
class="custom-control-input"
|
|
||||||
[id]="entry.id"
|
|
||||||
[ngModel]="entry.checked"
|
|
||||||
(ngModelChange)="selectCheckbox(entry, $event)">
|
|
||||||
<label class="custom-control-label" [for]="entry.id"></label>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<th scope="row">
|
|
||||||
{{entry.id}}
|
|
||||||
</th>
|
|
||||||
<td>{{entry.policy.name}}</td>
|
|
||||||
<td>{{entry.policy.policyType}}</td>
|
|
||||||
<td>{{entry.policy.action}}</td>
|
|
||||||
<td *ngIf="(hasEPerson(entry.policy) | async)">
|
|
||||||
{{getEPersonName(entry.policy) | async}}
|
|
||||||
</td>
|
|
||||||
<td *ngIf="!(hasEPerson(entry.policy) | async)"></td>
|
|
||||||
<td *ngIf="(hasGroup(entry.policy) | async)">
|
|
||||||
{{getGroupName(entry.policy) | async}}
|
|
||||||
</td>
|
|
||||||
<td *ngIf="!(hasGroup(entry.policy) | async)"></td>
|
|
||||||
<td>{{formatDate(entry.policy.startDate)}}</td>
|
|
||||||
<td>{{formatDate(entry.policy.endDate)}}</td>
|
|
||||||
<td class="text-center">
|
|
||||||
|
|
||||||
<div class="btn-group edit-field">
|
|
||||||
<button class="btn btn-outline-primary btn-sm"
|
|
||||||
[title]="'resource-policies.table.headers.edit.policy' | translate"
|
|
||||||
(click)="redirectToResourcePolicyEditPage(entry.policy)">
|
|
||||||
<i class="fas fa-edit fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
<button *ngIf="(hasGroup(entry.policy) | async)" class="btn btn-outline-primary btn-sm"
|
|
||||||
[title]="'resource-policies.table.headers.edit.group' | translate"
|
|
||||||
(click)="redirectToGroupEditPage(entry.policy)">
|
|
||||||
<i class="fas fa-users fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -33,6 +33,8 @@ import { PolicyType } from '../../core/resource-policy/models/policy-type.model'
|
|||||||
import { ActionType } from '../../core/resource-policy/models/action-type.model';
|
import { ActionType } from '../../core/resource-policy/models/action-type.model';
|
||||||
import { EPersonMock } from '../testing/eperson.mock';
|
import { EPersonMock } from '../testing/eperson.mock';
|
||||||
import { GroupMock } from '../testing/group-mock';
|
import { GroupMock } from '../testing/group-mock';
|
||||||
|
import { ResourcePolicyEntryComponent } from './entry/resource-policy-entry.component';
|
||||||
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
|
||||||
describe('ResourcePoliciesComponent test suite', () => {
|
describe('ResourcePoliciesComponent test suite', () => {
|
||||||
let comp: ResourcePoliciesComponent;
|
let comp: ResourcePoliciesComponent;
|
||||||
@@ -188,6 +190,10 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
const paginatedList = buildPaginatedList(pageInfo, array);
|
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||||
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||||
|
|
||||||
|
const dsoNameService = jasmine.createSpyObj('dsoNameMock', {
|
||||||
|
getName: 'NAME'
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -198,6 +204,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
ResourcePoliciesComponent,
|
ResourcePoliciesComponent,
|
||||||
|
ResourcePolicyEntryComponent,
|
||||||
TestComponent
|
TestComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -209,6 +216,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
{ provide: ResourcePolicyDataService, useValue: resourcePolicyService },
|
{ provide: ResourcePolicyDataService, useValue: resourcePolicyService },
|
||||||
{ provide: RequestService, useValue: getMockRequestService() },
|
{ provide: RequestService, useValue: getMockRequestService() },
|
||||||
{ provide: Router, useValue: routerStub },
|
{ provide: Router, useValue: routerStub },
|
||||||
|
{ provide: DSONameService, useValue: dsoNameService },
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
ResourcePoliciesComponent
|
ResourcePoliciesComponent
|
||||||
], schemas: [
|
], schemas: [
|
||||||
@@ -260,10 +268,10 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should init component properly', () => {
|
it('should init component properly', () => {
|
||||||
spyOn(comp, 'initResourcePolicyLIst');
|
spyOn(comp, 'initResourcePolicyList');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(compAsAny.isActive).toBeTruthy();
|
expect(compAsAny.isActive).toBeTruthy();
|
||||||
expect(comp.initResourcePolicyLIst).toHaveBeenCalled();
|
expect(comp.initResourcePolicyList).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should init resource policies list properly', () => {
|
it('should init resource policies list properly', () => {
|
||||||
@@ -274,7 +282,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
scheduler = getTestScheduler();
|
scheduler = getTestScheduler();
|
||||||
scheduler.schedule(() => comp.initResourcePolicyLIst());
|
scheduler.schedule(() => comp.initResourcePolicyList());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
|
|
||||||
expect(compAsAny.resourcePoliciesEntries$.value).toEqual(expected);
|
expect(compAsAny.resourcePoliciesEntries$.value).toEqual(expected);
|
||||||
@@ -291,7 +299,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
const initResourcePolicyEntries = getInitEntries();
|
const initResourcePolicyEntries = getInitEntries();
|
||||||
compAsAny.resourcePoliciesEntries$.next(initResourcePolicyEntries);
|
compAsAny.resourcePoliciesEntries$.next(initResourcePolicyEntries);
|
||||||
resourcePolicyService.searchByResource.and.returnValue(observableOf({}));
|
resourcePolicyService.searchByResource.and.returnValue(observableOf({}));
|
||||||
spyOn(comp, 'initResourcePolicyLIst').and.callFake(() => ({}));
|
spyOn(comp, 'initResourcePolicyList').and.callFake(() => ({}));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -361,7 +369,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
|
|
||||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||||
expect(comp.initResourcePolicyLIst).toHaveBeenCalled();
|
expect(comp.initResourcePolicyList).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should notify error when delete is not successful', () => {
|
it('should notify error when delete is not successful', () => {
|
||||||
@@ -372,7 +380,7 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
|
|
||||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||||
expect(comp.initResourcePolicyLIst).toHaveBeenCalled();
|
expect(comp.initResourcePolicyList).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -384,68 +392,6 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasEPerson', () => {
|
|
||||||
it('should true when policy is link to the eperson', () => {
|
|
||||||
|
|
||||||
expect(comp.hasEPerson(anotherResourcePolicy)).toBeObservable(cold('(ab|)', {
|
|
||||||
a: false,
|
|
||||||
b: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should false when policy is not link to the eperson', () => {
|
|
||||||
|
|
||||||
expect(comp.hasEPerson(resourcePolicy)).toBeObservable(cold('(aa|)', {
|
|
||||||
a: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('hasGroup', () => {
|
|
||||||
it('should true when policy is link to the group', () => {
|
|
||||||
|
|
||||||
expect(comp.hasGroup(resourcePolicy)).toBeObservable(cold('(ab|)', {
|
|
||||||
a: false,
|
|
||||||
b: true
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should false when policy is not link to the group', () => {
|
|
||||||
|
|
||||||
expect(comp.hasGroup(anotherResourcePolicy)).toBeObservable(cold('(aa|)', {
|
|
||||||
a: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getEPersonName', () => {
|
|
||||||
it('should return the eperson name', () => {
|
|
||||||
|
|
||||||
expect(comp.getEPersonName(anotherResourcePolicy)).toBeObservable(cold('(ab|)', {
|
|
||||||
a: '',
|
|
||||||
b: 'User Test'
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getGroupName', () => {
|
|
||||||
it('should return the group name', () => {
|
|
||||||
|
|
||||||
expect(comp.getGroupName(resourcePolicy)).toBeObservable(cold('(ab|)', {
|
|
||||||
a: '',
|
|
||||||
b: 'testgroupname'
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format date properly', () => {
|
|
||||||
expect(comp.formatDate('2020-04-14T12:00:00Z')).toBe('2020-04-14');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select All Checkbox', () => {
|
it('should select All Checkbox', () => {
|
||||||
spyOn(comp, 'selectAllCheckbox').and.callThrough();
|
spyOn(comp, 'selectAllCheckbox').and.callThrough();
|
||||||
const checkbox = fixture.debugElement.query(By.css('table > thead > tr:nth-child(2) input'));
|
const checkbox = fixture.debugElement.query(By.css('table > thead > tr:nth-child(2) input'));
|
||||||
@@ -469,19 +415,6 @@ describe('ResourcePoliciesComponent test suite', () => {
|
|||||||
comp.redirectToResourcePolicyCreatePage();
|
comp.redirectToResourcePolicyCreatePage();
|
||||||
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to resource policy edit page', () => {
|
|
||||||
|
|
||||||
comp.redirectToResourcePolicyEditPage(resourcePolicy);
|
|
||||||
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should redirect to resource policy edit page', () => {
|
|
||||||
compAsAny.groupService.findByHref.and.returnValue(observableOf(createSuccessfulRemoteDataObject(GroupMock)));
|
|
||||||
|
|
||||||
comp.redirectToGroupEditPage(resourcePolicy);
|
|
||||||
expect(compAsAny.router.navigate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -2,48 +2,25 @@ import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, from as observableFrom, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, from as observableFrom, Observable, Subscription } from 'rxjs';
|
||||||
import {
|
import { concatMap, distinctUntilChanged, filter, map, reduce, scan, take } from 'rxjs/operators';
|
||||||
concatMap,
|
|
||||||
distinctUntilChanged,
|
|
||||||
filter,
|
|
||||||
map,
|
|
||||||
reduce,
|
|
||||||
scan,
|
|
||||||
startWith,
|
|
||||||
take
|
|
||||||
} from 'rxjs/operators';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { getAllSucceededRemoteData } from '../../core/shared/operators';
|
||||||
import { ResourcePolicyDataService } from '../../core/resource-policy/resource-policy-data.service';
|
import { ResourcePolicyDataService } from '../../core/resource-policy/resource-policy-data.service';
|
||||||
import {
|
|
||||||
getFirstSucceededRemoteDataPayload,
|
|
||||||
getFirstSucceededRemoteDataWithNotEmptyPayload,
|
|
||||||
getAllSucceededRemoteData
|
|
||||||
} from '../../core/shared/operators';
|
|
||||||
import { ResourcePolicy } from '../../core/resource-policy/models/resource-policy.model';
|
import { ResourcePolicy } from '../../core/resource-policy/models/resource-policy.model';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
import { Group } from '../../core/eperson/models/group.model';
|
|
||||||
import { GroupDataService } from '../../core/eperson/group-data.service';
|
import { GroupDataService } from '../../core/eperson/group-data.service';
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
|
||||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
|
||||||
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||||
import { RequestService } from '../../core/data/request.service';
|
import { RequestService } from '../../core/data/request.service';
|
||||||
import { NotificationsService } from '../notifications/notifications.service';
|
import { NotificationsService } from '../notifications/notifications.service';
|
||||||
import { dateToString, stringToNgbDateStruct } from '../date.util';
|
|
||||||
import { followLink } from '../utils/follow-link-config.model';
|
import { followLink } from '../utils/follow-link-config.model';
|
||||||
import { ACCESS_CONTROL_MODULE_PATH } from '../../app-routing-paths';
|
import { ResourcePolicyCheckboxEntry } from './entry/resource-policy-entry.component';
|
||||||
import { GROUP_EDIT_PATH } from '../../access-control/access-control-routing-paths';
|
|
||||||
|
|
||||||
interface ResourcePolicyCheckboxEntry {
|
|
||||||
id: string;
|
|
||||||
policy: ResourcePolicy;
|
|
||||||
checked: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-resource-policies',
|
selector: 'ds-resource-policies',
|
||||||
styleUrls: ['./resource-policies.component.scss'],
|
styleUrls: ['./resource-policies.component.scss'],
|
||||||
templateUrl: './resource-policies.component.html'
|
templateUrl: './resource-policies.component.html',
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Component that shows the policies for given resource
|
* Component that shows the policies for given resource
|
||||||
@@ -126,7 +103,7 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.initResourcePolicyLIst();
|
this.initResourcePolicyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,48 +145,6 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a date in simplified format (YYYY-MM-DD).
|
|
||||||
*
|
|
||||||
* @param date
|
|
||||||
* @return a string with formatted date
|
|
||||||
*/
|
|
||||||
formatDate(date: string): string {
|
|
||||||
return isNotEmpty(date) ? dateToString(stringToNgbDateStruct(date)) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the ePerson's name which the given policy is linked to
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
*/
|
|
||||||
getEPersonName(policy: ResourcePolicy): Observable<string> {
|
|
||||||
// TODO to be reviewed when https://github.com/DSpace/dspace-angular/issues/644 will be resolved
|
|
||||||
// return this.ePersonService.findByHref(policy._links.eperson.href).pipe(
|
|
||||||
return policy.eperson.pipe(
|
|
||||||
filter(() => this.isActive),
|
|
||||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
|
||||||
map((eperson: EPerson) => this.dsoNameService.getName(eperson)),
|
|
||||||
startWith('')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the group's name which the given policy is linked to
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
*/
|
|
||||||
getGroupName(policy: ResourcePolicy): Observable<string> {
|
|
||||||
// TODO to be reviewed when https://github.com/DSpace/dspace-angular/issues/644 will be resolved
|
|
||||||
// return this.groupService.findByHref(policy._links.group.href).pipe(
|
|
||||||
return policy.group.pipe(
|
|
||||||
filter(() => this.isActive),
|
|
||||||
getFirstSucceededRemoteDataWithNotEmptyPayload(),
|
|
||||||
map((group: Group) => this.dsoNameService.getName(group)),
|
|
||||||
startWith('')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all resource's policies
|
* Return all resource's policies
|
||||||
*
|
*
|
||||||
@@ -219,46 +154,14 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
return this.resourcePoliciesEntries$.asObservable();
|
return this.resourcePoliciesEntries$.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the given policy is linked to a ePerson
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
* @return an observable that emits true when the policy is linked to a ePerson, false otherwise
|
|
||||||
*/
|
|
||||||
hasEPerson(policy): Observable<boolean> {
|
|
||||||
// TODO to be reviewed when https://github.com/DSpace/dspace-angular/issues/644 will be resolved
|
|
||||||
// return this.ePersonService.findByHref(policy._links.eperson.href).pipe(
|
|
||||||
return policy.eperson.pipe(
|
|
||||||
filter(() => this.isActive),
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((eperson: EPerson) => isNotEmpty(eperson)),
|
|
||||||
startWith(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the given policy is linked to a group
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
* @return an observable that emits true when the policy is linked to a group, false otherwise
|
|
||||||
*/
|
|
||||||
hasGroup(policy): Observable<boolean> {
|
|
||||||
// TODO to be reviewed when https://github.com/DSpace/dspace-angular/issues/644 will be resolved
|
|
||||||
// return this.groupService.findByHref(policy._links.group.href).pipe(
|
|
||||||
return policy.group.pipe(
|
|
||||||
filter(() => this.isActive),
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((group: Group) => isNotEmpty(group)),
|
|
||||||
startWith(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the resource's policies list
|
* Initialize the resource's policies list
|
||||||
*/
|
*/
|
||||||
initResourcePolicyLIst() {
|
initResourcePolicyList() {
|
||||||
this.subs.push(this.resourcePolicyService.searchByResource(this.resourceUUID, null, false, true,
|
this.subs.push(this.resourcePolicyService.searchByResource(
|
||||||
followLink('eperson'), followLink('group')).pipe(
|
this.resourceUUID, null, false, true,
|
||||||
|
followLink('eperson'), followLink('group')
|
||||||
|
).pipe(
|
||||||
filter(() => this.isActive),
|
filter(() => this.isActive),
|
||||||
getAllSucceededRemoteData()
|
getAllSucceededRemoteData()
|
||||||
).subscribe((result) => {
|
).subscribe((result) => {
|
||||||
@@ -295,37 +198,6 @@ export class ResourcePoliciesComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect to resource policy editing page
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
*/
|
|
||||||
redirectToResourcePolicyEditPage(policy: ResourcePolicy): void {
|
|
||||||
this.router.navigate([`./edit`], {
|
|
||||||
relativeTo: this.route,
|
|
||||||
queryParams: {
|
|
||||||
policyId: policy.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect to group edit page
|
|
||||||
*
|
|
||||||
* @param policy The resource policy
|
|
||||||
*/
|
|
||||||
redirectToGroupEditPage(policy: ResourcePolicy): void {
|
|
||||||
this.subs.push(
|
|
||||||
this.groupService.findByHref(policy._links.group.href, false).pipe(
|
|
||||||
filter(() => this.isActive),
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((group: Group) => group.id)
|
|
||||||
).subscribe((groupUUID) => {
|
|
||||||
this.router.navigate([ACCESS_CONTROL_MODULE_PATH, GROUP_EDIT_PATH, groupUUID]);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select/unselect all checkbox in the list
|
* Select/unselect all checkbox in the list
|
||||||
*/
|
*/
|
||||||
|
@@ -15,9 +15,11 @@ import { GroupSearchBoxComponent } from './form/eperson-group-list/group-search-
|
|||||||
import { EpersonSearchBoxComponent } from './form/eperson-group-list/eperson-search-box/eperson-search-box.component';
|
import { EpersonSearchBoxComponent } from './form/eperson-group-list/eperson-search-box/eperson-search-box.component';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { SharedModule } from '../shared.module';
|
import { SharedModule } from '../shared.module';
|
||||||
|
import { ResourcePolicyEntryComponent } from './entry/resource-policy-entry.component';
|
||||||
|
|
||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
ResourcePoliciesComponent,
|
ResourcePoliciesComponent,
|
||||||
|
ResourcePolicyEntryComponent,
|
||||||
ResourcePolicyFormComponent,
|
ResourcePolicyFormComponent,
|
||||||
ResourcePolicyEditComponent,
|
ResourcePolicyEditComponent,
|
||||||
ResourcePolicyCreateComponent,
|
ResourcePolicyCreateComponent,
|
||||||
|
@@ -140,6 +140,11 @@ export class SearchComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() showScopeSelector = true;
|
@Input() showScopeSelector = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to track search statistics by sending updates to the rest api
|
||||||
|
*/
|
||||||
|
@Input() trackStatistics = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current configuration used during the search
|
* The current configuration used during the search
|
||||||
*/
|
*/
|
||||||
@@ -360,8 +365,13 @@ export class SearchComponent implements OnInit {
|
|||||||
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses })
|
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses })
|
||||||
).pipe(getFirstCompletedRemoteData())
|
).pipe(getFirstCompletedRemoteData())
|
||||||
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
.subscribe((results: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||||
if (results.hasSucceeded && results.payload?.page?.length > 0) {
|
if (results.hasSucceeded) {
|
||||||
this.resultFound.emit(results.payload);
|
if (this.trackStatistics) {
|
||||||
|
this.service.trackSearch(searchOptions, results.payload);
|
||||||
|
}
|
||||||
|
if (results.payload?.page?.length > 0) {
|
||||||
|
this.resultFound.emit(results.payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.resultsRD$.next(results);
|
this.resultsRD$.next(results);
|
||||||
});
|
});
|
||||||
|
@@ -19,7 +19,7 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
|
|||||||
templateUrl: '../theme-support/themed.component.html',
|
templateUrl: '../theme-support/themed.component.html',
|
||||||
})
|
})
|
||||||
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
||||||
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showSidebar', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject'];
|
protected inAndOutputNames: (keyof SearchComponent & keyof this)[] = ['configurationList', 'context', 'configuration', 'fixedFilterQuery', 'useCachedVersionIfAvailable', 'inPlaceSearch', 'linkType', 'paginationId', 'searchEnabled', 'sideBarWidth', 'searchFormPlaceholder', 'selectable', 'selectionConfig', 'showSidebar', 'showViewModes', 'useUniquePageId', 'viewModeList', 'showScopeSelector', 'resultFound', 'deselectObject', 'selectObject', 'trackStatistics'];
|
||||||
|
|
||||||
@Input() configurationList: SearchConfigurationOption[] = [];
|
@Input() configurationList: SearchConfigurationOption[] = [];
|
||||||
|
|
||||||
@@ -57,6 +57,8 @@ export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
|
|||||||
|
|
||||||
@Input() showScopeSelector = true;
|
@Input() showScopeSelector = true;
|
||||||
|
|
||||||
|
@Input() trackStatistics = false;
|
||||||
|
|
||||||
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter<SearchObjects<DSpaceObject>>();
|
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter<SearchObjects<DSpaceObject>>();
|
||||||
|
|
||||||
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
9
src/app/shared/testing/data-service.stub.ts
Normal file
9
src/app/shared/testing/data-service.stub.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
|
|
||||||
|
export class DataServiceStub {
|
||||||
|
|
||||||
|
invalidateByHref(_href: string): Observable<boolean> {
|
||||||
|
return observableOf(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2242
src/assets/i18n/el.json5
Normal file
2242
src/assets/i18n/el.json5
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1283,6 +1283,7 @@
|
|||||||
|
|
||||||
"cookies.consent.purpose.statistical": "Statistical",
|
"cookies.consent.purpose.statistical": "Statistical",
|
||||||
|
|
||||||
|
"curation-task.task.citationpage.label": "Generate Citation Page",
|
||||||
|
|
||||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||||
|
|
||||||
@@ -1509,7 +1510,7 @@
|
|||||||
|
|
||||||
"forgot-password.form.head": "Forgot Password",
|
"forgot-password.form.head": "Forgot Password",
|
||||||
|
|
||||||
"forgot-password.form.info": "Enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.",
|
"forgot-password.form.info": "Enter a new password in the box below, and confirm it by typing it again into the second box.",
|
||||||
|
|
||||||
"forgot-password.form.card.security": "Security",
|
"forgot-password.form.card.security": "Security",
|
||||||
|
|
||||||
@@ -1525,8 +1526,6 @@
|
|||||||
|
|
||||||
"forgot-password.form.error.matching-passwords": "The passwords do not match.",
|
"forgot-password.form.error.matching-passwords": "The passwords do not match.",
|
||||||
|
|
||||||
"forgot-password.form.error.password-length": "The password should be at least 6 characters long.",
|
|
||||||
|
|
||||||
"forgot-password.form.notification.error.title": "Error when trying to submit new password",
|
"forgot-password.form.notification.error.title": "Error when trying to submit new password",
|
||||||
|
|
||||||
"forgot-password.form.notification.success.content": "The password reset was successful. You have been logged in as the created user.",
|
"forgot-password.form.notification.success.content": "The password reset was successful. You have been logged in as the created user.",
|
||||||
@@ -1536,7 +1535,6 @@
|
|||||||
"forgot-password.form.submit": "Submit password",
|
"forgot-password.form.submit": "Submit password",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"form.add": "Add more",
|
"form.add": "Add more",
|
||||||
|
|
||||||
"form.add-help": "Click here to add the current entry and to add another one",
|
"form.add-help": "Click here to add the current entry and to add another one",
|
||||||
@@ -3096,9 +3094,7 @@
|
|||||||
|
|
||||||
"profile.security.form.error.matching-passwords": "The passwords do not match.",
|
"profile.security.form.error.matching-passwords": "The passwords do not match.",
|
||||||
|
|
||||||
"profile.security.form.error.password-length": "The password should be at least 6 characters long.",
|
"profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box.",
|
||||||
|
|
||||||
"profile.security.form.info": "Optionally, you can enter a new password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.",
|
|
||||||
|
|
||||||
"profile.security.form.label.password": "Password",
|
"profile.security.form.label.password": "Password",
|
||||||
|
|
||||||
@@ -3110,10 +3106,10 @@
|
|||||||
|
|
||||||
"profile.security.form.notifications.error.title": "Error changing passwords",
|
"profile.security.form.notifications.error.title": "Error changing passwords",
|
||||||
|
|
||||||
"profile.security.form.notifications.error.not-long-enough": "The password has to be at least 6 characters long.",
|
|
||||||
|
|
||||||
"profile.security.form.notifications.error.not-same": "The provided passwords are not the same.",
|
"profile.security.form.notifications.error.not-same": "The provided passwords are not the same.",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.error.general": "Please fill required fields of security form.",
|
||||||
|
|
||||||
"profile.title": "Update Profile",
|
"profile.title": "Update Profile",
|
||||||
|
|
||||||
"profile.card.researcher": "Researcher Profile",
|
"profile.card.researcher": "Researcher Profile",
|
||||||
@@ -3196,7 +3192,7 @@
|
|||||||
|
|
||||||
"register-page.create-profile.security.header": "Security",
|
"register-page.create-profile.security.header": "Security",
|
||||||
|
|
||||||
"register-page.create-profile.security.info": "Please enter a password in the box below, and confirm it by typing it again into the second box. It should be at least six characters long.",
|
"register-page.create-profile.security.info": "Please enter a password in the box below, and confirm it by typing it again into the second box.",
|
||||||
|
|
||||||
"register-page.create-profile.security.label.password": "Password *",
|
"register-page.create-profile.security.label.password": "Password *",
|
||||||
|
|
||||||
@@ -3206,8 +3202,6 @@
|
|||||||
|
|
||||||
"register-page.create-profile.security.error.matching-passwords": "The passwords do not match.",
|
"register-page.create-profile.security.error.matching-passwords": "The passwords do not match.",
|
||||||
|
|
||||||
"register-page.create-profile.security.error.password-length": "The password should be at least 6 characters long.",
|
|
||||||
|
|
||||||
"register-page.create-profile.submit": "Complete Registration",
|
"register-page.create-profile.submit": "Complete Registration",
|
||||||
|
|
||||||
"register-page.create-profile.submit.error.content": "Something went wrong while registering a new user.",
|
"register-page.create-profile.submit.error.content": "Something went wrong while registering a new user.",
|
||||||
|
@@ -17,7 +17,9 @@ import { BrowseByConfig } from './browse-by-config.interface';
|
|||||||
import { BundleConfig } from './bundle-config.interface';
|
import { BundleConfig } from './bundle-config.interface';
|
||||||
import { ActuatorsConfig } from './actuators.config';
|
import { ActuatorsConfig } from './actuators.config';
|
||||||
import { InfoConfig } from './info-config.interface';
|
import { InfoConfig } from './info-config.interface';
|
||||||
|
import { CommunityListConfig } from './community-list-config.interface';
|
||||||
import { HomeConfig } from './homepage-config.interface';
|
import { HomeConfig } from './homepage-config.interface';
|
||||||
|
|
||||||
interface AppConfig extends Config {
|
interface AppConfig extends Config {
|
||||||
ui: UIServerConfig;
|
ui: UIServerConfig;
|
||||||
rest: ServerConfig;
|
rest: ServerConfig;
|
||||||
@@ -31,6 +33,8 @@ interface AppConfig extends Config {
|
|||||||
defaultLanguage: string;
|
defaultLanguage: string;
|
||||||
languages: LangConfig[];
|
languages: LangConfig[];
|
||||||
browseBy: BrowseByConfig;
|
browseBy: BrowseByConfig;
|
||||||
|
communityList: CommunityListConfig;
|
||||||
|
homePage: HomeConfig;
|
||||||
item: ItemConfig;
|
item: ItemConfig;
|
||||||
collection: CollectionPageConfig;
|
collection: CollectionPageConfig;
|
||||||
themes: ThemeConfig[];
|
themes: ThemeConfig[];
|
||||||
@@ -38,7 +42,6 @@ interface AppConfig extends Config {
|
|||||||
bundle: BundleConfig;
|
bundle: BundleConfig;
|
||||||
actuators: ActuatorsConfig
|
actuators: ActuatorsConfig
|
||||||
info: InfoConfig;
|
info: InfoConfig;
|
||||||
homePage: HomeConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,21 +1,32 @@
|
|||||||
import { Config } from './config.interface';
|
import { Config } from './config.interface';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config that determines how the dropdown list of years are created for browse-by-date components
|
* Config that determines how the dropdown list of years are created for
|
||||||
|
* browse-by-date components.
|
||||||
*/
|
*/
|
||||||
export interface BrowseByConfig extends Config {
|
export interface BrowseByConfig extends Config {
|
||||||
/**
|
/**
|
||||||
* The max amount of years to display using jumps of one year (current year - oneYearLimit)
|
* The max amount of years to display using jumps of one year
|
||||||
|
* (current year - oneYearLimit)
|
||||||
*/
|
*/
|
||||||
oneYearLimit: number;
|
oneYearLimit: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit for years to display using jumps of five years (current year - fiveYearLimit)
|
* Limit for years to display using jumps of five years
|
||||||
|
* (current year - fiveYearLimit)
|
||||||
*/
|
*/
|
||||||
fiveYearLimit: number;
|
fiveYearLimit: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The absolute lowest year to display in the dropdown when no lowest date can be found for all items
|
* The absolute lowest year to display in the dropdown when no lowest date can
|
||||||
|
* be found for all items.
|
||||||
*/
|
*/
|
||||||
defaultLowerLimit: number;
|
defaultLowerLimit: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of entries in the viewport of a paginated browse-by list.
|
||||||
|
* Rounded to the nearest size in the list of selectable sizes on the settings
|
||||||
|
* menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
|
||||||
|
*/
|
||||||
|
pageSize: number;
|
||||||
}
|
}
|
||||||
|
5
src/config/community-list-config.interface.ts
Normal file
5
src/config/community-list-config.interface.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Config } from './config.interface';
|
||||||
|
|
||||||
|
export interface CommunityListConfig extends Config {
|
||||||
|
pageSize: number;
|
||||||
|
}
|
@@ -17,7 +17,9 @@ import { UIServerConfig } from './ui-server-config.interface';
|
|||||||
import { BundleConfig } from './bundle-config.interface';
|
import { BundleConfig } from './bundle-config.interface';
|
||||||
import { ActuatorsConfig } from './actuators.config';
|
import { ActuatorsConfig } from './actuators.config';
|
||||||
import { InfoConfig } from './info-config.interface';
|
import { InfoConfig } from './info-config.interface';
|
||||||
|
import { CommunityListConfig } from './community-list-config.interface';
|
||||||
import { HomeConfig } from './homepage-config.interface';
|
import { HomeConfig } from './homepage-config.interface';
|
||||||
|
|
||||||
export class DefaultAppConfig implements AppConfig {
|
export class DefaultAppConfig implements AppConfig {
|
||||||
production = false;
|
production = false;
|
||||||
|
|
||||||
@@ -196,7 +198,8 @@ export class DefaultAppConfig implements AppConfig {
|
|||||||
{ code: 'sv', label: 'Svenska', active: true },
|
{ code: 'sv', label: 'Svenska', active: true },
|
||||||
{ code: 'tr', label: 'Türkçe', active: true },
|
{ code: 'tr', label: 'Türkçe', active: true },
|
||||||
{ code: 'kk', label: 'Қазақ', active: true },
|
{ code: 'kk', label: 'Қазақ', active: true },
|
||||||
{ code: 'bn', label: 'বাংলা', active: true }
|
{ code: 'bn', label: 'বাংলা', active: true },
|
||||||
|
{ code: 'el', label: 'Ελληνικά', active: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
// Browse-By Pages
|
// Browse-By Pages
|
||||||
@@ -206,7 +209,27 @@ export class DefaultAppConfig implements AppConfig {
|
|||||||
// Limit for years to display using jumps of five years (current year - fiveYearLimit)
|
// Limit for years to display using jumps of five years (current year - fiveYearLimit)
|
||||||
fiveYearLimit: 30,
|
fiveYearLimit: 30,
|
||||||
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
||||||
defaultLowerLimit: 1900
|
defaultLowerLimit: 1900,
|
||||||
|
// The number of entries in a paginated browse results list.
|
||||||
|
// Rounded to the nearest size in the list of selectable sizes on the
|
||||||
|
// settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
|
||||||
|
pageSize: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
communityList: CommunityListConfig = {
|
||||||
|
pageSize: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
homePage: HomeConfig = {
|
||||||
|
recentSubmissions: {
|
||||||
|
//The number of item showing in recent submission components
|
||||||
|
pageSize: 5,
|
||||||
|
//sort record of recent submission
|
||||||
|
sortField: 'dc.date.accessioned',
|
||||||
|
},
|
||||||
|
topLevelCommunityList: {
|
||||||
|
pageSize: 5
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Item Config
|
// Item Config
|
||||||
@@ -339,13 +362,4 @@ export class DefaultAppConfig implements AppConfig {
|
|||||||
enableEndUserAgreement: true,
|
enableEndUserAgreement: true,
|
||||||
enablePrivacyStatement: true
|
enablePrivacyStatement: true
|
||||||
};
|
};
|
||||||
// Home Pages
|
|
||||||
homePage: HomeConfig = {
|
|
||||||
recentSubmissions: {
|
|
||||||
//The number of item showing in recent submission components
|
|
||||||
pageSize: 5,
|
|
||||||
//sort record of recent submission
|
|
||||||
sortField: 'dc.date.accessioned',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@@ -16,5 +16,7 @@ export interface HomeConfig extends Config {
|
|||||||
sortField: string;
|
sortField: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topLevelCommunityList: {
|
||||||
|
pageSize: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -189,6 +189,10 @@ export const environment: BuildConfig = {
|
|||||||
code: 'bn',
|
code: 'bn',
|
||||||
label: 'বাংলা',
|
label: 'বাংলা',
|
||||||
active: true,
|
active: true,
|
||||||
|
}, {
|
||||||
|
code: 'el',
|
||||||
|
label: 'Ελληνικά',
|
||||||
|
active: true,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
// Browse-By Pages
|
// Browse-By Pages
|
||||||
@@ -199,6 +203,23 @@ export const environment: BuildConfig = {
|
|||||||
fiveYearLimit: 30,
|
fiveYearLimit: 30,
|
||||||
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
// The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items)
|
||||||
defaultLowerLimit: 1900,
|
defaultLowerLimit: 1900,
|
||||||
|
// The number of entries in a paginated browse results list.
|
||||||
|
// Rounded to the nearest size in the list of selectable sizes on the
|
||||||
|
// settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
communityList: {
|
||||||
|
pageSize: 20
|
||||||
|
},
|
||||||
|
homePage: {
|
||||||
|
recentSubmissions: {
|
||||||
|
pageSize: 5,
|
||||||
|
//sort record of recent submission
|
||||||
|
sortField: 'dc.date.accessioned',
|
||||||
|
},
|
||||||
|
topLevelCommunityList: {
|
||||||
|
pageSize: 5
|
||||||
|
}
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
edit: {
|
edit: {
|
||||||
@@ -248,12 +269,4 @@ export const environment: BuildConfig = {
|
|||||||
enableEndUserAgreement: true,
|
enableEndUserAgreement: true,
|
||||||
enablePrivacyStatement: true,
|
enablePrivacyStatement: true,
|
||||||
},
|
},
|
||||||
//Home Page
|
|
||||||
homePage: {
|
|
||||||
recentSubmissions: {
|
|
||||||
pageSize: 5,
|
|
||||||
//sort record of recent submission
|
|
||||||
sortField: 'dc.date.accessioned',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommunityPageSubCollectionListComponent as BaseComponent }
|
||||||
|
from '../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-page-sub-collection-list',
|
||||||
|
// styleUrls: ['./community-page-sub-collection-list.component.scss'],
|
||||||
|
styleUrls: ['../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss'],
|
||||||
|
// templateUrl: './community-page-sub-collection-list.component.html',
|
||||||
|
templateUrl: '../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component.html'
|
||||||
|
})
|
||||||
|
export class CommunityPageSubCollectionListComponent extends BaseComponent {}
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommunityPageSubCommunityListComponent as BaseComponent }
|
||||||
|
from '../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-community-page-sub-community-list',
|
||||||
|
// styleUrls: ['./community-page-sub-community-list.component.scss'],
|
||||||
|
styleUrls: ['../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component.scss'],
|
||||||
|
// templateUrl: './community-page-sub-community-list.component.html',
|
||||||
|
templateUrl: '../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component.html'
|
||||||
|
})
|
||||||
|
export class CommunityPageSubCommunityListComponent extends BaseComponent {}
|
@@ -99,6 +99,12 @@ import {
|
|||||||
import { LoadingComponent } from './app/shared/loading/loading.component';
|
import { LoadingComponent } from './app/shared/loading/loading.component';
|
||||||
import { SearchResultsComponent } from './app/shared/search/search-results/search-results.component';
|
import { SearchResultsComponent } from './app/shared/search/search-results/search-results.component';
|
||||||
import { AdminSidebarComponent } from './app/admin/admin-sidebar/admin-sidebar.component';
|
import { AdminSidebarComponent } from './app/admin/admin-sidebar/admin-sidebar.component';
|
||||||
|
import {
|
||||||
|
CommunityPageSubCommunityListComponent
|
||||||
|
} from './app/community-page/sub-community-list/community-page-sub-community-list.component';
|
||||||
|
import {
|
||||||
|
CommunityPageSubCollectionListComponent
|
||||||
|
} from './app/community-page/sub-collection-list/community-page-sub-collection-list.component';
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
FileSectionComponent,
|
FileSectionComponent,
|
||||||
@@ -118,6 +124,8 @@ const DECLARATIONS = [
|
|||||||
ItemStatisticsPageComponent,
|
ItemStatisticsPageComponent,
|
||||||
SiteStatisticsPageComponent,
|
SiteStatisticsPageComponent,
|
||||||
CommunityPageComponent,
|
CommunityPageComponent,
|
||||||
|
CommunityPageSubCommunityListComponent,
|
||||||
|
CommunityPageSubCollectionListComponent,
|
||||||
CollectionPageComponent,
|
CollectionPageComponent,
|
||||||
ItemPageComponent,
|
ItemPageComponent,
|
||||||
FullItemPageComponent,
|
FullItemPageComponent,
|
||||||
@@ -196,6 +204,9 @@ const DECLARATIONS = [
|
|||||||
ComcolModule,
|
ComcolModule,
|
||||||
],
|
],
|
||||||
declarations: DECLARATIONS,
|
declarations: DECLARATIONS,
|
||||||
|
exports: [
|
||||||
|
CommunityPageSubCollectionListComponent
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user