+ {{eperson.id}} |
-
- {{ dsoNameService.getName(ePerson.eperson) }}
+ {{ dsoNameService.getName(eperson) }}
|
- {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
- {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }}
+ {{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}
+ {{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }}
|
-
-
@@ -156,9 +132,10 @@ {{messagePrefix + '.headMembers' | translate}}
-
- {{messagePrefix + '.no-members-yet' | translate}}
+ {{messagePrefix + '.no-items' | translate}}
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
index 7c8db399bcd..5d97dcade8d 100644
--- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
+++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
@@ -17,7 +17,7 @@ import { Group } from '../../../../core/eperson/models/group.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
-import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock';
+import { GroupMock } from '../../../../shared/testing/group-mock';
import { MembersListComponent } from './members-list.component';
import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
@@ -39,28 +39,26 @@ describe('MembersListComponent', () => {
let ePersonDataServiceStub: any;
let groupsDataServiceStub: any;
let activeGroup;
- let allEPersons: EPerson[];
- let allGroups: Group[];
let epersonMembers: EPerson[];
- let subgroupMembers: Group[];
+ let epersonNonMembers: EPerson[];
let paginationService;
beforeEach(waitForAsync(() => {
activeGroup = GroupMock;
epersonMembers = [EPersonMock2];
- subgroupMembers = [GroupMock2];
- allEPersons = [EPersonMock, EPersonMock2];
- allGroups = [GroupMock, GroupMock2];
+ epersonNonMembers = [EPersonMock];
ePersonDataServiceStub = {
activeGroup: activeGroup,
epersonMembers: epersonMembers,
- subgroupMembers: subgroupMembers,
+ epersonNonMembers: epersonNonMembers,
+ // This method is used to get all the current members
findListByHref(_href: string): Observable>> {
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getEPersonMembers()));
},
- searchByScope(scope: string, query: string): Observable>> {
+ // This method is used to search across *non-members*
+ searchNonMembers(query: string, group: string): Observable>> {
if (query === '') {
- return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons));
+ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), epersonNonMembers));
}
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
},
@@ -77,22 +75,22 @@ describe('MembersListComponent', () => {
groupsDataServiceStub = {
activeGroup: activeGroup,
epersonMembers: epersonMembers,
- subgroupMembers: subgroupMembers,
- allGroups: allGroups,
+ epersonNonMembers: epersonNonMembers,
getActiveGroup(): Observable {
return observableOf(activeGroup);
},
getEPersonMembers() {
return this.epersonMembers;
},
- searchGroups(query: string): Observable>> {
- if (query === '') {
- return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups));
- }
- return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
- },
- addMemberToGroup(parentGroup, eperson: EPerson): Observable {
- this.epersonMembers = [...this.epersonMembers, eperson];
+ addMemberToGroup(parentGroup, epersonToAdd: EPerson): Observable {
+ // Add eperson to list of members
+ this.epersonMembers = [...this.epersonMembers, epersonToAdd];
+ // Remove eperson from list of non-members
+ this.epersonNonMembers.forEach( (eperson: EPerson, index: number) => {
+ if (eperson.id === epersonToAdd.id) {
+ this.epersonNonMembers.splice(index, 1);
+ }
+ });
return observableOf(new RestResponse(true, 200, 'Success'));
},
clearGroupsRequests() {
@@ -105,14 +103,14 @@ describe('MembersListComponent', () => {
return '/access-control/groups/' + group.id;
},
deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable {
- this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => {
- if (eperson.id !== epersonToDelete.id) {
- return eperson;
+ // Remove eperson from list of members
+ this.epersonMembers.forEach( (eperson: EPerson, index: number) => {
+ if (eperson.id === epersonToDelete.id) {
+ this.epersonMembers.splice(index, 1);
}
});
- if (this.epersonMembers === undefined) {
- this.epersonMembers = [];
- }
+ // Add eperson to list of non-members
+ this.epersonNonMembers = [...this.epersonNonMembers, epersonToDelete];
return observableOf(new RestResponse(true, 200, 'Success'));
}
};
@@ -160,13 +158,37 @@ describe('MembersListComponent', () => {
expect(comp).toBeDefined();
}));
- it('should show list of eperson members of current active group', () => {
- const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child'));
- expect(epersonIdsFound.length).toEqual(1);
- epersonMembers.map((eperson: EPerson) => {
- expect(epersonIdsFound.find((foundEl) => {
- return (foundEl.nativeElement.textContent.trim() === eperson.uuid);
- })).toBeTruthy();
+ describe('current members list', () => {
+ it('should show list of eperson members of current active group', () => {
+ const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child'));
+ expect(epersonIdsFound.length).toEqual(1);
+ epersonMembers.map((eperson: EPerson) => {
+ expect(epersonIdsFound.find((foundEl) => {
+ return (foundEl.nativeElement.textContent.trim() === eperson.uuid);
+ })).toBeTruthy();
+ });
+ });
+
+ it('should show a delete button next to each member', () => {
+ const epersonsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tbody tr'));
+ epersonsFound.map((foundEPersonRowElement: DebugElement) => {
+ const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
+ const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
+ expect(addButton).toBeNull();
+ expect(deleteButton).not.toBeNull();
+ });
+ });
+
+ describe('if first delete button is pressed', () => {
+ beforeEach(() => {
+ const deleteButton: DebugElement = fixture.debugElement.query(By.css('#ePeopleMembersOfGroup tbody .fa-trash-alt'));
+ deleteButton.nativeElement.click();
+ fixture.detectChanges();
+ });
+ it('then no ePerson remains as a member of the active group.', () => {
+ const epersonsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tbody tr'));
+ expect(epersonsFound.length).toEqual(0);
+ });
});
});
@@ -174,76 +196,40 @@ describe('MembersListComponent', () => {
describe('when searching without query', () => {
let epersonsFound: DebugElement[];
beforeEach(fakeAsync(() => {
- spyOn(component, 'isMemberOfGroup').and.callFake((ePerson: EPerson) => {
- return observableOf(activeGroup.epersons.includes(ePerson));
- });
component.search({ scope: 'metadata', query: '' });
tick();
fixture.detectChanges();
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
- // Stop using the fake spy function (because otherwise the clicking on the buttons will not change anything
- // because they don't change the value of activeGroup.epersons)
- jasmine.getEnv().allowRespy(true);
- spyOn(component, 'isMemberOfGroup').and.callThrough();
}));
- it('should display all epersons', () => {
- expect(epersonsFound.length).toEqual(2);
+ it('should display only non-members of the group', () => {
+ const epersonIdsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr td:first-child'));
+ expect(epersonIdsFound.length).toEqual(1);
+ epersonNonMembers.map((eperson: EPerson) => {
+ expect(epersonIdsFound.find((foundEl) => {
+ return (foundEl.nativeElement.textContent.trim() === eperson.uuid);
+ })).toBeTruthy();
+ });
});
- describe('if eperson is already a eperson', () => {
- it('should have delete button, else it should have add button', () => {
- const memberIds: string[] = activeGroup.epersons.map((ePerson: EPerson) => ePerson.id);
- epersonsFound.map((foundEPersonRowElement: DebugElement) => {
- const epersonId: DebugElement = foundEPersonRowElement.query(By.css('td:first-child'));
- const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
- const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
- if (memberIds.includes(epersonId.nativeElement.textContent)) {
- expect(addButton).toBeNull();
- expect(deleteButton).not.toBeNull();
- } else {
- expect(deleteButton).toBeNull();
- expect(addButton).not.toBeNull();
- }
- });
+ it('should display an add button next to non-members, not a delete button', () => {
+ epersonsFound.map((foundEPersonRowElement: DebugElement) => {
+ const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
+ const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
+ expect(addButton).not.toBeNull();
+ expect(deleteButton).toBeNull();
});
});
describe('if first add button is pressed', () => {
- beforeEach(fakeAsync(() => {
+ beforeEach(() => {
const addButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus'));
addButton.nativeElement.click();
- tick();
fixture.detectChanges();
- }));
- it('then all the ePersons are member of the active group', () => {
- epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
- expect(epersonsFound.length).toEqual(2);
- epersonsFound.map((foundEPersonRowElement: DebugElement) => {
- const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
- const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
- expect(addButton).toBeNull();
- expect(deleteButton).not.toBeNull();
- });
});
- });
-
- describe('if first delete button is pressed', () => {
- beforeEach(fakeAsync(() => {
- const deleteButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-trash-alt'));
- deleteButton.nativeElement.click();
- tick();
- fixture.detectChanges();
- }));
- it('then no ePerson is member of the active group', () => {
+ it('then all (two) ePersons are member of the active group. No non-members left', () => {
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
- expect(epersonsFound.length).toEqual(2);
- epersonsFound.map((foundEPersonRowElement: DebugElement) => {
- const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
- const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
- expect(deleteButton).toBeNull();
- expect(addButton).not.toBeNull();
- });
+ expect(epersonsFound.length).toEqual(0);
});
});
});
diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
index b3e686c0123..feb90b52b37 100644
--- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
+++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
@@ -4,28 +4,23 @@ import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import {
Observable,
- of as observableOf,
Subscription,
- BehaviorSubject,
- combineLatest as observableCombineLatest,
- ObservedValueOf,
+ BehaviorSubject
} from 'rxjs';
-import { defaultIfEmpty, map, mergeMap, switchMap, take } from 'rxjs/operators';
-import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model';
+import { map, switchMap, take } from 'rxjs/operators';
+import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { Group } from '../../../../core/eperson/models/group.model';
import {
- getFirstSucceededRemoteData,
getFirstCompletedRemoteData,
getAllCompletedRemoteData,
getRemoteDataPayload
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
-import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model';
import { PaginationService } from '../../../../core/pagination/pagination.service';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
@@ -34,8 +29,8 @@ import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
*/
enum SubKey {
ActiveGroup,
- MembersDTO,
- SearchResultsDTO,
+ Members,
+ SearchResults,
}
/**
@@ -96,11 +91,11 @@ export class MembersListComponent implements OnInit, OnDestroy {
/**
* EPeople being displayed in search result, initially all members, after search result of search
*/
- ePeopleSearchDtos: BehaviorSubject> = new BehaviorSubject>(undefined);
+ ePeopleSearch: BehaviorSubject> = new BehaviorSubject>(undefined);
/**
* List of EPeople members of currently active group being edited
*/
- ePeopleMembersOfGroupDtos: BehaviorSubject> = new BehaviorSubject>(undefined);
+ ePeopleMembersOfGroup: BehaviorSubject> = new BehaviorSubject>(undefined);
/**
* Pagination config used to display the list of EPeople that are result of EPeople search
@@ -129,7 +124,6 @@ export class MembersListComponent implements OnInit, OnDestroy {
// Current search in edit group - epeople search form
currentSearchQuery: string;
- currentSearchScope: string;
// Whether or not user has done a EPeople search yet
searchDone: boolean;
@@ -148,18 +142,17 @@ export class MembersListComponent implements OnInit, OnDestroy {
public dsoNameService: DSONameService,
) {
this.currentSearchQuery = '';
- this.currentSearchScope = 'metadata';
}
ngOnInit(): void {
this.searchForm = this.formBuilder.group(({
- scope: 'metadata',
query: '',
}));
this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => {
if (activeGroup != null) {
this.groupBeingEdited = activeGroup;
this.retrieveMembers(this.config.currentPage);
+ this.search({query: ''});
}
}));
}
@@ -171,8 +164,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
* @private
*/
retrieveMembers(page: number): void {
- this.unsubFrom(SubKey.MembersDTO);
- this.subs.set(SubKey.MembersDTO,
+ this.unsubFrom(SubKey.Members);
+ this.subs.set(SubKey.Members,
this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
switchMap((currentPagination) => {
return this.ePersonDataService.findListByHref(this.groupBeingEdited._links.epersons.href, {
@@ -189,49 +182,12 @@ export class MembersListComponent implements OnInit, OnDestroy {
return rd;
}
}),
- switchMap((epersonListRD: RemoteData>) => {
- const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => {
- const dto$: Observable = observableCombineLatest(
- this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => {
- const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
- epersonDtoModel.eperson = member;
- epersonDtoModel.memberOfGroup = isMember;
- return epersonDtoModel;
- });
- return dto$;
- })]);
- return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
- return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
- }));
- }))
- .subscribe((paginatedListOfDTOs: PaginatedList) => {
- this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs);
+ getRemoteDataPayload())
+ .subscribe((paginatedListOfEPersons: PaginatedList) => {
+ this.ePeopleMembersOfGroup.next(paginatedListOfEPersons);
}));
}
- /**
- * Whether the given ePerson is a member of the group currently being edited
- * @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited
- */
- isMemberOfGroup(possibleMember: EPerson): Observable {
- return this.groupDataService.getActiveGroup().pipe(take(1),
- mergeMap((group: Group) => {
- if (group != null) {
- return this.ePersonDataService.findListByHref(group._links.epersons.href, {
- currentPage: 1,
- elementsPerPage: 9999
- })
- .pipe(
- getFirstSucceededRemoteData(),
- getRemoteDataPayload(),
- map((listEPeopleInGroup: PaginatedList) => listEPeopleInGroup.page.filter((ePersonInList: EPerson) => ePersonInList.id === possibleMember.id)),
- map((epeople: EPerson[]) => epeople.length > 0));
- } else {
- return observableOf(false);
- }
- }));
- }
-
/**
* Unsubscribe from a subscription if it's still subscribed, and remove it from the map of
* active subscriptions
@@ -248,14 +204,18 @@ export class MembersListComponent implements OnInit, OnDestroy {
/**
* Deletes a given EPerson from the members list of the group currently being edited
- * @param ePerson EPerson we want to delete as member from group that is currently being edited
+ * @param eperson EPerson we want to delete as member from group that is currently being edited
*/
- deleteMemberFromGroup(ePerson: EpersonDtoModel) {
- ePerson.memberOfGroup = false;
+ deleteMemberFromGroup(eperson: EPerson) {
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) {
- const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson);
- this.showNotifications('deleteMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup);
+ const response = this.groupDataService.deleteMemberFromGroup(activeGroup, eperson);
+ this.showNotifications('deleteMember', response, this.dsoNameService.getName(eperson), activeGroup);
+ // Reload search results (if there is an active query).
+ // This will potentially add this deleted subgroup into the list of search results.
+ if (this.currentSearchQuery != null) {
+ this.search({query: this.currentSearchQuery});
+ }
} else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
}
@@ -264,14 +224,18 @@ export class MembersListComponent implements OnInit, OnDestroy {
/**
* Adds a given EPerson to the members list of the group currently being edited
- * @param ePerson EPerson we want to add as member to group that is currently being edited
+ * @param eperson EPerson we want to add as member to group that is currently being edited
*/
- addMemberToGroup(ePerson: EpersonDtoModel) {
- ePerson.memberOfGroup = true;
+ addMemberToGroup(eperson: EPerson) {
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) {
- const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson);
- this.showNotifications('addMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup);
+ const response = this.groupDataService.addMemberToGroup(activeGroup, eperson);
+ this.showNotifications('addMember', response, this.dsoNameService.getName(eperson), activeGroup);
+ // Reload search results (if there is an active query).
+ // This will potentially add this deleted subgroup into the list of search results.
+ if (this.currentSearchQuery != null) {
+ this.search({query: this.currentSearchQuery});
+ }
} else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
}
@@ -279,37 +243,25 @@ export class MembersListComponent implements OnInit, OnDestroy {
}
/**
- * Search in the EPeople by name, email or metadata
- * @param data Contains scope and query param
+ * Search all EPeople who are NOT a member of the current group by name, email or metadata
+ * @param data Contains query param
*/
search(data: any) {
- this.unsubFrom(SubKey.SearchResultsDTO);
- this.subs.set(SubKey.SearchResultsDTO,
+ this.unsubFrom(SubKey.SearchResults);
+ this.subs.set(SubKey.SearchResults,
this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
switchMap((paginationOptions) => {
-
const query: string = data.query;
- const scope: string = data.scope;
if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
- this.router.navigate([], {
- queryParamsHandling: 'merge'
- });
this.currentSearchQuery = query;
this.paginationService.resetPage(this.configSearch.id);
}
- if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) {
- this.router.navigate([], {
- queryParamsHandling: 'merge'
- });
- this.currentSearchScope = scope;
- this.paginationService.resetPage(this.configSearch.id);
- }
this.searchDone = true;
- return this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
+ return this.ePersonDataService.searchNonMembers(this.currentSearchQuery, this.groupBeingEdited.id, {
currentPage: paginationOptions.currentPage,
elementsPerPage: paginationOptions.pageSize
- });
+ }, false, true);
}),
getAllCompletedRemoteData(),
map((rd: RemoteData) => {
@@ -319,23 +271,9 @@ export class MembersListComponent implements OnInit, OnDestroy {
return rd;
}
}),
- switchMap((epersonListRD: RemoteData>) => {
- const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => {
- const dto$: Observable = observableCombineLatest(
- this.isMemberOfGroup(member), (isMember: ObservedValueOf>) => {
- const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
- epersonDtoModel.eperson = member;
- epersonDtoModel.memberOfGroup = isMember;
- return epersonDtoModel;
- });
- return dto$;
- })]);
- return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
- return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
- }));
- }))
- .subscribe((paginatedListOfDTOs: PaginatedList) => {
- this.ePeopleSearchDtos.next(paginatedListOfDTOs);
+ getRemoteDataPayload())
+ .subscribe((paginatedListOfEPersons: PaginatedList) => {
+ this.ePeopleSearch.next(paginatedListOfEPersons);
}));
}
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
index d009f0283eb..85fe8974edd 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html
@@ -1,6 +1,55 @@
{{messagePrefix + '.head' | translate}}
+ {{messagePrefix + '.headSubgroups' | translate}}
+
+ 0"
+ [paginationOptions]="config"
+ [pageInfoState]="(subGroups$ | async)?.payload"
+ [collectionSize]="(subGroups$ | async)?.payload?.totalElements"
+ [hideGear]="true"
+ [hidePagerWhenSinglePage]="true">
+
+
+
+
+
+ {{messagePrefix + '.table.id' | translate}} |
+ {{messagePrefix + '.table.name' | translate}} |
+ {{messagePrefix + '.table.collectionOrCommunity' | translate}} |
+ {{messagePrefix + '.table.edit' | translate}} |
+
+
+
+
+ {{group.id}} |
+
+
+ {{ dsoNameService.getName(group) }}
+
+ |
+ {{ dsoNameService.getName((group.object | async)?.payload)}} |
+
+
+
+
+ |
+
+
+
+
+
+
+
+ {{messagePrefix + '.no-subgroups-yet' | translate}}
+
+
{{ dsoNameService.getName((group.object | async)?.payload) }} |
-
-
- {{ messagePrefix + '.table.edit.currentGroup' | translate }}
-
-
- {{messagePrefix + '.headSubgroups' | translate}}
-
- 0"
- [paginationOptions]="config"
- [pageInfoState]="(subGroups$ | async)?.payload"
- [collectionSize]="(subGroups$ | async)?.payload?.totalElements"
- [hideGear]="true"
- [hidePagerWhenSinglePage]="true">
-
-
-
-
-
- {{messagePrefix + '.table.id' | translate}} |
- {{messagePrefix + '.table.name' | translate}} |
- {{messagePrefix + '.table.collectionOrCommunity' | translate}} |
- {{messagePrefix + '.table.edit' | translate}} |
-
-
-
-
- {{group.id}} |
-
-
- {{ dsoNameService.getName(group) }}
-
- |
- {{ dsoNameService.getName((group.object | async)?.payload)}} |
-
-
-
-
- |
-
-
-
-
-
-
-
- {{messagePrefix + '.no-subgroups-yet' | translate}}
-
-
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
index ac5750dcaca..6fe7c2cf676 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
@@ -1,12 +1,12 @@
import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core';
-import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
+import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule, By } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
-import { Observable, of as observableOf, BehaviorSubject } from 'rxjs';
+import { Observable, of as observableOf } from 'rxjs';
import { RestResponse } from '../../../../core/cache/response.models';
import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
@@ -18,19 +18,18 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock';
import { SubgroupsListComponent } from './subgroups-list.component';
import {
- createSuccessfulRemoteDataObject$,
- createSuccessfulRemoteDataObject
+ createSuccessfulRemoteDataObject$
} from '../../../../shared/remote-data.utils';
import { RouterMock } from '../../../../shared/mocks/router.mock';
import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock';
import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock';
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
-import { map } from 'rxjs/operators';
import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock';
+import { EPersonMock2 } from 'src/app/shared/testing/eperson.mock';
describe('SubgroupsListComponent', () => {
let component: SubgroupsListComponent;
@@ -39,44 +38,70 @@ describe('SubgroupsListComponent', () => {
let builderService: FormBuilderService;
let ePersonDataServiceStub: any;
let groupsDataServiceStub: any;
- let activeGroup;
+ let activeGroup: Group;
let subgroups: Group[];
- let allGroups: Group[];
+ let groupNonMembers: Group[];
let routerStub;
let paginationService;
+ // Define a new mock activegroup for all tests below
+ let mockActiveGroup: Group = Object.assign(new Group(), {
+ handle: null,
+ subgroups: [GroupMock2],
+ epersons: [EPersonMock2],
+ selfRegistered: false,
+ permanent: false,
+ _links: {
+ self: {
+ href: 'https://rest.api/server/api/eperson/groups/activegroupid',
+ },
+ subgroups: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/subgroups' },
+ object: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/object' },
+ epersons: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/epersons' }
+ },
+ _name: 'activegroupname',
+ id: 'activegroupid',
+ uuid: 'activegroupid',
+ type: 'group',
+ });
beforeEach(waitForAsync(() => {
- activeGroup = GroupMock;
+ activeGroup = mockActiveGroup;
subgroups = [GroupMock2];
- allGroups = [GroupMock, GroupMock2];
+ groupNonMembers = [GroupMock];
ePersonDataServiceStub = {};
groupsDataServiceStub = {
activeGroup: activeGroup,
- subgroups$: new BehaviorSubject(subgroups),
+ subgroups: subgroups,
+ groupNonMembers: groupNonMembers,
getActiveGroup(): Observable {
return observableOf(this.activeGroup);
},
getSubgroups(): Group {
- return this.activeGroup;
+ return this.subgroups;
},
+ // This method is used to get all the current subgroups
findListByHref(_href: string): Observable>> {
- return this.subgroups$.pipe(
- map((currentGroups: Group[]) => {
- return createSuccessfulRemoteDataObject(buildPaginatedList(new PageInfo(), currentGroups));
- })
- );
+ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupsDataServiceStub.getSubgroups()));
},
getGroupEditPageRouterLink(group: Group): string {
return '/access-control/groups/' + group.id;
},
- searchGroups(query: string): Observable>> {
+ // This method is used to get all groups which are NOT currently a subgroup member
+ searchNonMemberGroups(query: string, group: string): Observable>> {
if (query === '') {
- return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allGroups));
+ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupNonMembers));
}
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
},
- addSubGroupToGroup(parentGroup, subgroup: Group): Observable {
- this.subgroups$.next([...this.subgroups$.getValue(), subgroup]);
+ addSubGroupToGroup(parentGroup, subgroupToAdd: Group): Observable {
+ // Add group to list of subgroups
+ this.subgroups = [...this.subgroups, subgroupToAdd];
+ // Remove group from list of non-members
+ this.groupNonMembers.forEach( (group: Group, index: number) => {
+ if (group.id === subgroupToAdd.id) {
+ this.groupNonMembers.splice(index, 1);
+ }
+ });
return observableOf(new RestResponse(true, 200, 'Success'));
},
clearGroupsRequests() {
@@ -85,12 +110,15 @@ describe('SubgroupsListComponent', () => {
clearGroupLinkRequests() {
// empty
},
- deleteSubGroupFromGroup(parentGroup, subgroup: Group): Observable {
- this.subgroups$.next(this.subgroups$.getValue().filter((group: Group) => {
- if (group.id !== subgroup.id) {
- return group;
+ deleteSubGroupFromGroup(parentGroup, subgroupToDelete: Group): Observable {
+ // Remove group from list of subgroups
+ this.subgroups.forEach( (group: Group, index: number) => {
+ if (group.id === subgroupToDelete.id) {
+ this.subgroups.splice(index, 1);
}
- }));
+ });
+ // Add group to list of non-members
+ this.groupNonMembers = [...this.groupNonMembers, subgroupToDelete];
return observableOf(new RestResponse(true, 200, 'Success'));
}
};
@@ -99,7 +127,7 @@ describe('SubgroupsListComponent', () => {
translateService = getMockTranslateService();
paginationService = new PaginationServiceStub();
- TestBed.configureTestingModule({
+ return TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({
loader: {
@@ -137,30 +165,38 @@ describe('SubgroupsListComponent', () => {
expect(comp).toBeDefined();
}));
- it('should show list of subgroups of current active group', () => {
- const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child'));
- expect(groupIdsFound.length).toEqual(1);
- activeGroup.subgroups.map((group: Group) => {
- expect(groupIdsFound.find((foundEl) => {
- return (foundEl.nativeElement.textContent.trim() === group.uuid);
- })).toBeTruthy();
+ describe('current subgroup list', () => {
+ it('should show list of subgroups of current active group', () => {
+ const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child'));
+ expect(groupIdsFound.length).toEqual(1);
+ subgroups.map((group: Group) => {
+ expect(groupIdsFound.find((foundEl) => {
+ return (foundEl.nativeElement.textContent.trim() === group.uuid);
+ })).toBeTruthy();
+ });
});
- });
- describe('if first group delete button is pressed', () => {
- let groupsFound: DebugElement[];
- beforeEach(fakeAsync(() => {
- const addButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton'));
- addButton.triggerEventHandler('click', {
- preventDefault: () => {/**/
- }
+ it('should show a delete button next to each subgroup', () => {
+ const subgroupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr'));
+ subgroupsFound.map((foundGroupRowElement: DebugElement) => {
+ const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
+ const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
+ expect(addButton).toBeNull();
+ expect(deleteButton).not.toBeNull();
+ });
+ });
+
+ describe('if first group delete button is pressed', () => {
+ let groupsFound: DebugElement[];
+ beforeEach(() => {
+ const deleteButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton'));
+ deleteButton.nativeElement.click();
+ fixture.detectChanges();
+ });
+ it('then no subgroup remains as a member of the active group', () => {
+ groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr'));
+ expect(groupsFound.length).toEqual(0);
});
- tick();
- fixture.detectChanges();
- }));
- it('one less subgroup in list from 1 to 0 (of 2 total groups)', () => {
- groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr'));
- expect(groupsFound.length).toEqual(0);
});
});
@@ -169,54 +205,38 @@ describe('SubgroupsListComponent', () => {
let groupsFound: DebugElement[];
beforeEach(fakeAsync(() => {
component.search({ query: '' });
+ fixture.detectChanges();
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
}));
- it('should display all groups', () => {
- fixture.detectChanges();
- groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
- expect(groupsFound.length).toEqual(2);
- groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
+ it('should display only non-member groups (i.e. groups that are not a subgroup)', () => {
const groupIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr td:first-child'));
- allGroups.map((group: Group) => {
+ expect(groupIdsFound.length).toEqual(1);
+ groupNonMembers.map((group: Group) => {
expect(groupIdsFound.find((foundEl: DebugElement) => {
return (foundEl.nativeElement.textContent.trim() === group.uuid);
})).toBeTruthy();
});
});
- describe('if group is already a subgroup', () => {
- it('should have delete button, else it should have add button', () => {
+ it('should display an add button next to non-member groups, not a delete button', () => {
+ groupsFound.map((foundGroupRowElement: DebugElement) => {
+ const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
+ const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
+ expect(addButton).not.toBeNull();
+ expect(deleteButton).toBeNull();
+ });
+ });
+
+ describe('if first add button is pressed', () => {
+ beforeEach(() => {
+ const addButton: DebugElement = fixture.debugElement.query(By.css('#groupsSearch tbody .fa-plus'));
+ addButton.nativeElement.click();
fixture.detectChanges();
+ });
+ it('then all (two) Groups are subgroups of the active group. No non-members left', () => {
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
- const getSubgroups = groupsDataServiceStub.getSubgroups().subgroups;
- if (getSubgroups !== undefined && getSubgroups.length > 0) {
- groupsFound.map((foundGroupRowElement: DebugElement) => {
- const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child'));
- const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
- const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
- expect(addButton).toBeNull();
- if (activeGroup.id === groupId.nativeElement.textContent) {
- expect(deleteButton).toBeNull();
- } else {
- expect(deleteButton).not.toBeNull();
- }
- });
- } else {
- const subgroupIds: string[] = activeGroup.subgroups.map((group: Group) => group.id);
- groupsFound.map((foundGroupRowElement: DebugElement) => {
- const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child'));
- const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
- const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
- if (subgroupIds.includes(groupId.nativeElement.textContent)) {
- expect(addButton).toBeNull();
- expect(deleteButton).not.toBeNull();
- } else {
- expect(deleteButton).toBeNull();
- expect(addButton).not.toBeNull();
- }
- });
- }
+ expect(groupsFound.length).toEqual(0);
});
});
});
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
index 0cff730c628..aea545e5543 100644
--- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
+++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
@@ -2,16 +2,15 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
-import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs';
-import { map, mergeMap, switchMap, take } from 'rxjs/operators';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
+import { map, switchMap, take } from 'rxjs/operators';
import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { Group } from '../../../../core/eperson/models/group.model';
import {
- getFirstCompletedRemoteData,
- getFirstSucceededRemoteData,
- getRemoteDataPayload
+ getAllCompletedRemoteData,
+ getFirstCompletedRemoteData
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
@@ -103,6 +102,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup != null) {
this.groupBeingEdited = activeGroup;
this.retrieveSubGroups();
+ this.search({query: ''});
}
}));
}
@@ -131,47 +131,6 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
}));
}
- /**
- * Whether or not the given group is a subgroup of the group currently being edited
- * @param possibleSubgroup Group that is a possible subgroup (being tested) of the group currently being edited
- */
- isSubgroupOfGroup(possibleSubgroup: Group): Observable {
- return this.groupDataService.getActiveGroup().pipe(take(1),
- mergeMap((activeGroup: Group) => {
- if (activeGroup != null) {
- if (activeGroup.uuid === possibleSubgroup.uuid) {
- return observableOf(false);
- } else {
- return this.groupDataService.findListByHref(activeGroup._links.subgroups.href, {
- currentPage: 1,
- elementsPerPage: 9999
- })
- .pipe(
- getFirstSucceededRemoteData(),
- getRemoteDataPayload(),
- map((listTotalGroups: PaginatedList) => listTotalGroups.page.filter((groupInList: Group) => groupInList.id === possibleSubgroup.id)),
- map((groups: Group[]) => groups.length > 0));
- }
- } else {
- return observableOf(false);
- }
- }));
- }
-
- /**
- * Whether or not the given group is the current group being edited
- * @param group Group that is possibly the current group being edited
- */
- isActiveGroup(group: Group): Observable {
- return this.groupDataService.getActiveGroup().pipe(take(1),
- mergeMap((activeGroup: Group) => {
- if (activeGroup != null && activeGroup.uuid === group.uuid) {
- return observableOf(true);
- }
- return observableOf(false);
- }));
- }
-
/**
* Deletes given subgroup from the group currently being edited
* @param subgroup Group we want to delete from the subgroups of the group currently being edited
@@ -181,6 +140,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup != null) {
const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup);
this.showNotifications('deleteSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
+ // Reload search results (if there is an active query).
+ // This will potentially add this deleted subgroup into the list of search results.
+ if (this.currentSearchQuery != null) {
+ this.search({query: this.currentSearchQuery});
+ }
} else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
}
@@ -197,6 +161,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup.uuid !== subgroup.uuid) {
const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup);
this.showNotifications('addSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
+ // Reload search results (if there is an active query).
+ // This will potentially remove this added subgroup from search results.
+ if (this.currentSearchQuery != null) {
+ this.search({query: this.currentSearchQuery});
+ }
} else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup'));
}
@@ -207,28 +176,38 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
}
/**
- * Search in the groups (searches by group name and by uuid exact match)
+ * Search all non-member groups (searches by group name and by uuid exact match). Used to search for
+ * groups that could be added to current group as a subgroup.
* @param data Contains query param
*/
search(data: any) {
- const query: string = data.query;
- if (query != null && this.currentSearchQuery !== query) {
- this.router.navigateByUrl(this.groupDataService.getGroupEditPageRouterLink(this.groupBeingEdited));
- this.currentSearchQuery = query;
- this.configSearch.currentPage = 1;
- }
- this.searchDone = true;
-
this.unsubFrom(SubKey.SearchResults);
- this.subs.set(SubKey.SearchResults, this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
- switchMap((config) => this.groupDataService.searchGroups(this.currentSearchQuery, {
- currentPage: config.currentPage,
- elementsPerPage: config.pageSize
- }, true, true, followLink('object')
- ))
- ).subscribe((rd: RemoteData>) => {
- this.searchResults$.next(rd);
- }));
+ this.subs.set(SubKey.SearchResults,
+ this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
+ switchMap((paginationOptions) => {
+ const query: string = data.query;
+ if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
+ this.currentSearchQuery = query;
+ this.paginationService.resetPage(this.configSearch.id);
+ }
+ this.searchDone = true;
+
+ return this.groupDataService.searchNonMemberGroups(this.currentSearchQuery, this.groupBeingEdited.id, {
+ currentPage: paginationOptions.currentPage,
+ elementsPerPage: paginationOptions.pageSize
+ }, false, true, followLink('object'));
+ }),
+ getAllCompletedRemoteData(),
+ map((rd: RemoteData) => {
+ if (rd.hasFailed) {
+ this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage }));
+ } else {
+ return rd;
+ }
+ }))
+ .subscribe((rd: RemoteData>) => {
+ this.searchResults$.next(rd);
+ }));
}
/**
diff --git a/src/app/access-control/group-registry/groups-registry.component.html b/src/app/access-control/group-registry/groups-registry.component.html
index 0523e6576cf..e1bc59d7476 100644
--- a/src/app/access-control/group-registry/groups-registry.component.html
+++ b/src/app/access-control/group-registry/groups-registry.component.html
@@ -5,7 +5,7 @@
diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts
index 0f5dde64af3..2ffc1f6caf4 100644
--- a/src/app/access-control/group-registry/groups-registry.component.ts
+++ b/src/app/access-control/group-registry/groups-registry.component.ts
@@ -218,18 +218,28 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
/**
* Get the members (epersons embedded value of a group)
+ * NOTE: At this time we only grab the *first* member in order to receive the `totalElements` value
+ * needed for our HTML template.
* @param group
*/
getMembers(group: Group): Observable>> {
- return this.ePersonDataService.findListByHref(group._links.epersons.href).pipe(getFirstSucceededRemoteData());
+ return this.ePersonDataService.findListByHref(group._links.epersons.href, {
+ currentPage: 1,
+ elementsPerPage: 1,
+ }).pipe(getFirstSucceededRemoteData());
}
/**
* Get the subgroups (groups embedded value of a group)
+ * NOTE: At this time we only grab the *first* subgroup in order to receive the `totalElements` value
+ * needed for our HTML template.
* @param group
*/
getSubgroups(group: Group): Observable>> {
- return this.groupService.findListByHref(group._links.subgroups.href).pipe(getFirstSucceededRemoteData());
+ return this.groupService.findListByHref(group._links.subgroups.href, {
+ currentPage: 1,
+ elementsPerPage: 1,
+ }).pipe(getFirstSucceededRemoteData());
}
/**
diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
index 773e0600fbf..f04324bdc59 100644
--- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
+++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
@@ -3,7 +3,8 @@ import {
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicFormLayout,
- DynamicInputModel
+ DynamicInputModel,
+ DynamicTextAreaModel
} from '@ng-dynamic-forms/core';
import { UntypedFormGroup } from '@angular/forms';
import { RegistryService } from '../../../../core/registry/registry.service';
@@ -51,7 +52,7 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
/**
* A dynamic input model for the scopeNote field
*/
- scopeNote: DynamicInputModel;
+ scopeNote: DynamicTextAreaModel;
/**
* A list of all dynamic input models
@@ -132,11 +133,12 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
maxLength: 'error.validation.metadata.qualifier.max-length',
},
});
- this.scopeNote = new DynamicInputModel({
+ this.scopeNote = new DynamicTextAreaModel({
id: 'scopeNote',
label: scopenote,
name: 'scopeNote',
required: false,
+ rows: 5,
});
this.formModel = [
new DynamicFormGroupModel(
diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html
index 557741df80c..2fe53dfcd84 100644
--- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html
+++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html
@@ -41,7 +41,7 @@ {{'admin.registries.schema.fields.head' | translate}}
|
{{field.id}} |
- {{schema?.prefix}}.{{field.element}}{{field.qualifier}} |
+ {{schema?.prefix}}.{{field.element}}{{field.qualifier ? '.' + field.qualifier : ''}} |
{{field.scopeNote}} |
|