mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-13 19:21:56 +01:00
Refactor add group component (#15518)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
6b8c5c9edd
commit
383635e970
@ -1,108 +1,43 @@
|
||||
<clr-modal [(clrModalOpen)]="opened" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="'true'" [clrModalClosable]="false">
|
||||
<h3 class="modal-title">{{'MEMBER.IMPORT_GROUP' | translate}}</h3>
|
||||
<clr-modal [(clrModalOpen)]="addGroupOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{'GROUP.NEW_MEMBER' | translate}}</h3>
|
||||
<div class="modal-body">
|
||||
<label>{{ 'MEMBER.NEW_GROUP_INFO' | translate}}</label>
|
||||
<div class="modeSelectradios">
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="modeRadios" [value]="false" id="select_group" [(ngModel)]="createGroupMode">
|
||||
<label for="select_group">{{'MEMBER.ADD_GROUP_SELECT' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="modeRadios" [value]="true" id="create_group" [(ngModel)]="createGroupMode">
|
||||
<label for="create_group">{{'MEMBER.CREATE_GROUP_SELECT' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
</div>
|
||||
<div *ngIf="createGroupMode">
|
||||
<form #groupForm="ngForm" class="clr-form clr-form-horizontal">
|
||||
<div class="clr-form-control">
|
||||
<label class="required clr-control-label">{{ 'MEMBER.LDAP_SEARCH_DN' | translate}}</label>
|
||||
<div class="clr-control-container" [class.clr-error]="isDNInvalid">
|
||||
<div class="clr-input-wrapper">
|
||||
<input class="clr-input" type="text" name="ldap_group_dn" size="45"
|
||||
required
|
||||
[(ngModel)]="group.ldap_group_dn"
|
||||
#groupDN="ngModel">
|
||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||
<inline-alert class="modal-title"></inline-alert>
|
||||
<label>{{ 'GROUP.NEW_USER_INFO' | translate}}</label>
|
||||
<form #groupForm="ngForm" class="clr-form clr-form-horizontal">
|
||||
<div class="clr-form-control">
|
||||
<label class="required clr-control-label">{{'GROUP.GROUP' | translate}} {{'GROUP.NAME' | translate}}</label>
|
||||
<div class="clr-control-container" [class.clr-error]="(groupName.invalid && (groupName.touched||groupName.dirty)) || !isGroupNameValid">
|
||||
<div class="clr-input-wrapper" (mouseleave)="leaveInput()">
|
||||
<input class="clr-input" type="text" id="group-name" [(ngModel)]="memberGroup.group_name"
|
||||
name="groupName"
|
||||
#groupName="ngModel"
|
||||
required autocomplete="off" (keyup)="input()">
|
||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||
<div class="selectBox" [style.display]="searchedGroups.length ? 'block' : 'none'" >
|
||||
<ul>
|
||||
<li *ngFor="let group of searchedGroups" (click)="selectGroup(group.group_name)">{{group.group_name}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<clr-control-error *ngIf="isDNInvalid" class="tooltip-content">
|
||||
{{dnTooltip | translate}}
|
||||
</clr-control-error>
|
||||
</div>
|
||||
<clr-control-error *ngIf="(groupName.invalid && (groupName.touched||groupName.dirty)) || !isGroupNameValid" class="tooltip-content">
|
||||
{{ groupTooltip | translate }}
|
||||
</clr-control-error>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-form-control">
|
||||
<label class="clr-control-label">{{'MEMBER.LDAP_SEARCH_NAME' | translate}}</label>
|
||||
<label class="clr-control-label">{{'GROUP.ROLE' | translate}}</label>
|
||||
<div class="clr-control-container">
|
||||
<div class="clr-input-wrapper">
|
||||
<input class="clr-input" type="text" name="ldap_group_name" size="35" [(ngModel)]="group.group_name">
|
||||
</div>
|
||||
<clr-radio-wrapper *ngFor="let projectRoot of projectRoots">
|
||||
<input clrRadio type="radio" name="member_role" id="{{'check_root_project_' + projectRoot.NAME}}" [value]="projectRoot.VALUE" [(ngModel)]="roleId">
|
||||
<label for="{{'check_root_project_' + projectRoot.NAME}}">{{ projectRoot.LABEL | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-form-control">
|
||||
<label class="clr-control-label" for="member_role1">{{ 'MEMBER.ROLE' | translate}}</label>
|
||||
<div class="clr-select-wrapper">
|
||||
<select class="clr-select" id="member_role1" name="member_role" [(ngModel)]="selectedRole">
|
||||
<option *ngFor="let role of roles" [ngValue]="role.id"> {{role.value | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div *ngIf="!createGroupMode">
|
||||
<div>
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="left">
|
||||
</div>
|
||||
<div class="right">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"MEMBER.FILTER_PLACEHOLDER" | translate}}' (filterEvt)="doFilter($event)"
|
||||
[currentValue]="currentTerm"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="loadGroups()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-1 col-md-1 col-sm-1 col-xs-1">
|
||||
<label>{{'MEMBER.LDAP_GROUP' | translate}}</label>
|
||||
</div>
|
||||
<div class="class=col-lg-11 col-md-11 col-sm-11 col-xs-11">
|
||||
<clr-datagrid class="datagrid-compact" [(clrDgSelected)]="selectedGroups" [clrDgLoading]="onLoading">
|
||||
<clr-dg-column [clrDgField]="'group_name'">{{'MEMBER.LDAP_SEARCH_NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'ldap_group_dn'">{{'MEMBER.LDAP_SEARCH_DN' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'property'">{{'MEMBER.LDAP_PROPERTY' | translate}}</clr-dg-column>
|
||||
|
||||
<clr-dg-row *clrDgItems="let group of groups" [clrDgItem]="group">
|
||||
<clr-dg-cell>{{group.group_name}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{group.ldap_group_dn}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{group.property}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="5" [clrDgTotalItems]="totalCount">
|
||||
<span *ngIf="groups?.length">
|
||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'MEMBER.OF' | translate}}
|
||||
</span>
|
||||
{{groups?.length}} {{'MEMBER.ITEMS' | translate}}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-form clr-form-horizontal">
|
||||
<div class="clr-form-control">
|
||||
<label class="clr-control-label" for="member_role1">{{ 'MEMBER.ROLE' | translate}}</label>
|
||||
<div class="clr-select-wrapper">
|
||||
<select class="clr-select" id="member_role2" [(ngModel)]="selectedRole">
|
||||
<option *ngFor="let role of roles" [ngValue]="role.id"> {{role.value | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSave()">{{'BUTTON.SAVE' | translate}}</button>
|
||||
<button [clrLoading]="btnStatus" type="button" class="btn btn-primary" [disabled]="!isValid()" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
|
||||
</clr-modal>
|
||||
|
@ -1,23 +1,27 @@
|
||||
clr-datagrid {
|
||||
::ng-deep .datagrid {
|
||||
margin-top: 0;
|
||||
}
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 12px;
|
||||
.selectBox{
|
||||
position: absolute;
|
||||
width: 173px;
|
||||
height: auto;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0,0,0,.15);
|
||||
border-right-width: 2px;
|
||||
border-bottom-width: 2px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
z-index: 100;
|
||||
}
|
||||
.flex-items-xs-between{
|
||||
display: flex;
|
||||
.selectBox ul li{
|
||||
list-style: none;
|
||||
padding: 3px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.modeSelectradios {
|
||||
margin-top: 21px;
|
||||
.selectBox ul li:hover{
|
||||
color: #262626;
|
||||
background-image: linear-gradient(180deg,#f5f5f5 0,#e8e8e8);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.left {
|
||||
flex: 1;
|
||||
}
|
||||
.right {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
right: 15px;
|
||||
}
|
@ -1,41 +1,41 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { ClarityModule } from '@clr/angular';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { of } from "rxjs";
|
||||
import { AppConfigService } from "../../../../services/app-config.service";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||
import { AddGroupComponent } from './add-group.component';
|
||||
import { GroupService } from "../../../left-side-nav/group/group.service";
|
||||
import { MemberService } from "../member.service";
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { OperationService } from "../../../../shared/components/operation/operation.service";
|
||||
import { MemberService } from 'ng-swagger-gen/services/member.service';
|
||||
|
||||
describe('AddGroupComponent', () => {
|
||||
describe('AddHttpAuthGroupComponent', () => {
|
||||
let component: AddGroupComponent;
|
||||
let fixture: ComponentFixture<AddGroupComponent>;
|
||||
let fakeMessageHandlerService = null;
|
||||
let fakeOperationService = null;
|
||||
let fakeGroupService = null;
|
||||
let fakeMemberService = null;
|
||||
let fakeAppConfigService = {
|
||||
isLdapMode: function () {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let fakeMemberService = {listProjectMembers: function() {
|
||||
return of(null);
|
||||
}};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AddGroupComponent],
|
||||
imports: [
|
||||
ClarityModule,
|
||||
FormsModule,
|
||||
TranslateModule.forRoot()
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
],
|
||||
imports: [
|
||||
SharedTestingModule
|
||||
],
|
||||
providers: [
|
||||
TranslateService,
|
||||
{ provide: MessageHandlerService, useValue: fakeMessageHandlerService },
|
||||
{ provide: OperationService, useValue: fakeOperationService },
|
||||
{ provide: GroupService, useValue: fakeGroupService },
|
||||
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
||||
{ provide: MemberService, useValue: fakeMemberService }
|
||||
]
|
||||
}).compileComponents();
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,170 +1,233 @@
|
||||
import { forkJoin, of as observableOf, throwError as observableThrowError } from "rxjs";
|
||||
import { catchError, mergeMap } from 'rxjs/operators';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core";
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { debounceTime, finalize, switchMap } from 'rxjs/operators';
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserGroup } from "../../../left-side-nav/group/group";
|
||||
import { MemberService } from "../member.service";
|
||||
import { GroupService } from "../../../left-side-nav/group/group.service";
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { OperationService } from "../../../../shared/components/operation/operation.service";
|
||||
import { operateChanges, OperateInfo, OperationState } from "../../../../shared/components/operation/operate";
|
||||
import { ProjectRoles } from "../../../../shared/entities/shared.const";
|
||||
import { errorHandler } from "../../../../shared/units/shared.utils";
|
||||
import { ProjectMemberEntity } from "../../../../../../ng-swagger-gen/models/project-member-entity";
|
||||
import { AppConfigService } from "../../../../services/app-config.service";
|
||||
import { ProjectRootInterface } from "../../../../shared/services";
|
||||
import { GroupType, PROJECT_ROOTS } from "../../../../shared/entities/shared.const";
|
||||
import { InlineAlertComponent } from "../../../../shared/components/inline-alert/inline-alert.component";
|
||||
import { UsergroupService } from "../../../../../../ng-swagger-gen/services/usergroup.service";
|
||||
import { of, Subject, Subscription } from "rxjs";
|
||||
import { UserGroup } from 'ng-swagger-gen/models/user-group';
|
||||
import { ClrLoadingState } from "@clr/angular";
|
||||
import { MemberService } from 'ng-swagger-gen/services/member.service';
|
||||
import { MessageHandlerService } from "../../../../shared/services/message-handler.service";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: "add-group",
|
||||
templateUrl: "./add-group.component.html",
|
||||
styleUrls: ["./add-group.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
selector: 'add-group',
|
||||
templateUrl: './add-group.component.html',
|
||||
styleUrls: ['./add-group.component.scss'],
|
||||
})
|
||||
export class AddGroupComponent implements OnInit {
|
||||
opened = false;
|
||||
createGroupMode = false;
|
||||
onLoading = false;
|
||||
roles = ProjectRoles;
|
||||
currentTerm = '';
|
||||
|
||||
selectedRole = 1;
|
||||
group = new UserGroup(1);
|
||||
selectedGroups: UserGroup[] = [];
|
||||
groups: UserGroup[] = [];
|
||||
totalCount = 0;
|
||||
export class AddGroupComponent implements OnInit, OnDestroy {
|
||||
projectRoots: ProjectRootInterface[] = PROJECT_ROOTS;
|
||||
memberGroup: UserGroup = {
|
||||
group_name: ''
|
||||
};
|
||||
roleId: number = 1; // default value is 1(project admin);
|
||||
addGroupOpened: boolean = false;
|
||||
staticBackdrop: boolean = true;
|
||||
closable: boolean = false;
|
||||
|
||||
dnTooltip = 'TOOLTIP.ITEM_REQUIRED';
|
||||
@ViewChild('groupForm', {static: true})
|
||||
currentForm: NgForm;
|
||||
|
||||
@ViewChild(InlineAlertComponent)
|
||||
inlineAlert: InlineAlertComponent;
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() memberList: ProjectMemberEntity[] = [];
|
||||
@Output() added = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild('groupForm')
|
||||
groupForm: NgForm;
|
||||
checkOnGoing: boolean = false;
|
||||
searchedGroups: UserGroup[] = [];
|
||||
groupChecker: Subject<string> = new Subject<string>();
|
||||
groupSearcher: Subject<string> = new Subject<string>();
|
||||
groupCheckerSub: Subscription;
|
||||
groupSearcherSub: Subscription;
|
||||
btnStatus: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
isGroupNameValid: boolean = true;
|
||||
groupTooltip: string = 'MEMBER.GROUP_NAME_REQUIRED';
|
||||
isNameChecked: boolean = false; // this is only for LDAP mode
|
||||
constructor(private memberService: MemberService,
|
||||
private appConfigService: AppConfigService, private messageHandlerService: MessageHandlerService,
|
||||
private userGroupService: UsergroupService) { }
|
||||
|
||||
constructor(
|
||||
private translateService: TranslateService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private operationService: OperationService,
|
||||
private ref: ChangeDetectorRef,
|
||||
private groupService: GroupService,
|
||||
private memberService: MemberService
|
||||
) {}
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
public get isValid(): boolean {
|
||||
if (this.createGroupMode) {
|
||||
return this.groupForm && this.groupForm.valid;
|
||||
} else {
|
||||
return this.selectedGroups.length > 0;
|
||||
}
|
||||
}
|
||||
public get isDNInvalid(): boolean {
|
||||
if (!this.groupForm) {return false; }
|
||||
let dnControl = this.groupForm.controls['ldap_group_dn'];
|
||||
return dnControl && dnControl.invalid && (dnControl.dirty || dnControl.touched);
|
||||
}
|
||||
|
||||
loadGroups() {
|
||||
this.onLoading = true;
|
||||
this.groupService.getUserGroups().subscribe(groups => {
|
||||
this.groups = groups.filter(group => {
|
||||
if (!group.group_name) {group.group_name = ''; }
|
||||
return group.group_name.includes(this.currentTerm)
|
||||
&& !this.memberList.some(member => member.entity_type === 'g' && member.entity_id === group.id);
|
||||
});
|
||||
this.totalCount = groups.length;
|
||||
this.onLoading = false;
|
||||
this.ref.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
doFilter(name: string) {
|
||||
this.currentTerm = name;
|
||||
this.loadGroups();
|
||||
}
|
||||
|
||||
resetModaldata() {
|
||||
this.createGroupMode = false;
|
||||
this.group = new UserGroup(1);
|
||||
this.selectedRole = 1;
|
||||
this.selectedGroups = [];
|
||||
this.groups = [];
|
||||
}
|
||||
|
||||
public open() {
|
||||
this.resetModaldata();
|
||||
this.loadGroups();
|
||||
this.opened = true;
|
||||
this.ref.detectChanges();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.resetModaldata();
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
onSave() {
|
||||
if (!this.createGroupMode) {
|
||||
this.addGroups();
|
||||
} else {
|
||||
this.createGroupAsMember();
|
||||
}
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
addGroups() {
|
||||
let GroupAdders$ = this.selectedGroups.map(group => {
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'OPERATION.ADD_GROUP';
|
||||
operMessage.data.id = group.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = group.group_name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
return this.memberService
|
||||
.addGroupMember(this.projectId, group, this.selectedRole).pipe(
|
||||
mergeMap(response => {
|
||||
return this.translateService.get("BATCH.DELETED_SUCCESS").pipe(
|
||||
mergeMap(res => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
return observableOf(res);
|
||||
})); }),
|
||||
catchError(
|
||||
error => {
|
||||
const message = errorHandler(error);
|
||||
this.translateService.get(message).subscribe(res =>
|
||||
operateChanges(operMessage, OperationState.failure, res)
|
||||
);
|
||||
return observableThrowError(error);
|
||||
}),
|
||||
catchError(error => observableThrowError(error)), );
|
||||
});
|
||||
forkJoin(GroupAdders$)
|
||||
.subscribe(results => {
|
||||
if (results.some(code => code < 200 || code > 299)) {
|
||||
this.added.emit(false);
|
||||
} else {
|
||||
this.added.emit(true);
|
||||
ngOnInit(): void {
|
||||
if (!this.groupCheckerSub) {
|
||||
this.groupCheckerSub = this.groupChecker.pipe(
|
||||
debounceTime(500),
|
||||
switchMap((name) => {
|
||||
if (name) {
|
||||
this.checkOnGoing = true;
|
||||
const params: MemberService.ListProjectMembersParams = {
|
||||
projectNameOrId: this.projectId.toString(),
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
entityname: name
|
||||
};
|
||||
return this.memberService.listProjectMembers(params).pipe(finalize(() => this.checkOnGoing = false));
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
})).subscribe(res => {
|
||||
if (res && res.length) {
|
||||
if (res.filter(g => g.entity_name === this.memberGroup.group_name).length > 0) {
|
||||
this.isGroupNameValid = false;
|
||||
this.groupTooltip = 'MEMBER.GROUP_ALREADY_ADDED';
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
this.msgHandler.handleError(error);
|
||||
});
|
||||
this.opened = false;
|
||||
}
|
||||
if (!this.groupSearcherSub) {
|
||||
this.groupSearcherSub = this.groupSearcher.pipe(
|
||||
debounceTime(500),
|
||||
switchMap(name => {
|
||||
if (name) {
|
||||
return this.userGroupService.searchUserGroups({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
groupname: name
|
||||
});
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
})
|
||||
).subscribe(res => {
|
||||
if (res) {
|
||||
this.searchedGroups = res;
|
||||
}
|
||||
// for LDAP mode, if input group name is not found from search result, then show "Group name does not exists" error
|
||||
if (this.appConfigService.isLdapMode() && this.memberGroup.group_name) {
|
||||
let flag = false;
|
||||
this.searchedGroups.forEach(item => {
|
||||
if (item.group_name === this.memberGroup.group_name) {
|
||||
flag = true;
|
||||
}
|
||||
});
|
||||
if (!flag) {
|
||||
this.isGroupNameValid = false;
|
||||
this.groupTooltip = 'MEMBER.NON_EXISTENT_GROUP';
|
||||
} else { // it means input group name is valid
|
||||
this.isNameChecked = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.groupCheckerSub) {
|
||||
this.groupCheckerSub.unsubscribe();
|
||||
this.groupCheckerSub = null;
|
||||
}
|
||||
if (this.groupSearcherSub) {
|
||||
this.groupSearcherSub.unsubscribe();
|
||||
this.groupSearcherSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
createGroupAsMember() {
|
||||
let groupCopy = Object.assign({}, this.group);
|
||||
this.memberService.addGroupMember(this.projectId, groupCopy, this.selectedRole)
|
||||
.subscribe(
|
||||
res => this.added.emit(true),
|
||||
err => {
|
||||
this.msgHandler.handleError(err);
|
||||
this.added.emit(false);
|
||||
this.btnStatus = ClrLoadingState.LOADING;
|
||||
if (this.appConfigService.isHttpAuthMode()) {
|
||||
this.memberGroup.group_type = GroupType.HTTP_TYPE;
|
||||
}
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
this.memberGroup.group_type = GroupType.LDAP_TYPE;
|
||||
}
|
||||
if (this.appConfigService.isOidcMode()) {
|
||||
this.memberGroup.group_type = GroupType.OIDC_TYPE;
|
||||
}
|
||||
this.memberService.createProjectMember({
|
||||
projectNameOrId: this.projectId.toString(),
|
||||
projectMember: {
|
||||
role_id: this.roleId,
|
||||
member_group: this.memberGroup
|
||||
}
|
||||
);
|
||||
this.opened = false;
|
||||
}).subscribe(
|
||||
res => {
|
||||
this.messageHandlerService.showSuccess('MEMBER.ADDED_SUCCESS');
|
||||
this.btnStatus = ClrLoadingState.SUCCESS;
|
||||
this.addGroupOpened = false;
|
||||
this.added.emit(true);
|
||||
},
|
||||
err => {
|
||||
this.btnStatus = ClrLoadingState.ERROR;
|
||||
this.inlineAlert.showInlineError(err);
|
||||
this.added.emit(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
onSubmit(): void {
|
||||
this.createGroupAsMember();
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.addGroupOpened = false;
|
||||
}
|
||||
|
||||
|
||||
openAddGroupModal(): void {
|
||||
this.currentForm.reset({
|
||||
member_role: 1
|
||||
});
|
||||
this.addGroupOpened = true;
|
||||
this.inlineAlert.close();
|
||||
this.memberGroup = {
|
||||
group_name: ''
|
||||
};
|
||||
this.isGroupNameValid = true;
|
||||
this.groupTooltip = "MEMBER.USERNAME_IS_REQUIRED";
|
||||
this.searchedGroups = [];
|
||||
}
|
||||
isValid(): boolean {
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
if (!this.isNameChecked) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.isGroupNameValid && this.currentForm &&
|
||||
this.currentForm.valid &&
|
||||
!this.checkOnGoing;
|
||||
}
|
||||
|
||||
selectGroup(groupName) {
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
this.isNameChecked = true;
|
||||
this.isGroupNameValid = true;
|
||||
}
|
||||
this.memberGroup.group_name = groupName;
|
||||
this.groupChecker.next(groupName);
|
||||
this.searchedGroups = [];
|
||||
}
|
||||
|
||||
leaveInput() {
|
||||
this.searchedGroups = [];
|
||||
}
|
||||
|
||||
input() {
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
this.isNameChecked = false;
|
||||
}
|
||||
this.groupChecker.next(this.memberGroup.group_name);
|
||||
this.groupSearcher.next(this.memberGroup.group_name);
|
||||
if (!this.memberGroup.group_name) {
|
||||
this.searchedGroups = [];
|
||||
this.isGroupNameValid = false;
|
||||
this.groupTooltip = 'MEMBER.GROUP_NAME_REQUIRED';
|
||||
} else {
|
||||
this.isGroupNameValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
<clr-modal [(clrModalOpen)]="addHttpAuthOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{'GROUP.NEW_MEMBER' | translate}}</h3>
|
||||
<inline-alert class="modal-title padding-0"></inline-alert>
|
||||
<div class="modal-body">
|
||||
<label>{{ 'GROUP.NEW_USER_INFO' | translate}}</label>
|
||||
<form #memberForm="ngForm" class="clr-form clr-form-horizontal">
|
||||
<div class="clr-form-control">
|
||||
<label class="required clr-control-label">{{'GROUP.GROUP' | translate}} {{'GROUP.NAME' | translate}}</label>
|
||||
<div class="clr-control-container">
|
||||
<div class="clr-input-wrapper">
|
||||
<input class="clr-input" type="text" id="member_name" [(ngModel)]="member_group.group_name"
|
||||
name="member_name"
|
||||
size="20"
|
||||
minlength="3"
|
||||
#memberName="ngModel"
|
||||
required autocomplete="off">
|
||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-form-control">
|
||||
<label class="clr-control-label">{{'GROUP.ROLE' | translate}}</label>
|
||||
<div class="clr-control-container">
|
||||
<clr-radio-wrapper *ngFor="let projectRoot of projectRoots">
|
||||
<input clrRadio type="radio" name="member_role" id="{{'check_root_project_' + projectRoot.NAME}}" [value]="projectRoot.VALUE" [(ngModel)]="role_id">
|
||||
<label for="{{'check_root_project_' + projectRoot.NAME}}">{{ projectRoot.LABEL | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
@ -1,8 +0,0 @@
|
||||
.form-group-label-override {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.padding-0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { of } from "rxjs";
|
||||
import { MemberService } from '../member.service';
|
||||
import { AppConfigService } from "../../../../services/app-config.service";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AddHttpAuthGroupComponent } from './add-http-auth-group.component';
|
||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||
|
||||
describe('AddHttpAuthGroupComponent', () => {
|
||||
let component: AddHttpAuthGroupComponent;
|
||||
let fixture: ComponentFixture<AddHttpAuthGroupComponent>;
|
||||
let fakeAppConfigService = {
|
||||
isHttpAuthMode: function () {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let fakeMemberService = {addGroupMember: function() {
|
||||
return of(null);
|
||||
}};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AddHttpAuthGroupComponent],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
],
|
||||
imports: [
|
||||
SharedTestingModule
|
||||
],
|
||||
providers: [
|
||||
TranslateService,
|
||||
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
||||
{ provide: MemberService, useValue: fakeMemberService }
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AddHttpAuthGroupComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserService } from '../../../left-side-nav/user/user.service';
|
||||
import { MemberService } from '../member.service';
|
||||
import { UserGroup } from "../../../left-side-nav/group/group";
|
||||
import { AppConfigService } from "../../../../services/app-config.service";
|
||||
import { ProjectRootInterface } from "../../../../shared/services";
|
||||
import { GroupType, PROJECT_ROOTS } from "../../../../shared/entities/shared.const";
|
||||
import { InlineAlertComponent } from "../../../../shared/components/inline-alert/inline-alert.component";
|
||||
import { errorHandler } from "../../../../shared/units/shared.utils";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'add-http-auth-group',
|
||||
templateUrl: './add-http-auth-group.component.html',
|
||||
styleUrls: ['./add-http-auth-group.component.scss'],
|
||||
providers: [UserService]
|
||||
})
|
||||
|
||||
export class AddHttpAuthGroupComponent implements OnInit {
|
||||
projectRoots: ProjectRootInterface[];
|
||||
member_group: UserGroup = { group_name: '', group_type: 2 };
|
||||
role_id: number;
|
||||
addHttpAuthOpened: boolean;
|
||||
|
||||
memberForm: NgForm;
|
||||
|
||||
staticBackdrop: boolean = true;
|
||||
closable: boolean = false;
|
||||
|
||||
@ViewChild('memberForm', {static: true})
|
||||
currentForm: NgForm;
|
||||
|
||||
@ViewChild(InlineAlertComponent)
|
||||
inlineAlert: InlineAlertComponent;
|
||||
|
||||
@Input() projectId: number;
|
||||
@Output() added = new EventEmitter<boolean>();
|
||||
|
||||
checkOnGoing: boolean = false;
|
||||
|
||||
constructor(private memberService: MemberService,
|
||||
private appConfigService: AppConfigService,
|
||||
private translateService: TranslateService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectRoots = PROJECT_ROOTS;
|
||||
|
||||
this.member_group = new UserGroup(this.appConfigService.isHttpAuthMode() ? GroupType.HTTP_TYPE : GroupType.OIDC_TYPE);
|
||||
}
|
||||
|
||||
createGroupAsMember() {
|
||||
this.checkOnGoing = true;
|
||||
this.memberService.addGroupMember(this.projectId, this.member_group, this.role_id)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.checkOnGoing = false;
|
||||
}
|
||||
))
|
||||
.subscribe(
|
||||
res => {
|
||||
this.role_id = null;
|
||||
this.addHttpAuthOpened = false;
|
||||
this.added.emit(true);
|
||||
},
|
||||
err => {
|
||||
let errorMessageKey: string = errorHandler(err);
|
||||
this.translateService
|
||||
.get(errorMessageKey)
|
||||
.subscribe(errorMessage => this.inlineAlert.showInlineError(errorMessage));
|
||||
this.added.emit(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
onSubmit(): void {
|
||||
this.createGroupAsMember();
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.role_id = null;
|
||||
this.addHttpAuthOpened = false;
|
||||
}
|
||||
|
||||
|
||||
openAddMemberModal(): void {
|
||||
this.currentForm.reset();
|
||||
this.addHttpAuthOpened = true;
|
||||
this.role_id = 1;
|
||||
this.inlineAlert.close();
|
||||
}
|
||||
|
||||
|
||||
public get isValid(): boolean {
|
||||
return this.currentForm &&
|
||||
this.currentForm.valid &&
|
||||
!this.checkOnGoing;
|
||||
}
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
<clr-modal [(clrModalOpen)]="addMemberOpened" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{'MEMBER.NEW_MEMBER' | translate}}</h3>
|
||||
<div class="modal-body">
|
||||
<inline-alert class="modal-title"></inline-alert>
|
||||
<label>{{ 'MEMBER.NEW_USER_INFO' | translate}}</label>
|
||||
<form #memberForm="ngForm" class="clr-form clr-form-horizontal">
|
||||
<div class="clr-form-control">
|
||||
<label class="required clr-control-label">{{'MEMBER.NAME' | translate}}</label>
|
||||
<div class="clr-control-container" [class.clr-error]="!isMemberNameValid">
|
||||
<div class="clr-control-container" [class.clr-error]="(memberName.invalid && (memberName.touched||memberName.dirty)) || !isMemberNameValid">
|
||||
<div class="clr-input-wrapper" (mouseleave)="leaveInput()">
|
||||
<input class="clr-input" type="text" id="member_name" [(ngModel)]="member.entity_name"
|
||||
<input class="clr-input" type="text" id="member_name" [(ngModel)]="member.username"
|
||||
name="member_name"
|
||||
size="20"
|
||||
#memberName="ngModel"
|
||||
required
|
||||
(keyup)="handleValidation()" autocomplete="off">
|
||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||
<div class="selectBox" [style.display]="selectUserName.length ? 'block' : 'none'" >
|
||||
<div class="selectBox" [style.display]="searchedUserLists.length ? 'block' : 'none'" >
|
||||
<ul>
|
||||
<li *ngFor="let name of selectUserName" (click)="selectedName(name)">{{name}}</li>
|
||||
<li *ngFor="let user of searchedUserLists" (click)="selectedName(user.username)">{{user.username}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<clr-control-error *ngIf="!isMemberNameValid" class="tooltip-content">
|
||||
<clr-control-error *ngIf="(memberName.invalid && (memberName.touched||memberName.dirty)) || !isMemberNameValid" class="tooltip-content">
|
||||
{{ memberTooltip | translate }}
|
||||
</clr-control-error>
|
||||
</div>
|
||||
@ -30,23 +30,23 @@
|
||||
<label class="clr-control-label">{{'MEMBER.ROLE' | translate}}</label>
|
||||
<div class="clr-control-container">
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_project_admin" [value]=1 [(ngModel)]="member.role_id">
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_project_admin" [value]=1 [(ngModel)]="roleId">
|
||||
<label for="checkrads_project_admin">{{'MEMBER.PROJECT_ADMIN' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_project_maintainer" [value]=4 [(ngModel)]="member.role_id">
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_project_maintainer" [value]=4 [(ngModel)]="roleId">
|
||||
<label for="checkrads_project_maintainer">{{'MEMBER.PROJECT_MAINTAINER' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_developer" [value]=2 [(ngModel)]="member.role_id">
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_developer" [value]=2 [(ngModel)]="roleId">
|
||||
<label for="checkrads_developer">{{'MEMBER.DEVELOPER' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_guest" [value]=3 [(ngModel)]="member.role_id">
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_guest" [value]=3 [(ngModel)]="roleId">
|
||||
<label for="checkrads_guest">{{'MEMBER.GUEST' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_limited_guest" [value]=5 [(ngModel)]="member.role_id">
|
||||
<input clrRadio type="radio" name="member_role" id="checkrads_limited_guest" [value]=5 [(ngModel)]="roleId">
|
||||
<label for="checkrads_limited_guest">{{'MEMBER.LIMITED_GUEST' | translate}}</label>
|
||||
</clr-radio-wrapper>
|
||||
</div>
|
||||
@ -55,6 +55,6 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
<button [clrLoading]="btnStatus" type="button" class="btn btn-primary" [disabled]="!isValid()" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||
</div>
|
||||
</clr-modal>
|
||||
|
@ -6,7 +6,6 @@
|
||||
position: absolute;
|
||||
width: 173px;
|
||||
height: auto;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0,0,0,.15);
|
||||
border-right-width: 2px;
|
||||
@ -17,7 +16,8 @@
|
||||
}
|
||||
.selectBox ul li{
|
||||
list-style: none;
|
||||
padding: 3px 20px
|
||||
padding: 3px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectBox ul li:hover{
|
||||
color: #262626;
|
||||
|
@ -1,23 +1,23 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { AddMemberComponent } from './add-member.component';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { MemberService } from '../member.service';
|
||||
import { UserService } from '../../../left-side-nav/user/user.service';
|
||||
import { of } from 'rxjs';
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||
import { MemberService } from 'ng-swagger-gen/services/member.service';
|
||||
|
||||
describe('AddMemberComponent', () => {
|
||||
let component: AddMemberComponent;
|
||||
let fixture: ComponentFixture<AddMemberComponent>;
|
||||
const mockMemberService = {
|
||||
getUsersNameList: () => {
|
||||
listProjectMembers: () => {
|
||||
return of([]);
|
||||
}
|
||||
};
|
||||
const mockUserService = {
|
||||
getUsersNameList: () => {
|
||||
listUsers: () => {
|
||||
return of([
|
||||
[], []
|
||||
]);
|
||||
|
@ -11,64 +11,51 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { debounceTime, finalize } from 'rxjs/operators';
|
||||
import { AfterViewChecked, Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, } from '@angular/core';
|
||||
import { debounceTime, finalize, switchMap } from 'rxjs/operators';
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { forkJoin, Subject } from "rxjs";
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { of, Subject, Subscription } from "rxjs";
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { UserService } from '../../../left-side-nav/user/user.service';
|
||||
import { User } from "../../../left-side-nav/user/user";
|
||||
import { Project } from "../../project";
|
||||
import { Member } from '../member';
|
||||
import { MemberService } from '../member.service';
|
||||
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
||||
import { InlineAlertComponent } from "../../../../shared/components/inline-alert/inline-alert.component";
|
||||
import { ProjectMemberEntity } from "../../../../../../ng-swagger-gen/models/project-member-entity";
|
||||
import { ClrLoadingState } from "@clr/angular";
|
||||
import { MemberService } from 'ng-swagger-gen/services/member.service';
|
||||
import { UserService } from 'ng-swagger-gen/services/user.service';
|
||||
import { UserResp } from "../../../../../../ng-swagger-gen/models/user-resp";
|
||||
import { UserEntity } from "../../../../../../ng-swagger-gen/models/user-entity";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'add-member',
|
||||
templateUrl: 'add-member.component.html',
|
||||
styleUrls: ['add-member.component.scss'],
|
||||
providers: [UserService],
|
||||
})
|
||||
export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
|
||||
|
||||
@Input() memberList: ProjectMemberEntity[] = [];
|
||||
member: ProjectMemberEntity = new Member();
|
||||
|
||||
addMemberOpened: boolean;
|
||||
|
||||
memberForm: NgForm;
|
||||
|
||||
export class AddMemberComponent implements OnInit, OnDestroy {
|
||||
member: UserEntity = {};
|
||||
addMemberOpened: boolean = false;
|
||||
staticBackdrop: boolean = true;
|
||||
closable: boolean = false;
|
||||
|
||||
@ViewChild('memberForm', {static: true})
|
||||
currentForm: NgForm;
|
||||
|
||||
hasChanged: boolean;
|
||||
|
||||
@ViewChild(InlineAlertComponent)
|
||||
inlineAlert: InlineAlertComponent;
|
||||
|
||||
@Input() projectId: number;
|
||||
@Output() added = new EventEmitter<boolean>();
|
||||
|
||||
isMemberNameValid: boolean = true;
|
||||
memberTooltip: string = 'MEMBER.USERNAME_IS_REQUIRED';
|
||||
nameChecker: Subject<string> = new Subject<string>();
|
||||
searcher: Subject<string> = new Subject<string>();
|
||||
nameCheckerSub: Subscription;
|
||||
searcherSub: Subscription;
|
||||
checkOnGoing: boolean = false;
|
||||
selectUserName: string[] = [];
|
||||
userLists: User[];
|
||||
searchedUserLists: UserResp[] = [];
|
||||
btnStatus: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
roleId: number = 1; // default value is 1(project admin)
|
||||
|
||||
constructor(private memberService: MemberService,
|
||||
private userService: UserService,
|
||||
private errorHandle: ErrorHandler,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private translateService: TranslateService,
|
||||
private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -78,115 +65,130 @@ export class AddMemberComponent implements AfterViewChecked, OnInit, OnDestroy {
|
||||
hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||
}
|
||||
if (hasProjectAdminRole) {
|
||||
this.nameChecker.pipe(
|
||||
debounceTime(500))
|
||||
.subscribe((name: string) => {
|
||||
let cont = this.currentForm.controls['member_name'];
|
||||
if (cont) {
|
||||
this.isMemberNameValid = cont.valid;
|
||||
if (cont.valid) {
|
||||
this.checkOnGoing = true;
|
||||
forkJoin([this.userService.getUsersNameList(cont.value, 20), this.memberService
|
||||
.listMembers(this.projectId, cont.value)]).subscribe((res: Array<any>) => {
|
||||
this.userLists = res[0];
|
||||
if (res[1].filter(m => { return m.entity_name === cont.value; }).length > 0) {
|
||||
this.isMemberNameValid = false;
|
||||
this.memberTooltip = 'MEMBER.USERNAME_ALREADY_EXISTS';
|
||||
}
|
||||
this.checkOnGoing = false;
|
||||
if (this.userLists && this.userLists.length) {
|
||||
this.selectUserName = [];
|
||||
this.userLists.forEach(data => {
|
||||
if (data.username.startsWith(cont.value) && !this.memberList.find(mem => mem.entity_name === data.username)) {
|
||||
if (this.selectUserName.length < 10) {
|
||||
this.selectUserName.push(data.username);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, error => {
|
||||
this.checkOnGoing = false;
|
||||
});
|
||||
} else {
|
||||
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
|
||||
if (!this.searcherSub) {
|
||||
this.searcherSub = this.searcher.pipe(
|
||||
debounceTime(500),
|
||||
switchMap(name => {
|
||||
if (name) {
|
||||
return this.userService.listUsers({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
q: encodeURIComponent(`username=~${name}`)
|
||||
});
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
}),
|
||||
).subscribe(res => {
|
||||
if (res) {
|
||||
this.searchedUserLists = res;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!this.nameCheckerSub) {
|
||||
this.nameCheckerSub = this.nameChecker.pipe(
|
||||
debounceTime(500),
|
||||
switchMap(name => {
|
||||
if (name) {
|
||||
this.checkOnGoing = true;
|
||||
return this.memberService.listProjectMembers({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
projectNameOrId: this.projectId.toString(),
|
||||
entityname: name
|
||||
}).pipe(
|
||||
finalize(() => this.checkOnGoing = false)
|
||||
);
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
}),
|
||||
).subscribe(res => {
|
||||
if (res && res.length) {
|
||||
if (res.filter(m => m.entity_name === this.member.username).length > 0) {
|
||||
this.isMemberNameValid = false;
|
||||
this.memberTooltip = 'MEMBER.USERNAME_ALREADY_EXISTS';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.nameChecker.unsubscribe();
|
||||
if (this.nameCheckerSub) {
|
||||
this.nameCheckerSub.unsubscribe();
|
||||
this.nameCheckerSub = null;
|
||||
}
|
||||
if (this.searcherSub) {
|
||||
this.searcherSub.unsubscribe();
|
||||
this.searcherSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (!this.member.entity_name || this.member.entity_name.length === 0) { return; }
|
||||
if (!this.member.username || this.member.username.length === 0) { return; }
|
||||
this.btnStatus = ClrLoadingState.LOADING;
|
||||
this.memberService
|
||||
.addUserMember(this.projectId, {username: this.member.entity_name}, +this.member.role_id).pipe(
|
||||
finalize(() => {
|
||||
this.addMemberOpened = false;
|
||||
this.member.role_id = null;
|
||||
}
|
||||
))
|
||||
.createProjectMember({
|
||||
projectNameOrId: this.projectId.toString(),
|
||||
projectMember: {
|
||||
role_id: this.roleId,
|
||||
member_user: this.member
|
||||
}
|
||||
})
|
||||
.subscribe(
|
||||
() => {
|
||||
this.addMemberOpened = false;
|
||||
this.btnStatus = ClrLoadingState.SUCCESS;
|
||||
this.messageHandlerService.showSuccess('MEMBER.ADDED_SUCCESS');
|
||||
this.added.emit(true);
|
||||
},
|
||||
error => {
|
||||
this.errorHandle.error(error);
|
||||
this.btnStatus = ClrLoadingState.ERROR;
|
||||
this.inlineAlert.showInlineError(error);
|
||||
});
|
||||
}
|
||||
|
||||
selectedName(username: string) {
|
||||
this.member.entity_name = username;
|
||||
this.selectUserName = [];
|
||||
this.member.username = username;
|
||||
this.nameChecker.next(username);
|
||||
this.searchedUserLists = [];
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.addMemberOpened = false;
|
||||
this.memberForm.reset();
|
||||
}
|
||||
|
||||
leaveInput() {
|
||||
this.selectUserName = [];
|
||||
this.searchedUserLists = [];
|
||||
}
|
||||
ngAfterViewChecked(): void {
|
||||
if (this.memberForm !== this.currentForm) {
|
||||
this.memberForm = this.currentForm;
|
||||
}
|
||||
if (this.memberForm) {
|
||||
this.memberForm.valueChanges.subscribe(data => {
|
||||
let memberName = data['member_name'];
|
||||
if (memberName && memberName !== '') {
|
||||
this.hasChanged = true;
|
||||
} else {
|
||||
this.hasChanged = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openAddMemberModal(): void {
|
||||
this.currentForm.reset();
|
||||
this.member = new Member();
|
||||
this.currentForm.reset({
|
||||
member_role: 1
|
||||
});
|
||||
this.inlineAlert.close();
|
||||
this.member = {};
|
||||
this.addMemberOpened = true;
|
||||
this.hasChanged = false;
|
||||
this.member.role_id = 1;
|
||||
this.member.entity_name = '';
|
||||
this.member.username = '';
|
||||
this.isMemberNameValid = true;
|
||||
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
|
||||
this.selectUserName = [];
|
||||
this.searchedUserLists = [];
|
||||
}
|
||||
|
||||
handleValidation(): void {
|
||||
let cont = this.currentForm.controls['member_name'];
|
||||
if (cont) {
|
||||
this.nameChecker.next(cont.value);
|
||||
this.nameChecker.next(this.member.username);
|
||||
this.searcher.next(this.member.username);
|
||||
if (!this.member.username) {
|
||||
this.searchedUserLists = [];
|
||||
this.isMemberNameValid = false;
|
||||
this.memberTooltip = 'MEMBER.USERNAME_IS_REQUIRED';
|
||||
} else {
|
||||
this.isMemberNameValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
public get isValid(): boolean {
|
||||
isValid(): boolean {
|
||||
return this.currentForm &&
|
||||
this.currentForm.valid &&
|
||||
this.isMemberNameValid &&
|
||||
|
@ -16,7 +16,7 @@
|
||||
<button class="btn btn-secondary" (click)="openAddMemberModal()" [disabled]="!hasCreateMemberPermission">
|
||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.USER' | translate }}</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasCreateMemberPermission || !((isLdapMode && currentUser?.has_admin_role) || isHttpAuthMode || isOidcMode)">
|
||||
<button class="btn btn-secondary" (click)="openAddGroupModal()" [disabled]="!hasCreateMemberPermission || !(isLdapMode || isHttpAuthMode || isOidcMode)">
|
||||
<span><clr-icon shape="plus" size="16"></clr-icon> {{'MEMBER.LDAP_GROUP' | translate}}</span>
|
||||
</button>
|
||||
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-link" clrDropdownTrigger>
|
||||
@ -54,7 +54,6 @@
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
<add-member [projectId]="projectId" [memberList]="members" (added)="addedMember($event)"></add-member>
|
||||
<add-group [projectId]="projectId" [memberList]="members" (added)="addedGroup($event)"></add-group>
|
||||
<add-http-auth-group [projectId]="projectId" (added)="addedGroup($event)"></add-http-auth-group>
|
||||
<add-member [projectId]="projectId" (added)="addedMember($event)"></add-member>
|
||||
<add-group [projectId]="projectId" (added)="addedGroup($event)"></add-group>
|
||||
</div>
|
||||
|
@ -19,8 +19,6 @@ import { TranslateService } from "@ngx-translate/core";
|
||||
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
||||
import { SessionService } from "../../../shared/services/session.service";
|
||||
import { SessionUser } from "../../../shared/entities/session-user";
|
||||
import { AddGroupComponent } from './add-group/add-group.component';
|
||||
import { AddHttpAuthGroupComponent } from './add-http-auth-group/add-http-auth-group.component';
|
||||
import { AddMemberComponent } from "./add-member/add-member.component";
|
||||
import { AppConfigService } from "../../../services/app-config.service";
|
||||
import { OperationService } from "../../../shared/components/operation/operation.service";
|
||||
@ -35,6 +33,7 @@ import { DEFAULT_PAGE_SIZE } from "../../../shared/units/utils";
|
||||
import { MemberService } from "../../../../../ng-swagger-gen/services/member.service";
|
||||
import { ClrDatagridStateInterface } from "@clr/angular";
|
||||
import { ProjectMemberEntity } from "../../../../../ng-swagger-gen/models/project-member-entity";
|
||||
import { AddGroupComponent } from './add-group/add-group.component';
|
||||
|
||||
@Component({
|
||||
templateUrl: "member.component.html",
|
||||
@ -64,11 +63,8 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
isOidcMode: boolean;
|
||||
@ViewChild(AddMemberComponent)
|
||||
addMemberComponent: AddMemberComponent;
|
||||
|
||||
@ViewChild(AddGroupComponent)
|
||||
addGroupComponent: AddGroupComponent;
|
||||
@ViewChild(AddHttpAuthGroupComponent)
|
||||
addHttpAuthGroupComponent: AddHttpAuthGroupComponent;
|
||||
hasCreateMemberPermission: boolean;
|
||||
hasUpdateMemberPermission: boolean;
|
||||
hasDeleteMemberPermission: boolean;
|
||||
@ -193,11 +189,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
||||
|
||||
// Add group
|
||||
openAddGroupModal() {
|
||||
if (this.isLdapMode) {
|
||||
this.addGroupComponent.open();
|
||||
} else {
|
||||
this.addHttpAuthGroupComponent.openAddMemberModal();
|
||||
}
|
||||
this.addGroupComponent.openAddGroupModal();
|
||||
}
|
||||
addedGroup(result: boolean) {
|
||||
this.searchMember = "";
|
||||
|
@ -3,9 +3,8 @@ import { RouterModule, Routes } from "@angular/router";
|
||||
import { SharedModule } from "../../../shared/shared.module";
|
||||
import { MemberComponent } from "./member.component";
|
||||
import { AddMemberComponent } from "./add-member/add-member.component";
|
||||
import { AddHttpAuthGroupComponent } from "./add-http-auth-group/add-http-auth-group.component";
|
||||
import { AddGroupComponent } from "./add-group/add-group.component";
|
||||
import { MemberService } from "./member.service";
|
||||
import { AddGroupComponent } from './add-group/add-group.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -17,15 +16,11 @@ const routes: Routes = [
|
||||
declarations: [
|
||||
MemberComponent,
|
||||
AddMemberComponent,
|
||||
AddHttpAuthGroupComponent,
|
||||
AddGroupComponent
|
||||
AddGroupComponent,
|
||||
],
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
SharedModule
|
||||
],
|
||||
providers: [
|
||||
MemberService
|
||||
]
|
||||
})
|
||||
export class MemberModule { }
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { inject, TestBed } from '@angular/core/testing';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { MemberService } from './member.service';
|
||||
|
||||
describe('MemberService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientTestingModule
|
||||
],
|
||||
providers: [MemberService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([MemberService], (service: MemberService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
@ -1,72 +0,0 @@
|
||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { User } from '../../left-side-nav/user/user';
|
||||
import { Member } from './member';
|
||||
import { Observable, throwError as observableThrowError } from "rxjs";
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { CURRENT_BASE_HREF, HTTP_GET_OPTIONS, HTTP_JSON_OPTIONS } from "../../../shared/units/utils";
|
||||
|
||||
@Injectable()
|
||||
export class MemberService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
listMembers(projectId: number, entity_name: string): Observable<Member[]> {
|
||||
return this.http
|
||||
.get(`${ CURRENT_BASE_HREF }/projects/${projectId}/members?entityname=${entity_name}`, HTTP_GET_OPTIONS).pipe(
|
||||
map(response => response as Member[]),
|
||||
catchError(error => observableThrowError(error)), );
|
||||
}
|
||||
|
||||
addUserMember(projectId: number, user: User, roleId: number): Observable<any> {
|
||||
let member_user = {};
|
||||
if (user.user_id) {
|
||||
member_user = {user_id: user.user_id};
|
||||
} else if (user.username) {
|
||||
member_user = {username: user.username};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
return this.http.post(
|
||||
`${ CURRENT_BASE_HREF }/projects/${projectId}/members`,
|
||||
{
|
||||
role_id: roleId,
|
||||
member_user: member_user
|
||||
},
|
||||
HTTP_JSON_OPTIONS).pipe(
|
||||
catchError(error => observableThrowError(error)), );
|
||||
}
|
||||
|
||||
addGroupMember(projectId: number, group: any, roleId: number): Observable<any> {
|
||||
return this.http
|
||||
.post(`${ CURRENT_BASE_HREF }/projects/${projectId}/members`,
|
||||
{ role_id: roleId, member_group: group},
|
||||
HTTP_JSON_OPTIONS).pipe(
|
||||
catchError(error => observableThrowError(error)), );
|
||||
}
|
||||
|
||||
changeMemberRole(projectId: number, userId: number, roleId: number): Observable<any> {
|
||||
return this.http
|
||||
.put(`${ CURRENT_BASE_HREF }/projects/${projectId}/members/${userId}`, { role_id: roleId }, HTTP_JSON_OPTIONS)
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
deleteMember(projectId: number, memberId: number): Observable<any> {
|
||||
return this.http
|
||||
.delete(`${ CURRENT_BASE_HREF }/projects/${projectId}/members/${memberId}`)
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
{
|
||||
"user_id": 1,
|
||||
"username": "admin",
|
||||
"email": "",
|
||||
"password": "",
|
||||
"realname": "",
|
||||
"comment": "",
|
||||
"deleted": false,
|
||||
"role_name": "projectAdmin",
|
||||
"role_id": 1,
|
||||
"has_admin_role": false,
|
||||
"reset_uuid": "",
|
||||
"creation_time": "0001-01-01T00:00:00Z",
|
||||
"update_time": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
*/
|
||||
|
||||
export class Member {
|
||||
id: number;
|
||||
project_id: number;
|
||||
entity_name: string;
|
||||
role_name: string;
|
||||
role_id: number;
|
||||
entity_id: number;
|
||||
entity_type: string;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { TargetExistsValidatorDirective } from './target-exists-directive';
|
||||
|
||||
describe('TargetExistsValidatorDirective', () => {
|
||||
it('should create an instance', () => {
|
||||
const directive = new TargetExistsValidatorDirective(null, null);
|
||||
expect(directive).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,78 +0,0 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Directive, OnChanges, Input, SimpleChanges } from '@angular/core';
|
||||
import { NG_ASYNC_VALIDATORS, Validator, Validators, ValidatorFn, AbstractControl } from '@angular/forms';
|
||||
import { MemberService } from '../../base/project/member/member.service';
|
||||
import { Member } from '../../base/project/member/member';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ProjectDefaultService, ProjectService } from "../services";
|
||||
|
||||
@Directive({
|
||||
selector: '[targetExists]',
|
||||
providers: [
|
||||
MemberService,
|
||||
{ provide: ProjectService, useClass: ProjectDefaultService },
|
||||
{ provide: NG_ASYNC_VALIDATORS, useExisting: TargetExistsValidatorDirective, multi: true },
|
||||
]
|
||||
})
|
||||
export class TargetExistsValidatorDirective implements Validator, OnChanges {
|
||||
@Input() targetExists: string;
|
||||
@Input() projectId: number;
|
||||
|
||||
valFn = Validators.nullValidator;
|
||||
|
||||
constructor(
|
||||
private projectService: ProjectService,
|
||||
private memberService: MemberService) { }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const change = changes['targetExists'];
|
||||
if (change) {
|
||||
const target: string = change.currentValue;
|
||||
this.valFn = this.targetExistsValidator(target);
|
||||
} else {
|
||||
this.valFn = Validators.nullValidator;
|
||||
}
|
||||
}
|
||||
validate(control: AbstractControl): { [key: string]: any } {
|
||||
return this.valFn(control);
|
||||
}
|
||||
|
||||
targetExistsValidator(target: string): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: any } => {
|
||||
switch (target) {
|
||||
case 'PROJECT_NAME':
|
||||
return new Observable(observer => {
|
||||
this.projectService
|
||||
.checkProjectExists(control.value)
|
||||
.subscribe(res => observer.next({ 'targetExists': true }), error => observer.next(null));
|
||||
});
|
||||
case 'MEMBER_NAME':
|
||||
return new Observable(observer => {
|
||||
this.memberService
|
||||
.listMembers(this.projectId, control.value)
|
||||
.subscribe((members: Member[]) => {
|
||||
return members.filter(m => {
|
||||
if (m.entity_name === control.value) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}).length > 0 ?
|
||||
observer.next({ 'targetExists': true }) : observer.next(null);
|
||||
}, error => observer.next(null));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { map, catchError } from "rxjs/operators";
|
||||
import { Observable, throwError as observableThrowError } from "rxjs";
|
||||
import { SessionUser, SessionUserBackend } from '../entities/session-user';
|
||||
import { Member } from '../../base/project/member/member';
|
||||
import { SessionViewmodelFactory } from './session.viewmodel.factory';
|
||||
import {
|
||||
HTTP_FORM_OPTIONS,
|
||||
@ -27,6 +26,7 @@ import {
|
||||
import { FlushAll } from "../units/cache-util";
|
||||
import { SignInCredential } from "../../account/sign-in/sign-in-credential";
|
||||
import { DeFaultLang } from "../entities/shared.const";
|
||||
import { ProjectMemberEntity } from "../../../../ng-swagger-gen/models/project-member-entity";
|
||||
|
||||
|
||||
const signInUrl = '/c/login';
|
||||
@ -52,7 +52,7 @@ const langMap = {
|
||||
})
|
||||
export class SessionService {
|
||||
currentUser: SessionUser = null;
|
||||
projectMembers: Member[];
|
||||
projectMembers: ProjectMemberEntity[];
|
||||
constructor(private http: HttpClient, public sessionViewmodel: SessionViewmodelFactory) { }
|
||||
|
||||
// Handle the related exceptions
|
||||
@ -180,11 +180,11 @@ export class SessionService {
|
||||
.pipe(catchError(error => this.handleError(error)));
|
||||
}
|
||||
|
||||
setProjectMembers(projectMembers: Member[]): void {
|
||||
setProjectMembers(projectMembers: ProjectMemberEntity[]): void {
|
||||
this.projectMembers = projectMembers;
|
||||
}
|
||||
|
||||
getProjectMembers(): Member[] {
|
||||
getProjectMembers(): ProjectMemberEntity[] {
|
||||
return this.projectMembers;
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,10 @@
|
||||
"SWITCH_TITLE": "Bestätige Rollenwechsel des Projekt-Mitglieds",
|
||||
"SWITCH_SUMMARY": "Soll die Rolle des Mitglieds {{param}} geändert werden?",
|
||||
"SET_ROLE": "Rolle festlegen",
|
||||
"REMOVE": "Entfernen"
|
||||
"REMOVE": "Entfernen",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "Name",
|
||||
|
@ -321,9 +321,9 @@
|
||||
"GROUP_TYPE": "Group",
|
||||
"USER_TYPE": "User",
|
||||
"USERNAME_IS_REQUIRED": "Username is required",
|
||||
"USERNAME_DOES_NOT_EXISTS": "Username does not exist.",
|
||||
"USERNAME_ALREADY_EXISTS": "Username already exists.",
|
||||
"UNKNOWN_ERROR": "Unknown error occurred while adding member.",
|
||||
"USERNAME_DOES_NOT_EXISTS": "Username does not exist",
|
||||
"USERNAME_ALREADY_EXISTS": "Username has been already added to this project",
|
||||
"UNKNOWN_ERROR": "Unknown error occurred while adding member",
|
||||
"FILTER_PLACEHOLDER": "Filter Members",
|
||||
"DELETION_TITLE": "Confirm project members deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete project members {{param}}?",
|
||||
@ -334,7 +334,10 @@
|
||||
"SWITCH_TITLE": "Confirm project members switch",
|
||||
"SWITCH_SUMMARY": "Do you want to switch project members {{param}}?",
|
||||
"SET_ROLE": "Set Role",
|
||||
"REMOVE": "Remove"
|
||||
"REMOVE": "Remove",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "Name",
|
||||
|
@ -335,7 +335,10 @@
|
||||
"SWITCH_TITLE": "Confirm project members switch",
|
||||
"SWITCH_SUMMARY": "Do you want to switch project members {{param}}?",
|
||||
"SET_ROLE": "Set Role",
|
||||
"REMOVE": "Remove"
|
||||
"REMOVE": "Remove",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "Name",
|
||||
|
@ -326,7 +326,10 @@
|
||||
"SWITCHED_SUCCESS": "Rôle du membre changé avec succés.",
|
||||
"OF": "de",
|
||||
"SET_ROLE": "Set Role",
|
||||
"REMOVE": "Remove"
|
||||
"REMOVE": "Remove",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "Nom",
|
||||
|
@ -332,7 +332,10 @@
|
||||
"SWITCH_TITLE": "Confirmar alteração dos membros do projeto",
|
||||
"SWITCH_SUMMARY": "Você quer alterar os membros do projeto {{param}}?",
|
||||
"SET_ROLE": "DEFINIR FUNÇÃO",
|
||||
"REMOVE": "Remover"
|
||||
"REMOVE": "Remover",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "Nome",
|
||||
|
@ -334,7 +334,10 @@
|
||||
"SWITCH_TITLE": "Proje üyelerinin geçişini onayla",
|
||||
"SWITCH_SUMMARY": "Proje üyelerini değiştirmek ister misiniz? {{param}}?",
|
||||
"SET_ROLE": "ROL ATA",
|
||||
"REMOVE": "Sil"
|
||||
"REMOVE": "Sil",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "İsim",
|
||||
|
@ -319,21 +319,24 @@
|
||||
"MEMBER_TYPE": "成员类型",
|
||||
"GROUP_TYPE": "组",
|
||||
"USER_TYPE": "用户",
|
||||
"USERNAME_IS_REQUIRED": "用户名为必填项。",
|
||||
"USERNAME_DOES_NOT_EXISTS": "用户名不存在。",
|
||||
"USERNAME_ALREADY_EXISTS": "用户名已存在。",
|
||||
"UNKNOWN_ERROR": "添加成员时发生未知错误。",
|
||||
"USERNAME_IS_REQUIRED": "用户名为必填项",
|
||||
"USERNAME_DOES_NOT_EXISTS": "用户名不存在",
|
||||
"USERNAME_ALREADY_EXISTS": "该用户已存在于当前项目",
|
||||
"UNKNOWN_ERROR": "添加成员时发生未知错误",
|
||||
"FILTER_PLACEHOLDER": "过滤成员",
|
||||
"DELETION_TITLE": "删除项目成员确认",
|
||||
"DELETION_SUMMARY": "你确认删除项目成员 {{param}}?",
|
||||
"ADDED_SUCCESS": "成功新增成员。",
|
||||
"DELETED_SUCCESS": "成功删除成员。",
|
||||
"SWITCHED_SUCCESS": "切换角色成功。",
|
||||
"ADDED_SUCCESS": "成功新增成员",
|
||||
"DELETED_SUCCESS": "成功删除成员",
|
||||
"SWITCHED_SUCCESS": "切换角色成功",
|
||||
"OF": "共计",
|
||||
"SWITCH_TITLE": "切换项目成员确认",
|
||||
"SWITCH_SUMMARY": "你确认切换项目成员 {{param}}??",
|
||||
"SET_ROLE": "设置角色",
|
||||
"REMOVE": "移除成员"
|
||||
"REMOVE": "移除成员",
|
||||
"GROUP_NAME_REQUIRED": "组名称为必填项",
|
||||
"NON_EXISTENT_GROUP": "组名称不存在",
|
||||
"GROUP_ALREADY_ADDED": "该组已存在于当前项目"
|
||||
},
|
||||
"ROBOT_ACCOUNT": {
|
||||
"NAME": "名称",
|
||||
|
@ -331,7 +331,10 @@
|
||||
"SWITCH_TITLE": "切換項目成員確認",
|
||||
"SWITCH_SUMMARY": "你確認切換項目成員{{param}}??",
|
||||
"SET_ROLE": "設置角色",
|
||||
"REMOVE": "移除成員"
|
||||
"REMOVE": "移除成員",
|
||||
"GROUP_NAME_REQUIRED": "Group name is required",
|
||||
"NON_EXISTENT_GROUP": "Group name does not exists",
|
||||
"GROUP_ALREADY_ADDED": "Group name has been already added to this project"
|
||||
},
|
||||
"ROBOT_ACCOUNT":{
|
||||
"NAME": "名稱",
|
||||
|
Loading…
Reference in New Issue
Block a user