You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2020/11/12 22:15:34 UTC

[archiva] branch master updated (1caa397 -> ef97e90)

This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git.


    from 1caa397  User form
     new 055964c  Updating user add component
     new 34697b1  Adding user edit component
     new dd42288  Adding user edit component
     new ef97e90  Improving localization. Adding user edit.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/main/archiva-web/package-lock.json         |  46 +++++-
 .../src/main/archiva-web/package.json              |   2 +
 .../main/archiva-web/src/app/app-routing.module.ts |   3 +
 .../src/main/archiva-web/src/app/app.module.ts     |  13 +-
 .../main/archiva-web/src/app/model/error-result.ts |   1 +
 .../main/archiva-web/src/app/model/user-info.ts    |   1 +
 .../app/model/{resource.spec.ts => user.spec.ts}   |   6 +-
 .../src/app/model/{user-info.spec.ts => user.ts}   |   9 +-
 .../user/manage-users/manage-users.component.html  |   3 +
 .../manage-users-add.component.html                | 111 ++++++++-----
 .../manage-users-add/manage-users-add.component.ts | 178 ++++++++++++++++++---
 .../manage-users-edit.component.html               | 142 ++++++++++++++++
 .../manage-users-edit.component.scss}              |   0
 .../manage-users-edit.component.spec.ts}           |  13 +-
 .../manage-users-edit.component.ts                 |  69 ++++++++
 .../manage-users-list.component.html               |   2 +-
 .../src/app/services/archiva-request.service.ts    |  53 +++++-
 .../archiva-web/src/app/services/user.service.ts   | 111 +++++++++----
 .../src/main/archiva-web/src/assets/i18n/en.json   |  40 ++++-
 .../archiva-web/src/environments/environment.ts    |   1 +
 20 files changed, 689 insertions(+), 115 deletions(-)
 copy archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/{resource.spec.ts => user.spec.ts} (88%)
 copy archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/{user-info.spec.ts => user.ts} (87%)
 create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
 copy archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/{repo/upload/upload.component.scss => user/users/manage-users-edit/manage-users-edit.component.scss} (100%)
 copy archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/{manage-users-list/manage-users-list.component.spec.ts => manage-users-edit/manage-users-edit.component.spec.ts} (78%)
 create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts


[archiva] 04/04: Improving localization. Adding user edit.

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit ef97e905a3a1d01a22379ceef9f4399f13852002
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Thu Nov 12 21:39:17 2020 +0100

    Improving localization. Adding user edit.
---
 .../src/main/archiva-web/package-lock.json         |  46 +++++-
 .../src/main/archiva-web/package.json              |   2 +
 .../src/main/archiva-web/src/app/app.module.ts     |  11 +-
 .../main/archiva-web/src/app/model/user-info.ts    |   1 +
 .../manage-users-add.component.html                |  29 +++-
 .../manage-users-add/manage-users-add.component.ts | 158 +++++++++++++++------
 .../manage-users-edit.component.html               | 118 ++++++++++-----
 .../manage-users-edit.component.ts                 |  18 ++-
 .../archiva-web/src/app/services/user.service.ts   |  35 ++++-
 .../src/main/archiva-web/src/assets/i18n/en.json   |  28 +++-
 10 files changed, 345 insertions(+), 101 deletions(-)

diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package-lock.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package-lock.json
index e3aaebc..f3e9dff 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package-lock.json
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package-lock.json
@@ -7758,6 +7758,14 @@
         }
       }
     },
+    "make-plural": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz",
+      "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==",
+      "requires": {
+        "minimist": "^1.2.0"
+      }
+    },
     "map-cache": {
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -7841,6 +7849,26 @@
       "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
       "dev": true
     },
+    "messageformat": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz",
+      "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==",
+      "requires": {
+        "make-plural": "^4.3.0",
+        "messageformat-formatters": "^2.0.1",
+        "messageformat-parser": "^4.1.2"
+      }
+    },
+    "messageformat-formatters": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz",
+      "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg=="
+    },
+    "messageformat-parser": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz",
+      "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg=="
+    },
     "methods": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@@ -7983,8 +8011,7 @@
     "minimist": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
     },
     "minipass": {
       "version": "3.1.3",
@@ -8177,6 +8204,21 @@
       "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
       "dev": true
     },
+    "ngx-translate-messageformat-compiler": {
+      "version": "4.8.0",
+      "resolved": "https://registry.npmjs.org/ngx-translate-messageformat-compiler/-/ngx-translate-messageformat-compiler-4.8.0.tgz",
+      "integrity": "sha512-A1Zg2sC0uCc1r8siT1M2DFcLhgjX6aEIu2g5NGnPh51KGtGqQqXHiXx2qCxz1U9sKMlYrvCZzfxzJ2kaCTtw+A==",
+      "requires": {
+        "tslib": "^1.10.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+        }
+      }
+    },
     "nice-try": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package.json
index 113e3a1..a0935ae 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package.json
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package.json
@@ -27,6 +27,8 @@
     "bootstrap": "^4.5.0",
     "flag-icon-css": "^3.5.0",
     "jquery": "^3.5.1",
+    "messageformat": "^2.3.0",
+    "ngx-translate-messageformat-compiler": "^4.8.0",
     "popper.js": "^1.16.1",
     "rxjs": "~6.6.3",
     "service": "^0.1.4",
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
index 6b7d2c4..bd46271 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
@@ -19,8 +19,9 @@
 import { BrowserModule } from '@angular/platform-browser';
 import { NgModule } from '@angular/core';
 import { HttpClient, HttpClientModule } from '@angular/common/http';
-import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { TranslateLoader, TranslateModule, TranslateCompiler } from '@ngx-translate/core';
 import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler';
 
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
@@ -79,6 +80,10 @@ import { ManageUsersEditComponent } from './modules/user/users/manage-users-edit
     ReactiveFormsModule,
     HttpClientModule,
     TranslateModule.forRoot({
+      compiler: {
+        provide: TranslateCompiler,
+        useClass: TranslateMessageFormatCompiler
+      },
       loader: {
         provide: TranslateLoader,
         useFactory: httpTranslateLoader,
@@ -88,7 +93,9 @@ import { ManageUsersEditComponent } from './modules/user/users/manage-users-edit
       NgbPaginationModule,
       NgbTooltipModule
   ],
-  providers: [],
+  providers: [
+    { provide: MESSAGE_FORMAT_CONFIG, useValue: { locales: ['en', 'de'] }}
+  ],
   bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts
index 653a491..0f4a476 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user-info.ts
@@ -32,4 +32,5 @@ export class UserInfo {
     user_manager_id:string;
     validation_token:string;
     language:string;
+    location;
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
index ad538a8..13b764c 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
@@ -24,6 +24,18 @@
                [ngClass]="valid('user_id')"
                placeholder="{{'users.input.user_id'|translate}}">
         <small>{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+        <div *ngIf="userForm.get('user_id').invalid" class="invalid-feedback">
+            <div *ngIf="userForm.get('user_id').errors.required">
+                {{'form.error.required'|translate}}
+            </div>
+            <div *ngIf="userForm.get('user_id').errors.containsWhitespace">
+                {{'form.error.containsWhitespace'|translate}}
+            </div>
+            <div *ngIf="userForm.get('user_id').errors.userexists">
+                {{'form.error.userexists'|translate}}
+            </div>
+        </div>
+
     </div>
     <div class="form-group col-md-8">
         <label for="full_name">{{'users.attributes.full_name' |translate}}</label>
@@ -52,28 +64,35 @@
     </div>
     <div class="form-group col-md-8">
         <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="" formControlName="locked" id="locked">
+            <input class="form-check-input" type="checkbox" formControlName="locked" id="locked">
             <label class="form-check-label" for="locked">
                 {{'users.attributes.locked'|translate}}
             </label>
         </div>
         <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="" formControlName="password_change_required"
+            <input class="form-check-input" type="checkbox" formControlName="password_change_required"
                    id="password_change_required" checked>
             <label class="form-check-label" for="password_change_required">
                 {{'users.attributes.password_change_required'|translate}}
             </label>
         </div>
+        <div class="form-check">
+            <input class="form-check-input" type="checkbox" formControlName="validated"
+                   id="validated" checked>
+            <label class="form-check-label" for="validated">
+                {{'users.attributes.validated'|translate}}
+            </label>
+        </div>
     </div>
     <div class="form-group col-md-8">
         <button class="btn btn-primary" type="submit"
-                [disabled]="!userForm.valid">{{'users.add.submit'|translate}}</button>
+                [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',userid]">{{userid}}</a> was added to the list.
+        User <a [routerLink]="['user','users','edit',result?.user_id]">{{result?.userid}}</a> was added to the list.
     </div>
     <div *ngIf="error" class="alert alert-danger" role="alert" >
-        <h4 class="alert-heading">Errors</h4>
+        <h4 class="alert-heading">{{'users.add.errortitle'|translate}}</h4>
         <ng-container *ngFor="let message of errorResult?.error_messages; first as isFirst" >
             <hr *ngIf="!isFirst">
             <p>{{message.message}}</p>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
index 60649fc..f148d18 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
@@ -18,15 +18,23 @@
  */
 
 import {Component, OnInit} from '@angular/core';
-import {Validators, FormBuilder, FormGroup} from '@angular/forms';
+import {
+    FormBuilder,
+    FormGroup,
+    Validators,
+    FormControl,
+    AsyncValidator,
+    AbstractControl,
+    ValidationErrors,
+    ValidatorFn
+} from '@angular/forms';
 import {UserService} from "../../../../services/user.service";
 import {User} from "../../../../model/user";
-import { UserInfo } from 'src/app/model/user-info';
-import {HttpErrorResponse} from "@angular/common/http";
 import {ErrorResult} from "../../../../model/error-result";
-import {catchError} from "rxjs/operators";
-import {of, throwError} from 'rxjs';
+import {catchError, debounceTime, distinctUntilChanged, map, switchMap} from "rxjs/operators";
+import {throwError, Observable, of, pipe, timer} from 'rxjs';
 import {environment} from "../../../../../environments/environment";
+import {UserInfo} from "../../../../model/user-info";
 
 @Component({
     selector: 'app-manage-users-add',
@@ -35,21 +43,24 @@ import {environment} from "../../../../../environments/environment";
 })
 export class ManageUsersAddComponent implements OnInit {
 
-    minUserIdSize=environment.application.minUserIdLength;
-    success:boolean=false;
-    error:boolean=false;
-    errorResult:ErrorResult;
-    result:string;
-    userid:string;
+    editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required',
+        'password', 'confirm_password', 'validated'];
+    minUserIdSize = environment.application.minUserIdLength;
+    success: boolean = false;
+    error: boolean = false;
+    errorResult: ErrorResult;
+    result: UserInfo;
+    user: string;
 
     userForm = this.fb.group({
-        user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize)]],
+        user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize), whitespaceValidator()],this.userUidExistsValidator()],
         full_name: ['', Validators.required],
-        email: ['', [Validators.required,Validators.email]],
+        email: ['', [Validators.required, Validators.email]],
         locked: [false],
         password_change_required: [true],
         password: [''],
         confirm_password: [''],
+        validated: [true]
     }, {
         validator: MustMatch('password', 'confirm_password')
     })
@@ -63,33 +74,39 @@ export class ManageUsersAddComponent implements OnInit {
 
     onSubmit() {
         // Process checkout data here
-        this.result=null;
+        this.result = null;
         if (this.userForm.valid) {
-            let user = this.copyForm(['user_id','full_name','email','locked','password_change_required',
-            'password','confirm_password'])
+            let user = this.copyFromForm(this.editProperties);
             console.info('Adding user ' + user);
-            this.userService.addUser(user).pipe(catchError((error : ErrorResult)=> {
-                console.log("Error " + error + " - " + typeof (error) + " - " + JSON.stringify(error));
-                if (error.status==422) {
-                    console.warn("Validation error");
+            this.userService.addUser(user).pipe(catchError((error: ErrorResult) => {
+                // console.log("Error " + error + " - " + typeof (error) + " - " + JSON.stringify(error));
+                if (error.status == 422) {
+                    // console.warn("Validation error");
+                    let pwdErrors = {};
+                    for (let message of error.error_messages) {
+                        if (message.error_key.startsWith('user.password.violation')) {
+                            pwdErrors[message.error_key] = message.message;
+                        }
+                    }
+                    this.userForm.get('password').setErrors(pwdErrors);
 
                 }
                 this.errorResult = error;
-                this.success=false;
-                this.error=true;
-                return throwError(error);
-            })).subscribe((location : string ) => {
-                this.result = location;
-                this.success=true;
+                this.success = false;
+                this.error = true;
+                return [];
+                // return throwError(error);
+            })).subscribe((user: UserInfo) => {
+                this.result = user;
+                this.success = true;
                 this.error = false;
-                this.userid = location.substring(location.lastIndexOf('/') + 1);
             });
         }
     }
 
 
-    private copyForm(properties:string[]) : User {
-        let user : any  = new User();
+    public copyFromForm(properties: string[]): User {
+        let user: any = new User();
         for (let prop of properties) {
             user[prop] = this.userForm.get(prop).value;
         }
@@ -97,27 +114,83 @@ export class ManageUsersAddComponent implements OnInit {
         return user;
     }
 
+    public copyToForm(properties: string[], user: User): void {
+        let propMap = {};
+        for (let prop of properties) {
+            let propValue = user[prop] == null ? '' : user[prop];
+            propMap[prop] = propValue;
+        }
+        this.userForm.patchValue(propMap);
+        console.log("User " + user);
+    }
 
-    valid(field:string) : string[] {
-      let formField = this.userForm.get(field);
-      if (formField.dirty||formField.touched) {
-        if (formField.valid) {
-          return ['is-valid']
+
+    valid(field: string): string[] {
+        let formField = this.userForm.get(field);
+        if (formField.dirty || formField.touched) {
+            if (formField.valid) {
+                return ['is-valid']
+            } else {
+                return ['is-invalid']
+            }
         } else {
-          return ['is-invalid']
+            return ['']
+        }
+    }
+
+    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);
+    }
+
+    /**
+     * Async validator with debounce time
+     * @constructor
+     */
+    userUidExistsValidator() {
+
+        return (ctrl : FormControl) => {
+            // debounceTimer() does not work here, as the observable is created with each keystroke
+            // but angular does unsubscribe on previous started async observables.
+            return timer(500).pipe(
+                switchMap((userid) => this.userService.userExists(ctrl.value)),
+                catchError(() => of(null)),
+                map(exists => (exists ? {userexists: true} : null))
+            );
         }
-      } else {
-        return ['']
-      }
     }
 
+    forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
+        return (control: AbstractControl): {[key: string]: any} | null => {
+            const forbidden = nameRe.test(control.value);
+            return forbidden ? {forbiddenName: {value: control.value}} : null;
+        };
+    }
 
 
 
 }
 
-export function MustMatch(controlName: string, matchingControlName: string) {
-    return (formGroup: FormGroup) => {
+export function whitespaceValidator(): ValidatorFn {
+    return (control: AbstractControl): ValidationErrors | null => {
+        const hasWhitespace =  /\s/g.test(control.value);
+        return hasWhitespace ? {containsWhitespace: {value: control.value}} : null;
+    };
+}
+export function MustMatch(controlName: string, matchingControlName: string) : ValidatorFn  {
+    return (formGroup: FormGroup): ValidationErrors | null => {
         const control = formGroup.controls[controlName];
         const matchingControl = formGroup.controls[matchingControlName];
 
@@ -128,9 +201,10 @@ export function MustMatch(controlName: string, matchingControlName: string) {
 
         // set error on matchingControl if validation fails
         if (control.value !== matchingControl.value) {
-            matchingControl.setErrors({ mustMatch: true });
+            matchingControl.setErrors({mustMatch: true});
         } else {
             matchingControl.setErrors(null);
         }
     }
-}
\ No newline at end of file
+}
+
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
index f929de2..8fa520d 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
@@ -17,86 +17,126 @@
   -->
 
 <form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
-    <div class="form-group row col-md-8">
-        <div class="col-md-1">Edit  <span class="fas fa-edit"></span></div>
+    <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"
-                                     (change)="editMode=!editMode"
-                                     >
+            <input class="form-check-input" type="checkbox" [value]="editMode"
+                   (change)="editMode=!editMode"
+            >
         </div>
     </div>
     <div class="form-group row col-md-8">
         <label class="col-md-2 col-form-label" for="user_id">{{'users.attributes.user_id' |translate}}</label>
-        <div class="col-md-6" >
-        <input type="text" formControlName="user_id" id="user_id"
-               [ngClass]="valid('user_id')"
-               value="{{editUser.user_id}}" [attr.readonly]="!editMode">
-        <small *ngIf="editMode">{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+        <div class="col-md-6">
+            <input type="text" formControlName="user_id" id="user_id"
+                   [ngClass]="valid('user_id')"
+                   [attr.readonly]="true">
         </div>
     </div>
     <div class="form-group row col-md-8">
         <label class="col-md-2 col-form-label" for="full_name">{{'users.attributes.full_name' |translate}}</label>
-        <div class="col-md-6" >
-        <input type="text" formControlName="full_name" id="full_name"
-               [ngClass]="valid('full_name')" value="{{editUser.full_name}}" [attr.readonly]="!editMode" >
-        <small *ngIf="editMode">{{'users.input.small.full_name'|translate}}</small>
+        <div class="col-md-6">
+            <input type="text" formControlName="full_name" id="full_name"
+                   [ngClass]="valid('full_name')" [attr.readonly]="editMode?null:true">
+            <small *ngIf="editMode">{{'users.input.small.full_name'|translate}}</small>
         </div>
     </div>
     <div class="form-group row col-md-8">
         <label class="col-md-2 col-form-label" for="email">{{'users.attributes.email' |translate}}</label>
-        <div class="col-md-6" >
-        <input type="text" formControlName="email" id="email"
-               [ngClass]="valid('email')" value="{{editUser.email}}" [attr.readonly]="!editMode">
+        <div class="col-md-6">
+            <input type="text" formControlName="email" id="email"
+                   [ngClass]="valid('email')" value="{{editUser.email}}" [attr.readonly]="editMode?null:true">
         </div>
     </div>
     <div class="form-group row col-md-8" *ngIf="editMode">
         <label class="col-md-2 col-form-label" for="password">{{'users.attributes.password' |translate}}</label>
-        <div class="col-md-6" >
-        <input type="password" class="form-control" formControlName="password" id="password"
-               [ngClass]="valid('password')"
-               placeholder="{{'users.input.password'|translate}}">
+        <div class="col-md-6">
+            <input type="password" class="form-control" formControlName="password" id="password"
+                   [ngClass]="valid('password')"
+                   placeholder="{{'users.input.password'|translate}}">
+            <small>{{'users.edit.small.password'|translate}}</small>
         </div>
     </div>
     <div class="form-group row col-md-8" *ngIf="editMode">
-        <label class="col-md-2 col-form-label" for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
+        <label class="col-md-2 col-form-label"
+               for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
         <div class="col-md-6">
-        <input type="password" class="form-control" formControlName="confirm_password" id="confirm_password"
-               [ngClass]="valid('confirm_password')"
-               placeholder="{{'users.input.confirm_password'|translate}}">
+            <input type="password" class="form-control" formControlName="confirm_password" id="confirm_password"
+                   [ngClass]="valid('confirm_password')"
+                   placeholder="{{'users.input.confirm_password'|translate}}">
         </div>
     </div>
     <div class="form-group row col-md-8">
         <div class="col-md-2">Flags</div>
         <div class="col-md-6">
-        <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="{{editUser.locked}}" formControlName="locked" id="locked" [attr.disabled]="editMode?null:true">
-            <label class="form-check-label" for="locked" >
-                {{'users.attributes.locked'|translate}}
-            </label>
+            <div class="form-check">
+                <input class="form-check-input" type="checkbox" formControlName="locked"
+                       id="locked" [attr.disabled]="editMode?null:true">
+                <label class="form-check-label " for="locked">
+                    {{'users.attributes.locked'|translate}}
+                </label>
+            </div>
+            <div class="form-check" >
+                <input class="form-check-input" type="checkbox"
+                       formControlName="password_change_required"
+                       id="password_change_required" [attr.disabled]="editMode?null:true">
+                <label class="form-check-label" for="password_change_required" >
+                    {{'users.attributes.password_change_required'|translate}}
+                </label>
+            </div>
+            <div class="form-check">
+                <input class="form-check-input" type="checkbox"
+                        formControlName="validated"
+                       id="validated" [attr.disabled]="editMode?null:true">
+                <label class="form-check-label" for="validated">
+                    {{'users.attributes.validated'|translate}}
+                </label>
+            </div>
         </div>
-        <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="{{editUser.password_change_required}}" formControlName="password_change_required"
-                   id="password_change_required" [attr.disabled]="editMode?null:true">
-            <label class="form-check-label" for="password_change_required">
-                {{'users.attributes.password_change_required'|translate}}
-            </label>
+    </div>
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="created">{{'users.attributes.created' |translate}}</label>
+        <div class="col-md-6">
+            <input type="text"  id="created" class="form-control-plaintext"
+                   value="{{editUser.timestamp_account_creation|date:'yyyy-MM-ddTHH:mm:ss'}}" [attr.readonly]="true">
+        </div>
+    </div>
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="last_login">{{'users.attributes.last_login' |translate}}</label>
+        <div class="col-md-6">
+            <input type="text" id="last_login" class="form-control-plaintext"
+                   value="{{editUser.timestamp_last_login|date:'yyyy-MM-ddTHH:mm:ss'}}" [attr.readonly]="true">
         </div>
+    </div>
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="email">{{'users.attributes.last_password_change' |translate}}</label>
+        <div class="col-md-6">
+            <input type="text" id="last_password_change" class="form-control-plaintext"
+                   value="{{editUser.timestamp_last_password_change|date:'yyyy-MM-ddTHH:mm:ss'}}" [attr.readonly]="true">
         </div>
     </div>
+
     <div class="form-group col-md-8" *ngIf="editMode">
         <button class="btn btn-primary" type="submit"
-                [disabled]="!userForm.valid">{{'users.edit.submit'|translate}}</button>
+                [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.
     </div>
-    <div *ngIf="error" class="alert alert-danger" role="alert" >
+    <div *ngIf="editMode && 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" >
+        <ng-container *ngFor="let message of errorResult?.error_messages; first as isFirst">
             <hr *ngIf="!isFirst">
             <p>{{message.message}}</p>
         </ng-container>
     </div>
+    <div *ngIf="editMode && userForm.invalid" class="alert alert-danger" role="alert" >
+        <h4 class="alert-heading">Errors</h4>
+        <ng-container *ngFor="let message of getAllErrors(userForm); first as isFirst" >
+            <hr *ngIf="!isFirst">
+            <p>{{message}}</p>
+        </ng-container>
+    </div>
 
 
 </form>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
index adb5b91..7a01f25 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
@@ -19,7 +19,7 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import {UserService} from "../../../../services/user.service";
-import {FormBuilder, Validators} from "@angular/forms";
+import {FormBuilder, FormControl, Validators} from "@angular/forms";
 import {ManageUsersAddComponent, MustMatch} from "../manage-users-add/manage-users-add.component";
 import {environment} from "../../../../../environments/environment";
 import {map, switchMap} from 'rxjs/operators';
@@ -31,17 +31,23 @@ import {map, switchMap} from 'rxjs/operators';
 })
 export class ManageUsersEditComponent extends ManageUsersAddComponent implements OnInit {
 
+  editProperties = ['user_id', 'full_name', 'email', 'locked', 'password_change_required',
+    'password', 'confirm_password', 'validated'];
   editUser;
+  originUser;
   editMode:boolean=false;
+  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.editUser = user;
+      this.originUser = user;
+      this.copyToForm(this.editProperties, this.editUser);});
   }
 
   ngOnInit(): void {
-
+    this.userForm.setControl('user_id', new FormControl());
   }
 
   valid(field: string): string[] {
@@ -54,4 +60,10 @@ export class ManageUsersEditComponent extends ManageUsersAddComponent implements
   }
 
 
+  onSubmit() {
+    this.copyFromForm(this.editProperties)
+
+  }
+
+
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
index 5e0f172..5205bfc 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
@@ -278,11 +278,19 @@ export class UserService implements OnInit, OnDestroy {
     }
 
 
-    public addUser(user: User): Observable<string> {
-        return this.rest.executeResponseCall<string>("post", "redback", "users", user).pipe(
+    public addUser(user: User): Observable<UserInfo> {
+        return this.rest.executeResponseCall<UserInfo>("post", "redback", "users", user).pipe(
             catchError((error: HttpErrorResponse) => {
                 return throwError(this.rest.getTranslatedErrorResult(error));
-            }), map((httpResponse: HttpResponse<string>) => httpResponse.headers.get('Location')));
+            }), map((httpResponse: HttpResponse<UserInfo>) => {
+                if (httpResponse.status==201) {
+                    let user = httpResponse.body;
+                    user.location = httpResponse.headers.get('Location');
+                    return user;
+                } else {
+                    throwError(new HttpErrorResponse({headers:httpResponse.headers,status:httpResponse.status,statusText:"Bad response code"}))
+                }
+            }));
     }
 
     public getUser(userid: string): Observable<UserInfo> {
@@ -291,4 +299,25 @@ export class UserService implements OnInit, OnDestroy {
                 return throwError(this.rest.getTranslatedErrorResult(error));
             }));
     }
+
+    public updateUser(user:User): Observable<UserInfo> {
+        return this.rest.executeRestCall<UserInfo>("put", "redback", "users/" + user.user_id, user).pipe(
+            catchError((error: HttpErrorResponse) => {
+                return throwError(this.rest.getTranslatedErrorResult(error));
+            }));
+    }
+
+    public userExists(userid:string): Observable<boolean> {
+        console.log("Checking user " + userid);
+        return this.rest.executeResponseCall<string>("head", "redback", "users/" + userid, null).pipe(
+            catchError((error: HttpErrorResponse) => {
+                if (error.status==404) {
+                    console.log("Status 404")
+                    return [false];
+                } else {
+                    return throwError(this.rest.getTranslatedErrorResult(error));
+                }
+            }), map((httpResponse: HttpResponse<string>) => httpResponse.status == 200));
+    }
+
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
index 05843c4..aaffa09 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
@@ -48,7 +48,12 @@
   },
   "api" : {
     "rb.auth.invalid_credentials": "Invalid credentials given",
-    "user.password.violation.numeric" : "Password must have at least {{arg0}} numeric characters."
+    "user.password.violation.length": "You must provide a password between {arg0} and {arg1} characters in length.",
+    "user.password.violation.alpha": "You must provide a password containing at least {arg0} alphabetic {arg0, plural, one {character} other {characters}}.",
+    "user.password.violation.numeric":"You must provide a password containing at least {arg0} numeric {arg0, plural, one {character} other {characters}}.",
+    "user.password.violation.reuse":"The password must not match any of the previous {arg0} {arg0, plural, one {password} other {passwords}}.",
+    "user.password.violation.alphanum.only":"You must provide a password containing all alpha-numeric characters.",
+    "user.password.violation.whitespace.detected":"You must provide a password without whitespace characters."
   },
   "users": {
     "attributes":{
@@ -64,11 +69,11 @@
         "permanent": "Permanent",
         "last_password_change": "Last Password Change",
         "password": "Password",
-      "confirm_password": "Confirm Password"
+        "confirm_password": "Confirm Password"
     },
     "input" : {
       "small": {
-        "user_id": "Must be a unique key with at least {{minSize}} characters. No spaces allowed.",
+        "user_id": "Must be a unique key with at least {minSize} characters. No spaces allowed.",
         "full_name": "This is the display name of the user"
       },
       "user_id": "Enter user ID",
@@ -83,7 +88,15 @@
     },
     "add": {
       "head": "Add User",
-      "submit": "Add User"
+      "submit": "Add User",
+      "errortitle": "Could not add the user. Please check the following error messages."
+    },
+    "edit": {
+      "submit": "Save Changes",
+      "head": "View/Edit User",
+      "small": {
+        "password": "If the password field is empty, it will not be updated."
+      }
     }
   },
   "search": {
@@ -92,7 +105,12 @@
     "input": "Search"
   },
   "form": {
-    "submit": "Submit"
+    "submit": "Submit",
+    "error": {
+      "required": "Value is empty. This is required.",
+      "containsWhitespace": "Value must not contain whitespace.",
+      "userexists": "This user exists already."
+    }
   },
   "password": {
     "violations" : {


[archiva] 02/04: Adding user edit component

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit 34697b1a558b90fb30e13d9a4197c3e61f2d3f52
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Nov 11 21:01:58 2020 +0100

    Adding user edit component
---
 .../main/archiva-web/src/app/app-routing.module.ts |  2 +
 .../src/main/archiva-web/src/app/app.module.ts     |  2 +
 .../manage-users-edit.component.html               | 83 ++++++++++++++++++++++
 .../manage-users-edit.component.scss               | 18 +++++
 .../manage-users-edit.component.spec.ts            | 43 +++++++++++
 .../manage-users-edit.component.ts                 | 38 ++++++++++
 6 files changed, 186 insertions(+)

diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
index 9f1249e..eed0365 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
@@ -32,6 +32,7 @@ import {ManageRolesComponent} from "./modules/user/manage-roles/manage-roles.com
 import {SecurityConfigurationComponent} from "./modules/user/security-configuration/security-configuration.component";
 import {ManageUsersListComponent} from "./modules/user/users/manage-users-list/manage-users-list.component";
 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";
 
 const routes: Routes = [
   { path: '', component: HomeComponent,
@@ -47,6 +48,7 @@ const routes: Routes = [
         children: [
           {path: 'list', component: ManageUsersListComponent},
           {path: 'add', component: ManageUsersAddComponent},
+          {path: 'edit/:userid', component: ManageUsersEditComponent},
           {path: '', redirectTo:'list', pathMatch:'full'}
         ]
       },
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
index f3ddcc7..6b7d2c4 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app.module.ts
@@ -45,6 +45,7 @@ import { NgbPaginationModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap
 import { PaginatedEntitiesComponent } from './modules/general/paginated-entities/paginated-entities.component';
 import { SortedTableHeaderComponent } from './modules/general/sorted-table-header/sorted-table-header.component';
 import { SortedTableHeaderRowComponent } from './modules/general/sorted-table-header-row/sorted-table-header-row.component';
+import { ManageUsersEditComponent } from './modules/user/users/manage-users-edit/manage-users-edit.component';
 
 
 @NgModule({
@@ -69,6 +70,7 @@ import { SortedTableHeaderRowComponent } from './modules/general/sorted-table-he
     PaginatedEntitiesComponent,
     SortedTableHeaderComponent,
     SortedTableHeaderRowComponent,
+    ManageUsersEditComponent,
   ],
   imports: [
     BrowserModule,
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
new file mode 100644
index 0000000..c70355a
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
@@ -0,0 +1,83 @@
+<!--
+  ~ 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.
+  -->
+
+<form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
+    <div class="form-group col-md-8">
+        <label for="user_id">{{'users.attributes.user_id' |translate}}</label>
+        <input type="text" class="form-control" formControlName="user_id" id="user_id"
+               [ngClass]="valid('user_id')"
+               placeholder="{{'users.input.user_id'|translate}}">
+        <small>{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+    </div>
+    <div class="form-group col-md-8">
+        <label for="full_name">{{'users.attributes.full_name' |translate}}</label>
+        <input type="text" class="form-control" formControlName="full_name" id="full_name"
+               [ngClass]="valid('full_name')"
+               placeholder="{{'users.input.full_name'|translate}}">
+        <small>{{'users.input.small.full_name'|translate}}</small>
+    </div>
+    <div class="form-group col-md-8">
+        <label for="email">{{'users.attributes.email' |translate}}</label>
+        <input type="text" class="form-control" formControlName="email" id="email"
+               [ngClass]="valid('email')"
+               placeholder="{{'users.input.email'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <label for="password">{{'users.attributes.password' |translate}}</label>
+        <input type="password" class="form-control" formControlName="password" id="password"
+               [ngClass]="valid('password')"
+               placeholder="{{'users.input.password'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <label for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
+        <input type="password" class="form-control" formControlName="confirm_password" id="confirm_password"
+               [ngClass]="valid('confirm_password')"
+               placeholder="{{'users.input.confirm_password'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <div class="form-check">
+            <input class="form-check-input" type="checkbox" value="" formControlName="locked" id="locked">
+            <label class="form-check-label" for="locked">
+                {{'users.attributes.locked'|translate}}
+            </label>
+        </div>
+        <div class="form-check">
+            <input class="form-check-input" type="checkbox" value="" formControlName="password_change_required"
+                   id="password_change_required" checked>
+            <label class="form-check-label" for="password_change_required">
+                {{'users.attributes.password_change_required'|translate}}
+            </label>
+        </div>
+    </div>
+    <div class="form-group col-md-8">
+        <button class="btn btn-primary" type="submit"
+                [disabled]="!userForm.valid">{{'users.add.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.
+    </div>
+    <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">
+            <p>{{message.message}}</p>
+        </ng-container>
+    </div>
+
+
+</form>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.scss b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.scss
new file mode 100644
index 0000000..343c3b1
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.scss
@@ -0,0 +1,18 @@
+/*!
+ * 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.
+ */
+
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.spec.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.spec.ts
new file mode 100644
index 0000000..f21a49f
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { ManageUsersEditComponent } from './manage-users-edit.component';
+
+describe('ManageUsersEditComponent', () => {
+  let component: ManageUsersEditComponent;
+  let fixture: ComponentFixture<ManageUsersEditComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ManageUsersEditComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ManageUsersEditComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
new file mode 100644
index 0000000..5d55089
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
@@ -0,0 +1,38 @@
+/*
+ * 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 { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+@Component({
+  selector: 'app-manage-users-edit',
+  templateUrl: './manage-users-edit.component.html',
+  styleUrls: ['./manage-users-edit.component.scss']
+})
+export class ManageUsersEditComponent implements OnInit {
+
+  userid;
+
+  constructor(private route: ActivatedRoute) {
+    this.route.params.subscribe(params => this.userid=params.userid);
+  }
+
+  ngOnInit(): void {
+  }
+
+}


[archiva] 01/04: Updating user add component

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit 055964cce1d8c3b272227520836cd494048ab3e4
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Nov 11 21:01:46 2020 +0100

    Updating user add component
---
 .../main/archiva-web/src/app/model/error-result.ts |  1 +
 .../app/model/{error-result.ts => user.spec.ts}    | 14 ++--
 .../src/app/model/{error-result.ts => user.ts}     | 11 +--
 .../user/manage-users/manage-users.component.html  |  3 +
 .../manage-users-add.component.html                | 94 +++++++++++++---------
 .../manage-users-add/manage-users-add.component.ts | 81 +++++++++++++++++--
 .../manage-users-list.component.html               |  2 +-
 .../src/app/services/archiva-request.service.ts    | 53 ++++++++++--
 .../archiva-web/src/app/services/user.service.ts   | 13 ++-
 .../src/main/archiva-web/src/assets/i18n/en.json   | 18 ++++-
 10 files changed, 219 insertions(+), 71 deletions(-)

diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
index 64e9765..b54fa84 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
@@ -20,6 +20,7 @@ import {ErrorMessage} from "./error-message";
 
 export class ErrorResult {
     error_messages: Array<ErrorMessage>
+    status: number;
 
     constructor(errorMessages: Array<ErrorMessage>) {
         this.error_messages = errorMessages;
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts
similarity index 78%
copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts
index 64e9765..40e1437 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.spec.ts
@@ -16,12 +16,10 @@
  * under the License.
  */
 
-import {ErrorMessage} from "./error-message";
+import { User } from './user';
 
-export class ErrorResult {
-    error_messages: Array<ErrorMessage>
-
-    constructor(errorMessages: Array<ErrorMessage>) {
-        this.error_messages = errorMessages;
-    }
-}
+describe('User', () => {
+  it('should create an instance', () => {
+    expect(new User()).toBeTruthy();
+  });
+});
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts
similarity index 78%
copy from archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
copy to archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts
index 64e9765..f264d02 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/error-result.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/user.ts
@@ -16,12 +16,9 @@
  * under the License.
  */
 
-import {ErrorMessage} from "./error-message";
+import { UserInfo } from './user-info';
 
-export class ErrorResult {
-    error_messages: Array<ErrorMessage>
-
-    constructor(errorMessages: Array<ErrorMessage>) {
-        this.error_messages = errorMessages;
-    }
+export class User extends UserInfo {
+    password: string;
+    confirm_password: string;
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
index c617180..08b3fca 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
@@ -24,6 +24,9 @@
     <li class="nav-item">
         <a class="nav-link" routerLink="/user/users/add" routerLinkActive="active" href="#">{{'users.add.head' |translate }}</a>
     </li>
+    <li class="nav-item">
+        <a class="nav-link" routerLink="/user/users/edit/guest" routerLinkActive="active" href="#">{{'users.edit.head' |translate }}</a>
+    </li>
 </ul>
 
 <router-outlet ></router-outlet>
\ No newline at end of file
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
index 1bfac66..ad538a8 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.html
@@ -18,47 +18,67 @@
   -->
 
 <form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
-    <fieldset>
-        <div class="form-group col-md-8">
-            <label for="userId">{{'users.attributes.user_id' |translate}}</label>
-            <input type="text" class="form-control" formControlName="userId" id="userId"
-                   [ngClass]="valid('userId')"
-                   placeholder="{{'users.input.user_id'|translate}}">
-            <small>{{'users.input.small.user_id'|translate}}</small>
+    <div class="form-group col-md-8">
+        <label for="user_id">{{'users.attributes.user_id' |translate}}</label>
+        <input type="text" class="form-control" formControlName="user_id" id="user_id"
+               [ngClass]="valid('user_id')"
+               placeholder="{{'users.input.user_id'|translate}}">
+        <small>{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+    </div>
+    <div class="form-group col-md-8">
+        <label for="full_name">{{'users.attributes.full_name' |translate}}</label>
+        <input type="text" class="form-control" formControlName="full_name" id="full_name"
+               [ngClass]="valid('full_name')"
+               placeholder="{{'users.input.full_name'|translate}}">
+        <small>{{'users.input.small.full_name'|translate}}</small>
+    </div>
+    <div class="form-group col-md-8">
+        <label for="email">{{'users.attributes.email' |translate}}</label>
+        <input type="text" class="form-control" formControlName="email" id="email"
+               [ngClass]="valid('email')"
+               placeholder="{{'users.input.email'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <label for="password">{{'users.attributes.password' |translate}}</label>
+        <input type="password" class="form-control" formControlName="password" id="password"
+               [ngClass]="valid('password')"
+               placeholder="{{'users.input.password'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <label for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
+        <input type="password" class="form-control" formControlName="confirm_password" id="confirm_password"
+               [ngClass]="valid('confirm_password')"
+               placeholder="{{'users.input.confirm_password'|translate}}">
+    </div>
+    <div class="form-group col-md-8">
+        <div class="form-check">
+            <input class="form-check-input" type="checkbox" value="" formControlName="locked" id="locked">
+            <label class="form-check-label" for="locked">
+                {{'users.attributes.locked'|translate}}
+            </label>
         </div>
-        <div class="form-group col-md-8">
-            <label for="fullName">{{'users.attributes.full_name' |translate}}</label>
-            <input type="text" class="form-control" formControlName="fullName" id="fullName"
-                   [ngClass]="valid('fullName')"
-                   placeholder="{{'users.input.full_name'|translate}}">
-            <small>{{'users.input.small.full_name'|translate}}</small>
+        <div class="form-check">
+            <input class="form-check-input" type="checkbox" value="" formControlName="password_change_required"
+                   id="password_change_required" checked>
+            <label class="form-check-label" for="password_change_required">
+                {{'users.attributes.password_change_required'|translate}}
+            </label>
         </div>
-        <div class="form-group col-md-8">
-            <label for="email">{{'users.attributes.email' |translate}}</label>
-            <input type="text" class="form-control" formControlName="email" id="email"
-                   [ngClass]="valid('email')"
-                   placeholder="{{'users.input.email'|translate}}">
-        </div>
-        <div class="form-group col-md-8">
-            <div class="form-check">
-                <input class="form-check-input" type="checkbox" value="" formControlName="locked" id="locked">
-                <label class="form-check-label" for="locked">
-                    {{'users.attributes.locked'|translate}}
-                </label>
-            </div>
-            <div class="form-check">
-                <input class="form-check-input" type="checkbox" value="" formControlName="passwordChangeRequired"
-                       id="password_change_required" checked>
-                <label class="form-check-label" for="password_change_required">
-                    {{'users.attributes.password_change_required'|translate}}
-                </label>
-            </div>
-        </div>
-        <div class="form-group col-md-8">
+    </div>
+    <div class="form-group col-md-8">
         <button class="btn btn-primary" type="submit"
                 [disabled]="!userForm.valid">{{'users.add.submit'|translate}}</button>
-        </div>
-    </fieldset>
+    </div>
+    <div *ngIf="success" class="alert alert-success" role="alert">
+        User <a [routerLink]="['user','users','edit',userid]">{{userid}}</a> was added to the list.
+    </div>
+    <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">
+            <p>{{message.message}}</p>
+        </ng-container>
+    </div>
 
 
 </form>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
index ce95bff..6a50028 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
@@ -18,8 +18,14 @@
  */
 
 import {Component, OnInit} from '@angular/core';
-import {FormControl, FormGroup, Validators, FormBuilder} from '@angular/forms';
+import {Validators, FormBuilder, FormGroup} from '@angular/forms';
 import {UserService} from "../../../../services/user.service";
+import {User} from "../../../../model/user";
+import { UserInfo } from 'src/app/model/user-info';
+import {HttpErrorResponse} from "@angular/common/http";
+import {ErrorResult} from "../../../../model/error-result";
+import {catchError} from "rxjs/operators";
+import {of, throwError} from 'rxjs';
 
 @Component({
     selector: 'app-manage-users-add',
@@ -28,12 +34,23 @@ import {UserService} from "../../../../services/user.service";
 })
 export class ManageUsersAddComponent implements OnInit {
 
+    minUserIdSize=8;
+    success:boolean=false;
+    error:boolean=false;
+    errorResult:ErrorResult;
+    result:string;
+    userid:string;
+
     userForm = this.fb.group({
-        userId: ['', [Validators.required, Validators.minLength(8)]],
-        fullName: ['', Validators.required],
+        user_id: ['', [Validators.required, Validators.minLength(this.minUserIdSize)]],
+        full_name: ['', Validators.required],
         email: ['', [Validators.required,Validators.email]],
         locked: [false],
-      passwordChangeRequired: [true]
+        password_change_required: [true],
+        password: [''],
+        confirm_password: [''],
+    }, {
+        validator: MustMatch('password', 'confirm_password')
     })
 
     constructor(private userService: UserService, private fb: FormBuilder) {
@@ -45,13 +62,41 @@ export class ManageUsersAddComponent implements OnInit {
 
     onSubmit() {
         // Process checkout data here
-        console.warn('Your order has been submitted', JSON.stringify(this.userForm.value));
+        this.result=null;
+        if (this.userForm.valid) {
+            let user = this.copyForm(['user_id','full_name','email','locked','password_change_required',
+            'password','confirm_password'])
+            console.info('Adding user ' + user);
+            this.userService.addUser(user).pipe(catchError((error : ErrorResult)=> {
+                console.log("Error " + error + " - " + typeof (error) + " - " + JSON.stringify(error));
+                if (error.status==422) {
+                    console.warn("Validation error");
+
+                }
+                this.errorResult = error;
+                this.success=false;
+                this.error=true;
+                return throwError(error);
+            })).subscribe((location : string ) => {
+                this.result = location;
+                this.success=true;
+                this.error = false;
+                this.userid = location.substring(location.lastIndexOf('/') + 1);
+            });
+        }
     }
 
-    get userId() {
-      return this.userForm.get('userId');
+
+    private copyForm(properties:string[]) : User {
+        let user : any  = new User();
+        for (let prop of properties) {
+            user[prop] = this.userForm.get(prop).value;
+        }
+        console.log("User " + user);
+        return user;
     }
 
+
     valid(field:string) : string {
       let formField = this.userForm.get(field);
       if (formField.dirty||formField.touched) {
@@ -65,4 +110,26 @@ export class ManageUsersAddComponent implements OnInit {
       }
     }
 
+
+
+
 }
+
+export function MustMatch(controlName: string, matchingControlName: string) {
+    return (formGroup: FormGroup) => {
+        const control = formGroup.controls[controlName];
+        const matchingControl = formGroup.controls[matchingControlName];
+
+        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
+            // return if another validator has already found an error on the matchingControl
+            return;
+        }
+
+        // set error on matchingControl if validation fails
+        if (control.value !== matchingControl.value) {
+            matchingControl.setErrors({ mustMatch: true });
+        } else {
+            matchingControl.setErrors(null);
+        }
+    }
+}
\ No newline at end of file
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html
index fbd6895..960e16d 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-list/manage-users-list.component.html
@@ -50,7 +50,7 @@
                       [ngClass]="user.validated?'fa-check-circle':'fa-circle'"></span></td>
             <td><span class="far" [attr.aria-valuetext]="user.locked"
                       [ngClass]="user.locked?'fa-check-circle':'fa-circle'"></span></td>
-            <td><span class="far" [attr.aria-valuetext]="user.passwordChangeRequired"
+            <td><span class="far" [attr.aria-valuetext]="user.password_change_required"
                       [ngClass]="user.password_change_required?'fa-check-circle':'fa-circle'"></span></td>
             <td>{{user.timestamp_last_login | date:'yyyy-MM-ddTHH:mm:ss'}}</td>
             <td>{{user.timestamp_account_creation | date : 'yyyy-MM-ddTHH:mm:ss'}}</td>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts
index 0227893..2f1e993 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/archiva-request.service.ts
@@ -17,11 +17,12 @@
  * under the License.
  */
 import {Injectable} from '@angular/core';
-import {HttpClient} from "@angular/common/http";
+import {HttpClient, HttpErrorResponse, HttpResponse} from "@angular/common/http";
 import {environment} from "../../environments/environment";
 import {Observable} from "rxjs";
 import {ErrorMessage} from "../model/error-message";
 import {TranslateService} from "@ngx-translate/core";
+import {ErrorResult} from "../model/error-result";
 
 @Injectable({
     providedIn: 'root'
@@ -42,6 +43,21 @@ export class ArchivaRequestService {
      * @param input the input data, if this is a POST or UPDATE request
      */
     executeRestCall<R>(type: string, module: string, service: string, input: object): Observable<R> {
+        let httpArgs = this.getHttpOptions(type, module, service, input);
+        httpArgs['options']['observe'] = 'body';
+        httpArgs['options']['responseType'] = 'json';
+
+        let lType = type.toLowerCase();
+        if (lType == "get") {
+            return this.http.get<R>(httpArgs.url, httpArgs.options);
+        } else if (lType == "head" ) {
+            return this.http.head<R>(httpArgs.url, httpArgs.options);
+        } else if (lType == "post") {
+            return this.http.post<R>(httpArgs.url, input, httpArgs.options);
+        }
+    }
+
+    private getHttpOptions(type: string, module: string, service: string, input: object) {
         let modulePath = environment.application.servicePaths[module];
         let url = environment.application.baseUrl + environment.application.restPath + "/" + modulePath + "/" + service;
         let token = this.getToken();
@@ -53,14 +69,28 @@ export class ArchivaRequestService {
         } else {
             headers = {};
         }
-        if (type == "get") {
+        let options = {'headers': headers}
+        if (type.toLowerCase()=='get') {
             let params = {}
             if (input!=null) {
                 params = input;
             }
-            return this.http.get<R>(url, {"headers": headers,"params":params});
-        } else if (type == "post") {
-            return this.http.post<R>(url, input, {"headers": headers});
+            options['params'] = params;
+        }
+        return {'url':url, 'options':options}
+    }
+
+    executeResponseCall<R>(type: string, module: string, service:string, input:object) : Observable<HttpResponse<R>> {
+        let httpArgs = this.getHttpOptions(type, module, service, input);
+        httpArgs['options']['observe'] = 'response';
+        httpArgs['options']['responseType'] = 'json';
+        let lType = type.toLowerCase();
+        if (lType == "get") {
+            return this.http.get<HttpResponse<R>>(httpArgs.url, httpArgs.options);
+        } else if (lType=='head') {
+            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);
         }
     }
 
@@ -97,4 +127,17 @@ export class ArchivaRequestService {
             return this.translator.instant('api.' + errorMsg.error_key, parms);
         }
     }
+
+    public getTranslatedErrorResult(httpError : HttpErrorResponse) : ErrorResult {
+        let errorResult = httpError.error as ErrorResult;
+        errorResult.status = httpError.status;
+        if (errorResult.error_messages!=null) {
+            for (let message of errorResult.error_messages) {
+                if (message.message==null || message.message=='') {
+                    message.message = this.translateError(message);
+                }
+            }
+        }
+        return errorResult;
+    }
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
index 1792ec8..d3b8e9a 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
@@ -19,12 +19,14 @@
 import {Injectable, OnDestroy, OnInit} from '@angular/core';
 import {ArchivaRequestService} from "./archiva-request.service";
 import {UserInfo} from '../model/user-info';
-import {HttpErrorResponse} from "@angular/common/http";
+import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
 import {ErrorResult} from "../model/error-result";
-import {Observable} from "rxjs";
+import {Observable, throwError} from "rxjs";
 import {Permission} from '../model/permission';
 import {PagedResult} from "../model/paged-result";
 import {EntityService} from "../model/entity-service";
+import { User } from '../model/user';
+import {catchError, map} from "rxjs/operators";
 
 @Injectable({
     providedIn: 'root'
@@ -269,4 +271,11 @@ export class UserService implements OnInit, OnDestroy {
         return this.rest.executeRestCall<PagedResult<UserInfo>>("get", "redback", "users", {'q':searchTerm, 'offset':offset,'limit':limit,'orderBy':orderBy,'order':order});
     }
 
+
+    public addUser(user : User) : Observable<string> {
+        return this.rest.executeResponseCall<string>("post", "redback", "users", user).pipe(
+            catchError( ( error: HttpErrorResponse)  => {
+                return throwError(this.rest.getTranslatedErrorResult(error));
+            }),map( (httpResponse : HttpResponse<string>) => httpResponse.headers.get('Location')));
+    }
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
index 02a32fd..05843c4 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
@@ -47,7 +47,8 @@
     }
   },
   "api" : {
-    "rb.auth.invalid_credentials": "Invalid credentials given"
+    "rb.auth.invalid_credentials": "Invalid credentials given",
+    "user.password.violation.numeric" : "Password must have at least {{arg0}} numeric characters."
   },
   "users": {
     "attributes":{
@@ -61,16 +62,20 @@
         "last_login": "Last Login",
         "created": "Created",
         "permanent": "Permanent",
-        "last_password_change": "Last Password Change"
+        "last_password_change": "Last Password Change",
+        "password": "Password",
+      "confirm_password": "Confirm Password"
     },
     "input" : {
       "small": {
-        "user_id": "Must be a unique key. No space allowed.",
+        "user_id": "Must be a unique key with at least {{minSize}} characters. No spaces allowed.",
         "full_name": "This is the display name of the user"
       },
       "user_id": "Enter user ID",
       "full_name": "Enter full user name",
-      "email": "email@example.org"
+      "email": "email@example.org",
+      "password": "Enter password",
+      "confirm_password": "Confirm password"
     },
 
     "list": {
@@ -88,5 +93,10 @@
   },
   "form": {
     "submit": "Submit"
+  },
+  "password": {
+    "violations" : {
+
+    }
   }
 }
\ No newline at end of file


[archiva] 03/04: Adding user edit component

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit dd422882cac0e1634bcfe73b5e6707ab40924b78
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Wed Nov 11 23:02:43 2020 +0100

    Adding user edit component
---
 .../main/archiva-web/src/app/app-routing.module.ts |  1 +
 .../user/manage-users/manage-users.component.html  |  2 +-
 .../manage-users-add/manage-users-add.component.ts | 13 ++--
 .../manage-users-edit.component.html               | 73 ++++++++++++--------
 .../manage-users-edit.component.ts                 | 27 ++++++--
 .../archiva-web/src/app/services/user.service.ts   | 77 +++++++++++++---------
 .../archiva-web/src/environments/environment.ts    |  1 +
 7 files changed, 124 insertions(+), 70 deletions(-)

diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
index eed0365..f4699f8 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/app-routing.module.ts
@@ -49,6 +49,7 @@ const routes: Routes = [
           {path: 'list', component: ManageUsersListComponent},
           {path: 'add', component: ManageUsersAddComponent},
           {path: 'edit/:userid', component: ManageUsersEditComponent},
+          {path: 'edit', redirectTo:'edit/guest' },
           {path: '', redirectTo:'list', pathMatch:'full'}
         ]
       },
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
index 08b3fca..5eb66fd 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/manage-users/manage-users.component.html
@@ -25,7 +25,7 @@
         <a class="nav-link" routerLink="/user/users/add" routerLinkActive="active" href="#">{{'users.add.head' |translate }}</a>
     </li>
     <li class="nav-item">
-        <a class="nav-link" routerLink="/user/users/edit/guest" routerLinkActive="active" href="#">{{'users.edit.head' |translate }}</a>
+        <a class="nav-link" routerLink="/user/users/edit" routerLinkActive="active" href="#">{{'users.edit.head' |translate }}</a>
     </li>
 </ul>
 
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
index 6a50028..60649fc 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-add/manage-users-add.component.ts
@@ -26,6 +26,7 @@ import {HttpErrorResponse} from "@angular/common/http";
 import {ErrorResult} from "../../../../model/error-result";
 import {catchError} from "rxjs/operators";
 import {of, throwError} from 'rxjs';
+import {environment} from "../../../../../environments/environment";
 
 @Component({
     selector: 'app-manage-users-add',
@@ -34,7 +35,7 @@ import {of, throwError} from 'rxjs';
 })
 export class ManageUsersAddComponent implements OnInit {
 
-    minUserIdSize=8;
+    minUserIdSize=environment.application.minUserIdLength;
     success:boolean=false;
     error:boolean=false;
     errorResult:ErrorResult;
@@ -53,7 +54,7 @@ export class ManageUsersAddComponent implements OnInit {
         validator: MustMatch('password', 'confirm_password')
     })
 
-    constructor(private userService: UserService, private fb: FormBuilder) {
+    constructor(public userService: UserService, public fb: FormBuilder) {
 
     }
 
@@ -97,16 +98,16 @@ export class ManageUsersAddComponent implements OnInit {
     }
 
 
-    valid(field:string) : string {
+    valid(field:string) : string[] {
       let formField = this.userForm.get(field);
       if (formField.dirty||formField.touched) {
         if (formField.valid) {
-          return 'is-valid'
+          return ['is-valid']
         } else {
-          return 'is-invalid'
+          return ['is-invalid']
         }
       } else {
-        return ''
+        return ['']
       }
     }
 
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
index c70355a..f929de2 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.html
@@ -17,56 +17,75 @@
   -->
 
 <form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
-    <div class="form-group col-md-8">
-        <label for="user_id">{{'users.attributes.user_id' |translate}}</label>
-        <input type="text" class="form-control" formControlName="user_id" id="user_id"
+    <div class="form-group row col-md-8">
+        <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"
+                                     (change)="editMode=!editMode"
+                                     >
+        </div>
+    </div>
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="user_id">{{'users.attributes.user_id' |translate}}</label>
+        <div class="col-md-6" >
+        <input type="text" formControlName="user_id" id="user_id"
                [ngClass]="valid('user_id')"
-               placeholder="{{'users.input.user_id'|translate}}">
-        <small>{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+               value="{{editUser.user_id}}" [attr.readonly]="!editMode">
+        <small *ngIf="editMode">{{'users.input.small.user_id'|translate:{'minSize':this.minUserIdSize} }}</small>
+        </div>
     </div>
-    <div class="form-group col-md-8">
-        <label for="full_name">{{'users.attributes.full_name' |translate}}</label>
-        <input type="text" class="form-control" formControlName="full_name" id="full_name"
-               [ngClass]="valid('full_name')"
-               placeholder="{{'users.input.full_name'|translate}}">
-        <small>{{'users.input.small.full_name'|translate}}</small>
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="full_name">{{'users.attributes.full_name' |translate}}</label>
+        <div class="col-md-6" >
+        <input type="text" formControlName="full_name" id="full_name"
+               [ngClass]="valid('full_name')" value="{{editUser.full_name}}" [attr.readonly]="!editMode" >
+        <small *ngIf="editMode">{{'users.input.small.full_name'|translate}}</small>
+        </div>
     </div>
-    <div class="form-group col-md-8">
-        <label for="email">{{'users.attributes.email' |translate}}</label>
-        <input type="text" class="form-control" formControlName="email" id="email"
-               [ngClass]="valid('email')"
-               placeholder="{{'users.input.email'|translate}}">
+    <div class="form-group row col-md-8">
+        <label class="col-md-2 col-form-label" for="email">{{'users.attributes.email' |translate}}</label>
+        <div class="col-md-6" >
+        <input type="text" formControlName="email" id="email"
+               [ngClass]="valid('email')" value="{{editUser.email}}" [attr.readonly]="!editMode">
+        </div>
     </div>
-    <div class="form-group col-md-8">
-        <label for="password">{{'users.attributes.password' |translate}}</label>
+    <div class="form-group row col-md-8" *ngIf="editMode">
+        <label class="col-md-2 col-form-label" for="password">{{'users.attributes.password' |translate}}</label>
+        <div class="col-md-6" >
         <input type="password" class="form-control" formControlName="password" id="password"
                [ngClass]="valid('password')"
                placeholder="{{'users.input.password'|translate}}">
+        </div>
     </div>
-    <div class="form-group col-md-8">
-        <label for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
+    <div class="form-group row col-md-8" *ngIf="editMode">
+        <label class="col-md-2 col-form-label" for="confirm_password">{{'users.attributes.confirm_password' |translate}}</label>
+        <div class="col-md-6">
         <input type="password" class="form-control" formControlName="confirm_password" id="confirm_password"
                [ngClass]="valid('confirm_password')"
                placeholder="{{'users.input.confirm_password'|translate}}">
+        </div>
     </div>
-    <div class="form-group col-md-8">
+    <div class="form-group row col-md-8">
+        <div class="col-md-2">Flags</div>
+        <div class="col-md-6">
         <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="" formControlName="locked" id="locked">
-            <label class="form-check-label" for="locked">
+            <input class="form-check-input" type="checkbox" value="{{editUser.locked}}" formControlName="locked" id="locked" [attr.disabled]="editMode?null:true">
+            <label class="form-check-label" for="locked" >
                 {{'users.attributes.locked'|translate}}
             </label>
         </div>
         <div class="form-check">
-            <input class="form-check-input" type="checkbox" value="" formControlName="password_change_required"
-                   id="password_change_required" checked>
+            <input class="form-check-input" type="checkbox" value="{{editUser.password_change_required}}" formControlName="password_change_required"
+                   id="password_change_required" [attr.disabled]="editMode?null:true">
             <label class="form-check-label" for="password_change_required">
                 {{'users.attributes.password_change_required'|translate}}
             </label>
         </div>
+        </div>
     </div>
-    <div class="form-group col-md-8">
+    <div class="form-group col-md-8" *ngIf="editMode">
         <button class="btn btn-primary" type="submit"
-                [disabled]="!userForm.valid">{{'users.add.submit'|translate}}</button>
+                [disabled]="!userForm.valid">{{'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.
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
index 5d55089..adb5b91 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/user/users/manage-users-edit/manage-users-edit.component.ts
@@ -18,21 +18,40 @@
 
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
+import {UserService} from "../../../../services/user.service";
+import {FormBuilder, Validators} from "@angular/forms";
+import {ManageUsersAddComponent, MustMatch} from "../manage-users-add/manage-users-add.component";
+import {environment} from "../../../../../environments/environment";
+import {map, switchMap} from 'rxjs/operators';
 
 @Component({
   selector: 'app-manage-users-edit',
   templateUrl: './manage-users-edit.component.html',
   styleUrls: ['./manage-users-edit.component.scss']
 })
-export class ManageUsersEditComponent implements OnInit {
+export class ManageUsersEditComponent extends ManageUsersAddComponent implements OnInit {
 
-  userid;
+  editUser;
+  editMode:boolean=false;
 
-  constructor(private route: ActivatedRoute) {
-    this.route.params.subscribe(params => this.userid=params.userid);
+  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;});
   }
 
   ngOnInit(): void {
+
+  }
+
+  valid(field: string): string[] {
+    if (this.editMode) {
+      let classArr  = super.valid(field);
+      return classArr.concat('form-control')
+    } else {
+      return ['form-control-plaintext'];
+    }
   }
 
+
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
index d3b8e9a..5e0f172 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/user.service.ts
@@ -24,8 +24,7 @@ import {ErrorResult} from "../model/error-result";
 import {Observable, throwError} from "rxjs";
 import {Permission} from '../model/permission';
 import {PagedResult} from "../model/paged-result";
-import {EntityService} from "../model/entity-service";
-import { User } from '../model/user';
+import {User} from '../model/user';
 import {catchError, map} from "rxjs/operators";
 
 @Injectable({
@@ -37,25 +36,25 @@ export class UserService implements OnInit, OnDestroy {
     permissions: Permission[];
     guestPermissions: Permission[];
     authenticated: boolean;
-    uiPermissionsDefault  = {
+    uiPermissionsDefault = {
         'menu': {
-            'repo':{
-                'section':true,
-                'browse':true,
-                'search':true,
-                'upload':false
+            'repo': {
+                'section': true,
+                'browse': true,
+                'search': true,
+                'upload': false
             },
-            'admin':{
-                'section':false,
-                'config':false,
-                'status':false,
-                'reports':false
+            'admin': {
+                'section': false,
+                'config': false,
+                'status': false,
+                'reports': false
             },
-            'user':{
-                'section':false,
-                'manage':false,
-                'roles':false,
-                'config':false
+            'user': {
+                'section': false,
+                'manage': false,
+                'roles': false,
+                'config': false
             }
         }
     };
@@ -86,7 +85,7 @@ export class UserService implements OnInit, OnDestroy {
                     }
                 },
                 error: err => {
-                    console.log("Could not retrieve permissions "+err);
+                    console.log("Could not retrieve permissions " + err);
                 }
             }
             this.retrievePermissionInfo().subscribe(observer);
@@ -172,13 +171,14 @@ export class UserService implements OnInit, OnDestroy {
     resetPermissions() {
         this.deepCopy(this.uiPermissionsDefault, this.uiPermissions);
     }
+
     parsePermissions(permissions: Permission[]) {
         this.resetPermissions();
-        for ( let perm of permissions) {
+        for (let perm of permissions) {
             // console.debug("Checking permission for op: " + perm.operation.name);
             switch (perm.operation.name) {
                 case "archiva-manage-configuration": {
-                    if (perm.resource.identifier=='*') {
+                    if (perm.resource.identifier == '*') {
                         this.uiPermissions.menu.admin.section = true;
                         this.uiPermissions.menu.admin.config = true;
                         this.uiPermissions.menu.admin.reports = true;
@@ -187,7 +187,7 @@ export class UserService implements OnInit, OnDestroy {
 
                 }
                 case "archiva-manage-users": {
-                    if (perm.resource.identifier=='*') {
+                    if (perm.resource.identifier == '*') {
                         this.uiPermissions.menu.user.section = true;
                         this.uiPermissions.menu.user.config = true;
                         this.uiPermissions.menu.user.manage = true;
@@ -195,7 +195,7 @@ export class UserService implements OnInit, OnDestroy {
                     }
                 }
                 case "redback-configuration-edit": {
-                    if (perm.resource.identifier=='*') {
+                    if (perm.resource.identifier == '*') {
                         this.uiPermissions.menu.user.section = true;
                         this.uiPermissions.menu.user.config = true;
                     }
@@ -210,7 +210,7 @@ export class UserService implements OnInit, OnDestroy {
     private deepCopy(src: Object, dst: Object) {
         Object.keys(src).forEach((key, idx) => {
             let srcEl = src[key];
-            if (typeof(srcEl)=='object' ) {
+            if (typeof (srcEl) == 'object') {
                 let dstEl;
                 if (!dst.hasOwnProperty(key)) {
                     dst[key] = {}
@@ -260,22 +260,35 @@ export class UserService implements OnInit, OnDestroy {
         this.authenticated = false;
     }
 
-    public query(searchTerm : string, offset : number = 0, limit : number = 10, orderBy : string[] = ['user_id'], order: string = 'asc') : Observable<PagedResult<UserInfo>>  {
+    public query(searchTerm: string, offset: number = 0, limit: number = 10, orderBy: string[] = ['user_id'], order: string = 'asc'): Observable<PagedResult<UserInfo>> {
         console.log("getUserList " + searchTerm + "," + offset + "," + limit + "," + orderBy + "," + order);
-        if (searchTerm==null) {
-            searchTerm=""
+        if (searchTerm == null) {
+            searchTerm = ""
         }
-        if (orderBy==null || orderBy.length==0) {
+        if (orderBy == null || orderBy.length == 0) {
             orderBy = ['user_id'];
         }
-        return this.rest.executeRestCall<PagedResult<UserInfo>>("get", "redback", "users", {'q':searchTerm, 'offset':offset,'limit':limit,'orderBy':orderBy,'order':order});
+        return this.rest.executeRestCall<PagedResult<UserInfo>>("get", "redback", "users", {
+            'q': searchTerm,
+            'offset': offset,
+            'limit': limit,
+            'orderBy': orderBy,
+            'order': order
+        });
     }
 
 
-    public addUser(user : User) : Observable<string> {
+    public addUser(user: User): Observable<string> {
         return this.rest.executeResponseCall<string>("post", "redback", "users", user).pipe(
-            catchError( ( error: HttpErrorResponse)  => {
+            catchError((error: HttpErrorResponse) => {
+                return throwError(this.rest.getTranslatedErrorResult(error));
+            }), map((httpResponse: HttpResponse<string>) => httpResponse.headers.get('Location')));
+    }
+
+    public getUser(userid: string): Observable<UserInfo> {
+        return this.rest.executeRestCall<UserInfo>("get", "redback", "users/" + userid, null).pipe(
+            catchError((error: HttpErrorResponse) => {
                 return throwError(this.rest.getTranslatedErrorResult(error));
-            }),map( (httpResponse : HttpResponse<string>) => httpResponse.headers.get('Location')));
+            }));
     }
 }
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/environments/environment.ts b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/environments/environment.ts
index cf788e2..12dddab 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/environments/environment.ts
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/environments/environment.ts
@@ -28,6 +28,7 @@ export const environment = {
     client_id:'archiva_web_client',
     baseUrl: 'http://localhost:8080',
     restPath: '/archiva/api/v2',
+    minUserIdLength: 8,
     servicePaths: {
       archiva:"archiva",
       redback:"redback",