mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Also remove unnecessary EpersonDtoModel from extending ReviewersListComponent. Remove "memberOfGroup" from EpersonDtoModel as it is no longer used
This commit is contained in:
@@ -29,8 +29,8 @@ import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
|
|||||||
*/
|
*/
|
||||||
enum SubKey {
|
enum SubKey {
|
||||||
ActiveGroup,
|
ActiveGroup,
|
||||||
MembersDTO,
|
Members,
|
||||||
SearchResultsDTO,
|
SearchResults,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,8 +166,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
retrieveMembers(page: number): void {
|
retrieveMembers(page: number): void {
|
||||||
this.unsubFrom(SubKey.MembersDTO);
|
this.unsubFrom(SubKey.Members);
|
||||||
this.subs.set(SubKey.MembersDTO,
|
this.subs.set(SubKey.Members,
|
||||||
this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
|
this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
|
||||||
switchMap((currentPagination) => {
|
switchMap((currentPagination) => {
|
||||||
return this.ePersonDataService.findListByHref(this.groupBeingEdited._links.epersons.href, {
|
return this.ePersonDataService.findListByHref(this.groupBeingEdited._links.epersons.href, {
|
||||||
@@ -239,8 +239,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
|
|||||||
* @param data Contains scope and query param
|
* @param data Contains scope and query param
|
||||||
*/
|
*/
|
||||||
search(data: any) {
|
search(data: any) {
|
||||||
this.unsubFrom(SubKey.SearchResultsDTO);
|
this.unsubFrom(SubKey.SearchResults);
|
||||||
this.subs.set(SubKey.SearchResultsDTO,
|
this.subs.set(SubKey.SearchResults,
|
||||||
this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
|
this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
|
||||||
switchMap((paginationOptions) => {
|
switchMap((paginationOptions) => {
|
||||||
|
|
||||||
|
@@ -13,9 +13,4 @@ export class EpersonDtoModel {
|
|||||||
* Whether or not the linked EPerson is able to be deleted
|
* Whether or not the linked EPerson is able to be deleted
|
||||||
*/
|
*/
|
||||||
public ableToDelete: boolean;
|
public ableToDelete: boolean;
|
||||||
/**
|
|
||||||
* Whether or not this EPerson is member of group on page it is being used on
|
|
||||||
*/
|
|
||||||
public memberOfGroup: boolean;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ import { Group } from '../../../../core/eperson/models/group.model';
|
|||||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||||
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
|
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
|
||||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock';
|
import { GroupMock } from '../../../../shared/testing/group-mock';
|
||||||
import { ReviewersListComponent } from './reviewers-list.component';
|
import { ReviewersListComponent } from './reviewers-list.component';
|
||||||
import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock';
|
import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock';
|
||||||
import {
|
import {
|
||||||
@@ -31,8 +31,10 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio
|
|||||||
import { RouterMock } from '../../../../shared/mocks/router.mock';
|
import { RouterMock } from '../../../../shared/mocks/router.mock';
|
||||||
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 { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model';
|
|
||||||
|
|
||||||
|
// NOTE: Because ReviewersListComponent extends MembersListComponent, the below tests ONLY validate
|
||||||
|
// features which are *unique* to ReviewersListComponent. All other features are tested in the
|
||||||
|
// members-list.component.spec.ts file.
|
||||||
describe('ReviewersListComponent', () => {
|
describe('ReviewersListComponent', () => {
|
||||||
let component: ReviewersListComponent;
|
let component: ReviewersListComponent;
|
||||||
let fixture: ComponentFixture<ReviewersListComponent>;
|
let fixture: ComponentFixture<ReviewersListComponent>;
|
||||||
@@ -40,31 +42,27 @@ describe('ReviewersListComponent', () => {
|
|||||||
let builderService: FormBuilderService;
|
let builderService: FormBuilderService;
|
||||||
let ePersonDataServiceStub: any;
|
let ePersonDataServiceStub: any;
|
||||||
let groupsDataServiceStub: any;
|
let groupsDataServiceStub: any;
|
||||||
let activeGroup;
|
let activeGroup: Group;
|
||||||
let allEPersons;
|
let epersonMembers: EPerson[];
|
||||||
let allGroups;
|
let epersonNonMembers: EPerson[];
|
||||||
let epersonMembers;
|
|
||||||
let subgroupMembers;
|
|
||||||
let paginationService;
|
let paginationService;
|
||||||
let ePersonDtoModel1: EpersonDtoModel;
|
|
||||||
let ePersonDtoModel2: EpersonDtoModel;
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
activeGroup = GroupMock;
|
activeGroup = GroupMock;
|
||||||
epersonMembers = [EPersonMock2];
|
epersonMembers = [EPersonMock2];
|
||||||
subgroupMembers = [GroupMock2];
|
epersonNonMembers = [EPersonMock];
|
||||||
allEPersons = [EPersonMock, EPersonMock2];
|
|
||||||
allGroups = [GroupMock, GroupMock2];
|
|
||||||
ePersonDataServiceStub = {
|
ePersonDataServiceStub = {
|
||||||
activeGroup: activeGroup,
|
activeGroup: activeGroup,
|
||||||
epersonMembers: epersonMembers,
|
epersonMembers: epersonMembers,
|
||||||
subgroupMembers: subgroupMembers,
|
epersonNonMembers: epersonNonMembers,
|
||||||
|
// This method is used to get all the current members
|
||||||
findListByHref(_href: string): Observable<RemoteData<PaginatedList<EPerson>>> {
|
findListByHref(_href: string): Observable<RemoteData<PaginatedList<EPerson>>> {
|
||||||
return createSuccessfulRemoteDataObject$(buildPaginatedList<EPerson>(new PageInfo(), groupsDataServiceStub.getEPersonMembers()));
|
return createSuccessfulRemoteDataObject$(buildPaginatedList<EPerson>(new PageInfo(), groupsDataServiceStub.getEPersonMembers()));
|
||||||
},
|
},
|
||||||
|
// This method is used to search across *non-members*
|
||||||
searchByScope(scope: string, query: string): Observable<RemoteData<PaginatedList<EPerson>>> {
|
searchByScope(scope: string, query: string): Observable<RemoteData<PaginatedList<EPerson>>> {
|
||||||
if (query === '') {
|
if (query === '') {
|
||||||
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons));
|
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), epersonNonMembers));
|
||||||
}
|
}
|
||||||
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
|
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
|
||||||
},
|
},
|
||||||
@@ -81,22 +79,22 @@ describe('ReviewersListComponent', () => {
|
|||||||
groupsDataServiceStub = {
|
groupsDataServiceStub = {
|
||||||
activeGroup: activeGroup,
|
activeGroup: activeGroup,
|
||||||
epersonMembers: epersonMembers,
|
epersonMembers: epersonMembers,
|
||||||
subgroupMembers: subgroupMembers,
|
epersonNonMembers: epersonNonMembers,
|
||||||
allGroups: allGroups,
|
|
||||||
getActiveGroup(): Observable<Group> {
|
getActiveGroup(): Observable<Group> {
|
||||||
return observableOf(activeGroup);
|
return observableOf(activeGroup);
|
||||||
},
|
},
|
||||||
getEPersonMembers() {
|
getEPersonMembers() {
|
||||||
return this.epersonMembers;
|
return this.epersonMembers;
|
||||||
},
|
},
|
||||||
searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> {
|
addMemberToGroup(parentGroup, epersonToAdd: EPerson): Observable<RestResponse> {
|
||||||
if (query === '') {
|
// Add eperson to list of members
|
||||||
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups));
|
this.epersonMembers = [...this.epersonMembers, epersonToAdd];
|
||||||
}
|
// Remove eperson from list of non-members
|
||||||
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
|
this.epersonNonMembers.forEach( (eperson: EPerson, index: number) => {
|
||||||
},
|
if (eperson.id === epersonToAdd.id) {
|
||||||
addMemberToGroup(parentGroup, eperson: EPerson): Observable<RestResponse> {
|
this.epersonNonMembers.splice(index, 1);
|
||||||
this.epersonMembers = [...this.epersonMembers, eperson];
|
}
|
||||||
|
});
|
||||||
return observableOf(new RestResponse(true, 200, 'Success'));
|
return observableOf(new RestResponse(true, 200, 'Success'));
|
||||||
},
|
},
|
||||||
clearGroupsRequests() {
|
clearGroupsRequests() {
|
||||||
@@ -109,21 +107,20 @@ describe('ReviewersListComponent', () => {
|
|||||||
return '/access-control/groups/' + group.id;
|
return '/access-control/groups/' + group.id;
|
||||||
},
|
},
|
||||||
deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable<RestResponse> {
|
deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable<RestResponse> {
|
||||||
this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => {
|
// Remove eperson from list of members
|
||||||
if (eperson.id !== epersonToDelete.id) {
|
this.epersonMembers.forEach( (eperson: EPerson, index: number) => {
|
||||||
return eperson;
|
if (eperson.id === epersonToDelete.id) {
|
||||||
|
this.epersonMembers.splice(index, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (this.epersonMembers === undefined) {
|
// Add eperson to list of non-members
|
||||||
this.epersonMembers = [];
|
this.epersonNonMembers = [...this.epersonNonMembers, epersonToDelete];
|
||||||
}
|
|
||||||
return observableOf(new RestResponse(true, 200, 'Success'));
|
return observableOf(new RestResponse(true, 200, 'Success'));
|
||||||
},
|
},
|
||||||
|
// Used to find the currently active group
|
||||||
findById(id: string) {
|
findById(id: string) {
|
||||||
for (const group of allGroups) {
|
if (activeGroup.id === id) {
|
||||||
if (group.id === id) {
|
return createSuccessfulRemoteDataObject$(activeGroup);
|
||||||
return createSuccessfulRemoteDataObject$(group);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return createNoContentRemoteDataObject$();
|
return createNoContentRemoteDataObject$();
|
||||||
},
|
},
|
||||||
@@ -135,7 +132,7 @@ describe('ReviewersListComponent', () => {
|
|||||||
translateService = getMockTranslateService();
|
translateService = getMockTranslateService();
|
||||||
|
|
||||||
paginationService = new PaginationServiceStub();
|
paginationService = new PaginationServiceStub();
|
||||||
TestBed.configureTestingModule({
|
return TestBed.configureTestingModule({
|
||||||
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
|
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
|
||||||
TranslateModule.forRoot({
|
TranslateModule.forRoot({
|
||||||
loader: {
|
loader: {
|
||||||
@@ -169,12 +166,6 @@ describe('ReviewersListComponent', () => {
|
|||||||
fixture.debugElement.nativeElement.remove();
|
fixture.debugElement.nativeElement.remove();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
ePersonDtoModel1 = new EpersonDtoModel();
|
|
||||||
ePersonDtoModel1.eperson = EPersonMock;
|
|
||||||
ePersonDtoModel2 = new EpersonDtoModel();
|
|
||||||
ePersonDtoModel2.eperson = EPersonMock2;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when no group is selected', () => {
|
describe('when no group is selected', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -218,34 +209,32 @@ describe('ReviewersListComponent', () => {
|
|||||||
it('should replace the value when a new member is added when multipleReviewers is false', () => {
|
it('should replace the value when a new member is added when multipleReviewers is false', () => {
|
||||||
spyOn(component.selectedReviewersUpdated, 'emit');
|
spyOn(component.selectedReviewersUpdated, 'emit');
|
||||||
component.multipleReviewers = false;
|
component.multipleReviewers = false;
|
||||||
component.selectedReviewers = [ePersonDtoModel1];
|
component.selectedReviewers = [EPersonMock];
|
||||||
|
|
||||||
component.addMemberToGroup(ePersonDtoModel2);
|
component.addMemberToGroup(EPersonMock2);
|
||||||
|
|
||||||
expect(component.selectedReviewers).toEqual([ePersonDtoModel2]);
|
expect(component.selectedReviewers).toEqual([EPersonMock2]);
|
||||||
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel2.eperson]);
|
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([EPersonMock2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add the value when a new member is added when multipleReviewers is true', () => {
|
it('should add the value when a new member is added when multipleReviewers is true', () => {
|
||||||
spyOn(component.selectedReviewersUpdated, 'emit');
|
spyOn(component.selectedReviewersUpdated, 'emit');
|
||||||
component.multipleReviewers = true;
|
component.multipleReviewers = true;
|
||||||
component.selectedReviewers = [ePersonDtoModel1];
|
component.selectedReviewers = [EPersonMock];
|
||||||
|
|
||||||
component.addMemberToGroup(ePersonDtoModel2);
|
component.addMemberToGroup(EPersonMock2);
|
||||||
|
|
||||||
expect(component.selectedReviewers).toEqual([ePersonDtoModel1, ePersonDtoModel2]);
|
expect(component.selectedReviewers).toEqual([EPersonMock, EPersonMock2]);
|
||||||
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([ePersonDtoModel1.eperson, ePersonDtoModel2.eperson]);
|
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([EPersonMock, EPersonMock2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the member when present', () => {
|
it('should delete the member when present', () => {
|
||||||
spyOn(component.selectedReviewersUpdated, 'emit');
|
spyOn(component.selectedReviewersUpdated, 'emit');
|
||||||
ePersonDtoModel1.memberOfGroup = true;
|
component.selectedReviewers = [EPersonMock];
|
||||||
component.selectedReviewers = [ePersonDtoModel1];
|
|
||||||
|
|
||||||
component.deleteMemberFromGroup(ePersonDtoModel1);
|
component.deleteMemberFromGroup(EPersonMock);
|
||||||
|
|
||||||
expect(component.selectedReviewers).toEqual([]);
|
expect(component.selectedReviewers).toEqual([]);
|
||||||
expect(ePersonDtoModel1.memberOfGroup).toBeFalse();
|
|
||||||
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([]);
|
expect(component.selectedReviewersUpdated.emit).toHaveBeenCalledWith([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -8,10 +8,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
|
|||||||
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
import { PaginationService } from '../../../../core/pagination/pagination.service';
|
||||||
import { Group } from '../../../../core/eperson/models/group.model';
|
import { Group } from '../../../../core/eperson/models/group.model';
|
||||||
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
|
||||||
import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model';
|
|
||||||
import { EPerson } from '../../../../core/eperson/models/eperson.model';
|
import { EPerson } from '../../../../core/eperson/models/eperson.model';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
|
||||||
import { hasValue } from '../../../../shared/empty.util';
|
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||||
import {
|
import {
|
||||||
MembersListComponent,
|
MembersListComponent,
|
||||||
@@ -24,8 +21,8 @@ import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
|
|||||||
*/
|
*/
|
||||||
enum SubKey {
|
enum SubKey {
|
||||||
ActiveGroup,
|
ActiveGroup,
|
||||||
MembersDTO,
|
Members,
|
||||||
SearchResultsDTO,
|
SearchResults,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +47,7 @@ export class ReviewersListComponent extends MembersListComponent implements OnIn
|
|||||||
@Output()
|
@Output()
|
||||||
selectedReviewersUpdated: EventEmitter<EPerson[]> = new EventEmitter();
|
selectedReviewersUpdated: EventEmitter<EPerson[]> = new EventEmitter();
|
||||||
|
|
||||||
selectedReviewers: EpersonDtoModel[] = [];
|
selectedReviewers: EPerson[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected groupService: GroupDataService,
|
protected groupService: GroupDataService,
|
||||||
@@ -100,54 +97,40 @@ export class ReviewersListComponent extends MembersListComponent implements OnIn
|
|||||||
retrieveMembers(page: number): void {
|
retrieveMembers(page: number): void {
|
||||||
this.config.currentPage = page;
|
this.config.currentPage = page;
|
||||||
if (this.groupId === null) {
|
if (this.groupId === null) {
|
||||||
this.unsubFrom(SubKey.MembersDTO);
|
this.unsubFrom(SubKey.Members);
|
||||||
const paginatedListOfDTOs: PaginatedList<EpersonDtoModel> = new PaginatedList();
|
const paginatedListOfEPersons: PaginatedList<EPerson> = new PaginatedList();
|
||||||
paginatedListOfDTOs.page = this.selectedReviewers;
|
paginatedListOfEPersons.page = this.selectedReviewers;
|
||||||
this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs);
|
this.ePeopleMembersOfGroup.next(paginatedListOfEPersons);
|
||||||
} else {
|
} else {
|
||||||
super.retrieveMembers(page);
|
super.retrieveMembers(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given {@link possibleMember} is part of the {@link selectedReviewers}.
|
* Removes the {@link eperson} from the {@link selectedReviewers}
|
||||||
*
|
*
|
||||||
* @param possibleMember The {@link EPerson} that needs to be checked
|
* @param eperson The {@link EPerson} to remove
|
||||||
*/
|
*/
|
||||||
isMemberOfGroup(possibleMember: EPerson): Observable<boolean> {
|
deleteMemberFromGroup(eperson: EPerson) {
|
||||||
return observableOf(hasValue(this.selectedReviewers.find((reviewer: EpersonDtoModel) => reviewer.eperson.id === possibleMember.id)));
|
const index = this.selectedReviewers.indexOf(eperson);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the {@link ePerson} from the {@link selectedReviewers}
|
|
||||||
*
|
|
||||||
* @param ePerson The {@link EpersonDtoModel} containg the {@link EPerson} to remove
|
|
||||||
*/
|
|
||||||
deleteMemberFromGroup(ePerson: EpersonDtoModel) {
|
|
||||||
ePerson.memberOfGroup = false;
|
|
||||||
const index = this.selectedReviewers.indexOf(ePerson);
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.selectedReviewers.splice(index, 1);
|
this.selectedReviewers.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.selectedReviewersUpdated.emit(this.selectedReviewers.map((ePersonDtoModel: EpersonDtoModel) => ePersonDtoModel.eperson));
|
this.selectedReviewersUpdated.emit(this.selectedReviewers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the {@link ePerson} to the {@link selectedReviewers} (or replaces it when {@link multipleReviewers} is
|
* Adds the {@link eperson} to the {@link selectedReviewers} (or replaces it when {@link multipleReviewers} is
|
||||||
* `false`). Afterwards it will emit the list.
|
* `false`). Afterwards it will emit the list.
|
||||||
*
|
*
|
||||||
* @param ePerson The {@link EPerson} to add to the list
|
* @param eperson The {@link EPerson} to add to the list
|
||||||
*/
|
*/
|
||||||
addMemberToGroup(ePerson: EpersonDtoModel) {
|
addMemberToGroup(eperson: EPerson) {
|
||||||
ePerson.memberOfGroup = true;
|
|
||||||
if (!this.multipleReviewers) {
|
if (!this.multipleReviewers) {
|
||||||
for (const selectedReviewer of this.selectedReviewers) {
|
|
||||||
selectedReviewer.memberOfGroup = false;
|
|
||||||
}
|
|
||||||
this.selectedReviewers = [];
|
this.selectedReviewers = [];
|
||||||
}
|
}
|
||||||
this.selectedReviewers.push(ePerson);
|
this.selectedReviewers.push(eperson);
|
||||||
this.selectedReviewersUpdated.emit(this.selectedReviewers.map((epersonDtoModel: EpersonDtoModel) => epersonDtoModel.eperson));
|
this.selectedReviewersUpdated.emit(this.selectedReviewers);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user