import {ManageUsersAddComponent} from "./modules/user/users/manage-users-add/manage-users-add.component";
import {ManageUsersEditComponent} from "./modules/user/users/manage-users-edit/manage-users-edit.component";
import {RoutingGuardService as Guard} from "./services/routing-guard.service";
+import {ManageUsersDeleteComponent} from "./modules/user/users/manage-users-delete/manage-users-delete.component";
/**
* You can use Guard (RoutingGuardService) for permission checking. The service needs data with one parameter 'perm',
{path: 'add', component: ManageUsersAddComponent},
{path: 'edit/:userid', component: ManageUsersEditComponent},
{path: 'edit', redirectTo:'edit/guest' },
+ {path: 'delete/:userid', component: ManageUsersDeleteComponent},
{path: '', redirectTo:'list', pathMatch:'full'}
]
},
import {ManageUsersEditComponent} from "./users/manage-users-edit/manage-users-edit.component";
import {SharedModule} from "../shared/shared.module";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import { ManageUsersDeleteComponent } from './users/manage-users-delete/manage-users-delete.component';
ManageUsersComponent,
ManageUsersListComponent,
ManageUsersAddComponent,
- ManageUsersEditComponent
+ ManageUsersEditComponent,
+ ManageUsersDeleteComponent
],
exports: [
ManageUsersComponent,
[attr.disabled]="userForm.valid?null:true">{{'users.add.submit'|translate}}</button>
</div>
<div *ngIf="success" class="alert alert-success" role="alert">
- User <a [routerLink]="['user','users','edit',result?.user_id]">{{result?.userid}}</a> was added to the list.
+ User <a [routerLink]="['/user','users','edit',result?.user_id]">{{result?.user_id}}</a> was added to the list.
</div>
<div *ngIf="error" class="alert alert-danger" role="alert" >
<h4 class="alert-heading">{{'users.add.errortitle'|translate}}</h4>
this.result = user;
this.success = true;
this.error = false;
+ this.userForm.reset(this.formInitialValues);
});
}
}
- getAllErrors(formGroup: FormGroup, errors: string[] = []) : string[] {
- Object.keys(formGroup.controls).forEach(field => {
- const control = formGroup.get(field);
- if (control instanceof FormControl && control.errors != null) {
- let keys = Object.keys(control.errors).map(errorKey=>field+'.'+errorKey);
- errors = errors.concat(keys);
- } else if (control instanceof FormGroup) {
- errors = errors.concat(this.getAllErrors(control));
- }
- });
- return errors;
- }
- getAttributeErrors(control:string):string[] {
- return Object.keys(this.userForm.get(control).errors);
- }
errorResult: ErrorResult;
result: UserInfo;
user: string;
+ formInitialValues;
userForm = this.fb.group({
user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize), whitespaceValidator()], this.userUidExistsValidator()],
})
constructor(public userService: UserService, public fb: FormBuilder) {
+ this.formInitialValues=this.userForm.value
}
return forbidden ? {forbiddenName: {value: control.value}} : null;
};
}
+
+ getAllErrors(formGroup: FormGroup, errors: string[] = []) : string[] {
+ Object.keys(formGroup.controls).forEach(field => {
+ const control = formGroup.get(field);
+ if (control instanceof FormControl && control.errors != null) {
+ let keys = Object.keys(control.errors).map(errorKey=>field+'.'+errorKey);
+ errors = errors.concat(keys);
+ } else if (control instanceof FormGroup) {
+ errors = errors.concat(this.getAllErrors(control));
+ }
+ });
+ return errors;
+ }
+
+ getAttributeErrors(control:string):string[] {
+ return Object.keys(this.userForm.get(control).errors);
+ }
}
export function whitespaceValidator(): ValidatorFn {
--- /dev/null
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+<ng-template #userdelete let-modal>
+ <div class="modal-header">
+ <h5 class="modal-title alert" id="modal-basic-title">{{'users.delete.modal.title'|translate}}</h5>
+ <button type="button" class="close alert" aria-label="{{'modal.close'|translate}}" (click)="modal.dismiss('Cross click')">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="alert alert-warning">
+ <p [innerHTML]="'users.delete.modal.text'|translate:{'user_id':user_id}"></p>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-danger" (click)="modal.close('YES')">{{'form.button.yes'|translate}}</button>
+ <button type="button" class="btn btn-secondary" (click)="modal.close('NO')">{{'form.button.no'|translate}}</button>
+ </div>
+</ng-template>
--- /dev/null
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ManageUsersDeleteComponent } from './manage-users-delete.component';
+
+describe('ManageUsersDeleteComponent', () => {
+ let component: ManageUsersDeleteComponent;
+ let fixture: ComponentFixture<ManageUsersDeleteComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ManageUsersDeleteComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ManageUsersDeleteComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute, Router} from "@angular/router";
+import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
+import {UserService} from "../../../../services/user.service";
+
+@Component({
+ selector: 'app-manage-users-delete',
+ templateUrl: './manage-users-delete.component.html',
+ styleUrls: ['./manage-users-delete.component.scss']
+})
+export class ManageUsersDeleteComponent implements OnInit, AfterViewInit {
+
+ @ViewChild('userdelete') askModal;
+
+ user_id: string;
+
+ constructor(private route: ActivatedRoute, private modal: NgbModal,
+ private userService: UserService, private router : Router) {
+ this.route.params.subscribe((params)=>{
+ if (params.userid) {
+ this.user_id = params.userid;
+ }
+ })
+ }
+
+ ngOnInit(): void {
+
+ }
+
+ private runModal() {
+ if (this.user_id!=null && this.user_id!='') {
+ let modalInstance = this.modal.open(this.askModal).result.then((result) => {
+ console.log("Result: " + result);
+ let userId = this.user_id;
+ if (result=='YES' && userId!=null && userId!='') {
+ let deleted = this.userService.deleteUser(userId).subscribe();
+ if (deleted) {
+ this.router.navigate(['/user','users','list']);
+ }
+ }
+ }, (reason) => {
+ console.log("Reason: " + reason);
+ });
+ }
+ }
+
+ ngAfterViewInit(): void {
+ if (this.user_id!=null) {
+ this.runModal();
+ }
+ }
+
+}
<div class="form-group row col-md-8" *ngIf="!editUser.permanent">
<div class="col-md-1">Edit <span class="fas fa-edit"></span></div>
<div class="col-md-6">
- <input class="form-check-input" type="checkbox" [value]="editMode"
+ <input class="form-check-input" type="checkbox" [value]="editMode" [checked]="editMode"
(change)="editMode=!editMode"
>
</div>
[disabled]="userForm.invalid || !userForm.dirty">{{'users.edit.submit'|translate}}</button>
</div>
<div *ngIf="success" class="alert alert-success" role="alert">
- User <a [routerLink]="['user','users','edit',userid]">{{userid}}</a> was added to the list.
+ User has been updated.
</div>
- <div *ngIf="editMode && error" class="alert alert-danger" role="alert">
+ <div *ngIf="error" class="alert alert-danger" role="alert">
<h4 class="alert-heading">Errors</h4>
<ng-container *ngFor="let message of errorResult?.error_messages; first as isFirst">
<hr *ngIf="!isFirst">
</ng-container>
</div>
<div *ngIf="editMode && userForm.invalid" class="alert alert-danger" role="alert" >
- <h4 class="alert-heading">Errors</h4>
+ <h4 class="alert-heading">Validation Errors</h4>
<ng-container *ngFor="let message of getAllErrors(userForm); first as isFirst" >
<hr *ngIf="!isFirst">
<p>{{message}}</p>
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import {Component, OnInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
import {UserService} from "../../../../services/user.service";
import {FormBuilder, FormControl} from "@angular/forms";
-import {map, switchMap} from 'rxjs/operators';
+import {catchError, map, switchMap, tap} from 'rxjs/operators';
import {ManageUsersBaseComponent} from "../manage-users-base.component";
+import {ErrorResult} from "../../../../model/error-result";
@Component({
- selector: 'app-manage-users-edit',
- templateUrl: './manage-users-edit.component.html',
- styleUrls: ['./manage-users-edit.component.scss']
+ selector: 'app-manage-users-edit',
+ templateUrl: './manage-users-edit.component.html',
+ styleUrls: ['./manage-users-edit.component.scss']
})
export class ManageUsersEditComponent extends ManageUsersBaseComponent implements OnInit {
- editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required',
- 'password', 'confirm_password', 'validated'];
- editUser;
- originUser;
- editMode:boolean=false;
- minUserIdSize=0;
+ editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required',
+ 'password', 'confirm_password', 'validated'];
+ editUser;
+ originUser;
+ editMode: boolean;
+ minUserIdSize = 0;
- constructor(private route: ActivatedRoute, public userService: UserService, public fb: FormBuilder) {
- super(userService, fb);
- this.editUser = this.route.params.pipe(map (params => params.userid ), switchMap(userid => userService.getUser(userid)) ).subscribe(user => {
- this.editUser = user;
- this.originUser = user;
- this.copyToForm(this.editProperties, this.editUser);});
- }
+ constructor(private route: ActivatedRoute, public userService: UserService, public fb: FormBuilder) {
+ super(userService, fb);
+ this.editMode=false;
+ this.route.queryParams.subscribe((params)=>{
+ if (params.editmode) {
+ this.editMode=true;
+ }
+ })
+ this.editUser = this.route.params.pipe(
+ map(params => params.userid), switchMap(userid => userService.getUser(userid))).subscribe(user => {
+ this.editUser = user;
+ this.originUser = user;
+ this.copyToForm(this.editProperties, this.editUser);
+ });
+ }
- ngOnInit(): void {
- this.userForm.setControl('user_id', new FormControl());
- }
+ ngOnInit(): void {
+ // This resets the validators of the base class
+ this.userForm.get('user_id').clearValidators();
+ this.userForm.get('user_id').clearAsyncValidators();
+ }
- valid(field: string): string[] {
- if (this.editMode) {
- let classArr = super.valid(field);
- return classArr.concat('form-control')
- } else {
- return ['form-control-plaintext'];
+ valid(field: string): string[] {
+ if (this.editMode) {
+ let classArr = super.valid(field);
+ return classArr.concat('form-control')
+ } else {
+ return ['form-control-plaintext'];
+ }
}
- }
- onSubmit() {
- this.copyFromForm(this.editProperties)
+ onSubmit() {
+ let user = this.copyFromForm(this.editProperties);
+ this.userService.updateUser(user).pipe(
+ catchError((err: ErrorResult) => {
+ this.error = true;
+ this.success = false;
+ this.errorResult = err;
+ return [];
+ })
+ ).subscribe(userInfo=>{
+ this.error=false;
+ this.success=true;
+ this.errorResult=null;
+ this.result = userInfo;
+ this.editMode = false;
+ });
- }
+ }
}
<app-th-sorted [fieldArray]="['last_login']" contentText="users.attributes.last_login"></app-th-sorted>
<app-th-sorted [fieldArray]="['created']" contentText="users.attributes.created" ></app-th-sorted>
<app-th-sorted [fieldArray]="['last_password_change']" contentText="users.attributes.last_password_change"></app-th-sorted>
+ <th>Action</th>
</tr>
</thead>
<tbody>
<td>{{user.timestamp_last_login | date:'yyyy-MM-ddTHH:mm:ss'}}</td>
<td>{{user.timestamp_account_creation | date : 'yyyy-MM-ddTHH:mm:ss'}}</td>
<td>{{user.timestamp_last_password_change| date : 'yyyy-MM-ddTHH:mm:ss'}}</td>
+ <td><ng-container *ngIf="!user.permanent"><a [routerLink]="['..','edit', user.user_id]" [queryParams]="{editmode:true}" ><span class="fas fa-edit"></span></a>
+ <a *ngIf="!user.permanent" [routerLink]="['..','delete',user.user_id]"><span class="fas fa-user-minus"></span></a>
+ </ng-container>
+ </td>
</tr>
</tbody>
</table>
return this.http.head<R>(httpArgs.url, httpArgs.options);
} else if (lType == "post") {
return this.http.post<R>(httpArgs.url, input, httpArgs.options);
+ } else if (lType == "delete") {
+ return this.http.delete<R>(httpArgs.url, httpArgs.options);
+ } else if (lType == "put") {
+ return this.http.put<R>(httpArgs.url, input, httpArgs.options);
}
}
return this.http.head<HttpResponse<R>>(httpArgs.url, httpArgs.options);
} else if (lType == 'post') {
return this.http.post<HttpResponse<R>>(httpArgs.url, input, httpArgs.options);
+ } else if (lType=='delete') {
+ return this.http.delete<HttpResponse<R>>(httpArgs.url, httpArgs.options);
+ } else if (lType=='put') {
+ return this.http.put<HttpResponse<R>>(httpArgs.url, input, httpArgs.options);
}
}
}));
}
+ public deleteUser(user_id:string): Observable<boolean> {
+ return this.rest.executeResponseCall<boolean>("delete", "redback", "users/" + user_id, null).pipe(
+ catchError((error: HttpErrorResponse) => {
+ return throwError(this.rest.getTranslatedErrorResult(error));
+ }),
+ map((response) => {
+ return response.status == 200;
+ }));
+ }
+
public userExists(userid:string): Observable<boolean> {
console.log("Checking user " + userid);
return this.rest.executeResponseCall<string>("head", "redback", "users/" + userid, null).pipe(
"small": {
"password": "If the password field is empty, it will not be updated."
}
+ },
+ "delete": {
+ "modal": {
+ "title": "Delete User",
+ "text": "Are you sure? <br/> Do you want to delete the user <strong>{user_id}</strong>?"
+ }
}
},
"search": {
"required": "Value is empty. This is required.",
"containsWhitespace": "Value must not contain whitespace.",
"userexists": "This user exists already."
+ },
+ "button": {
+ "yes": "Yes",
+ "no": "No"
}
},
"password": {