mirror of https://github.com/apache/archiva.git
Adding user info and password change
This commit is contained in:
parent
cb0e4d19d7
commit
c7344aa1c4
|
@ -0,0 +1,28 @@
|
|||
package org.apache.archiva.rest.api.services.v2;/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Service for configuration of redback and security related settings.
|
||||
*
|
||||
* @author Martin Stockhammer <martin_s@apache.org>
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SecurityConfigurationService
|
||||
{
|
||||
}
|
|
@ -29,6 +29,7 @@ import {BrowseComponent} from "./modules/repo/browse/browse.component";
|
|||
import {UploadComponent} from "./modules/repo/upload/upload.component";
|
||||
import {RoutingGuardService as Guard} from "./services/routing-guard.service";
|
||||
import {SecurityConfigurationComponent} from "./modules/security/security-configuration/security-configuration.component";
|
||||
import {UserInfoComponent} from "@app/modules/shared/user-info/user-info.component";
|
||||
|
||||
/**
|
||||
* You can use Guard (RoutingGuardService) for permission checking. The service needs data with one parameter 'perm',
|
||||
|
@ -55,6 +56,7 @@ const routes: Routes = [
|
|||
]
|
||||
},
|
||||
{path: 'contact', component: ContactComponent},
|
||||
{path: 'me/info', component: UserInfoComponent},
|
||||
{path: 'about', component: AboutComponent},
|
||||
{path: 'login', component: LoginComponent},
|
||||
{path: 'logout', component: HomeComponent},
|
||||
|
|
|
@ -50,9 +50,11 @@
|
|||
<div class="collapse navbar-collapse" id="navbarsDefault">
|
||||
<div class="navbar-nav ml-auto">
|
||||
<span *ngIf="auth.authenticated" class="navbar-text border-right pr-2 mr-2">
|
||||
{{user.userInfo.full_name}}
|
||||
<a class="nav-link" routerLink="/me/info" >
|
||||
{{user.userInfo.full_name}}
|
||||
</a>
|
||||
</span>
|
||||
<ul class="navbar-nav">
|
||||
<ul class="navbar-nav d-lg-flex align-items-center">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" routerLink="/">
|
||||
<i class="fas fa-home mr-1"></i>{{ 'menu.home' |translate }}
|
||||
|
|
|
@ -37,6 +37,8 @@ import {RouterModule} from "@angular/router";
|
|||
import { WithLoadingPipe } from './with-loading.pipe';
|
||||
import { StripLoadingPipe } from './strip-loading.pipe';
|
||||
import { ToastComponent } from './toast/toast.component';
|
||||
import { UserInfoComponent } from './user-info/user-info.component';
|
||||
import {FormsModule} from "@angular/forms";
|
||||
|
||||
export { LoadingValue } from './model/loading-value';
|
||||
export { PageQuery } from './model/page-query';
|
||||
|
@ -48,7 +50,8 @@ export { PageQuery } from './model/page-query';
|
|||
SortedTableHeaderRowComponent,
|
||||
WithLoadingPipe,
|
||||
StripLoadingPipe,
|
||||
ToastComponent
|
||||
ToastComponent,
|
||||
UserInfoComponent
|
||||
],
|
||||
exports: [
|
||||
CommonModule,
|
||||
|
@ -84,6 +87,7 @@ export { PageQuery } from './model/page-query';
|
|||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
FormsModule,
|
||||
]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
|
|
@ -41,9 +41,7 @@ import {AppNotification} from "@app/model/app-notification";
|
|||
<ng-template #text>{{ toast.body }}</ng-template>
|
||||
</ngb-toast>
|
||||
`,
|
||||
styles: [".ngb-toasts{margin:.5em;padding:0.5em;position:fixed;right:2px;top:20px;z-index:1200}"
|
||||
],
|
||||
host: {'[class.ngb-toasts]': 'true'}
|
||||
styles: [':host { margin:.5em; padding:1em; position:fixed; right:10px; top:40px; z-index:1200; }']
|
||||
})
|
||||
export class ToastComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div class="col-md-12">
|
||||
<h2>{{'me.title'|translate}}</h2>
|
||||
<form #passwordForm="ngForm" (ngSubmit)="changePassword()" class="mt-4">
|
||||
<div class="form-group row" *ngFor="let att of ['user_id','full_name','email','language']">
|
||||
<label for="{{att}}" class="col-md-1 col-form-label">{{'users.attributes.' + att|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="{{att}}" class="form-control" value="{{userService.userInfo[att]}}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row"
|
||||
*ngFor="let dateAtt of ['timestamp_account_creation','timestamp_last_password_change']">
|
||||
<label for="{{dateAtt}}" class="col-md-1 col-form-label">{{'users.attributes.' + dateAtt|translate}}</label>
|
||||
<ng-container *ngIf="userService.userInfo[dateAtt]">
|
||||
<div class="col-md-4">
|
||||
<input id="{{dateAtt}}" class="form-control"
|
||||
value="{{userService.userInfo[dateAtt]|date:'YYYY-MM-dd HH:mm:ss'}}" readonly>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-1"> </div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-check" *ngFor="let checkAtt of ['password_change_required','validated','locked']">
|
||||
<input id="{{checkAtt}}" type="checkbox" class="form-check-input disabled"
|
||||
[checked]="userService.userInfo[checkAtt]?'true':null" disabled="disabled">
|
||||
<label for="{{checkAtt}}"
|
||||
class="form-check-label">{{'users.attributes.' + checkAtt|translate}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mt-3" />
|
||||
<h2>{{'users.edit.changePasswordTitle'|translate}}</h2>
|
||||
<div class="form-group row mt-4" >
|
||||
<label for="current_password" class="col-md-1 col-form-label">{{'users.attributes.current_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="current_password" name="current_password" type="password" class="form-control" required
|
||||
[(ngModel)]="formData.current_password" #v_current_password="ngModel" [ngClass]="valid('current_password')">
|
||||
<small class="invalid-feedback" *ngIf="v_current_password.errors?.required">{{'form.error.required'|translate}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<label for="password" class="col-md-1 col-form-label">{{'users.attributes.new_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="password" name="password" type="password" class="form-control" required
|
||||
[ngClass]="valid('password')"
|
||||
[(ngModel)]="formData.password" #v_password="ngModel" >
|
||||
<small class="invalid-feedback" *ngIf="v_password.errors?.required">{{'form.error.required'|translate}}</small>
|
||||
<small class="invalid-feedback" *ngIf="v_password.errors?.invalidpassword">{{v_password.errors.invalidpassword}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<label for="confirm_password" class="col-md-1 col-form-label">{{'users.attributes.confirm_password'|translate}}</label>
|
||||
<div class="col-md-4">
|
||||
<input id="confirm_password" name="confirm_password" type="password" class="form-control" required
|
||||
[(ngModel)]="formData.confirm_password" [class.is-valid]="confirmIsValid()" [class.is-invalid]="confirmIsNotValid()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" >
|
||||
<div class="col-md-1"> </div>
|
||||
<div class="col-md-4">
|
||||
<button class="btn btn-primary"
|
||||
type="submit">{{'users.edit.submitPassword'|translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -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 { UserInfoComponent } from './user-info.component';
|
||||
|
||||
describe('UserInfoComponent', () => {
|
||||
let component: UserInfoComponent;
|
||||
let fixture: ComponentFixture<UserInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ UserInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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, ViewChild} from '@angular/core';
|
||||
import {NgForm} from '@angular/forms';
|
||||
import {UserService} from "@app/services/user.service";
|
||||
import {ToastService} from "@app/services/toast.service";
|
||||
import {ErrorResult} from "@app/model/error-result";
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-info',
|
||||
templateUrl: './user-info.component.html',
|
||||
styleUrls: ['./user-info.component.scss']
|
||||
})
|
||||
export class UserInfoComponent implements OnInit {
|
||||
|
||||
@ViewChild('passwordForm') passwordForm: NgForm;
|
||||
|
||||
formData = {
|
||||
current_password:"",
|
||||
password: "",
|
||||
confirm_password: ""
|
||||
}
|
||||
|
||||
|
||||
constructor(public userService: UserService, private toastService: ToastService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
changePassword() {
|
||||
console.log("Submit Password ")
|
||||
if (!this.formData.current_password || this.formData.current_password.length==0) {
|
||||
this.passwordForm.controls['current_password'].setErrors({'required':'true'})
|
||||
this.passwordForm.controls['current_password'].markAsDirty()
|
||||
}
|
||||
if (!this.formData.password || this.formData.password.length==0) {
|
||||
this.passwordForm.controls['password'].setErrors({'required':'true'})
|
||||
this.passwordForm.controls['password'].markAsDirty()
|
||||
}
|
||||
if (this.passwordForm.valid) {
|
||||
this.userService.changeOwnPassword(this.formData.current_password, this.formData.password, this.formData.confirm_password).subscribe(
|
||||
val => {
|
||||
this.toastService.showSuccessByKey('user-info','users.edit.passwordChanged')
|
||||
this.formData.password=""
|
||||
this.formData.current_password=""
|
||||
this.formData.confirm_password=""
|
||||
this.passwordForm.reset()
|
||||
},
|
||||
( error : ErrorResult)=>{
|
||||
console.log("Error " + error.error_messages[0].message);
|
||||
if (error.error_messages.length>0) {
|
||||
if (error.error_messages[0].error_key.startsWith('user.password.violation')) {
|
||||
this.toastService.showError('user-info', error.error_messages[0].message)
|
||||
this.passwordForm.controls['password'].setErrors({'invalidpassword':error.error_messages[0].message})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.toastService.showErrorByKey('user-info','form.error.invaliddata')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
confirmIsValid() : boolean {
|
||||
return (this.formData.password && this.formData.password.length >0 &&
|
||||
this.formData.confirm_password && this.formData.confirm_password.length>0 && this.formData.password==this.formData.confirm_password )
|
||||
}
|
||||
|
||||
confirmIsNotValid() : boolean {
|
||||
return (this.formData.password && this.formData.password.length>0 &&
|
||||
this.formData.confirm_password && this.formData.confirm_password.length>0 && this.formData.password!=this.formData.confirm_password )
|
||||
}
|
||||
|
||||
valid(field:string) {
|
||||
if (this.passwordForm) {
|
||||
let ctrl = this.passwordForm.controls[field];
|
||||
if (ctrl && ( ctrl.dirty || ctrl.touched ) && ctrl.valid) {
|
||||
return 'is-valid'
|
||||
}
|
||||
if (ctrl && ( ctrl.dirty || ctrl.touched ) && ctrl.invalid) {
|
||||
return 'is-invalid'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -128,6 +128,7 @@ export class ArchivaRequestService {
|
|||
* @param errorMsg the errorMsg as returned by a REST call
|
||||
*/
|
||||
public translateError(errorMsg: ErrorMessage): string {
|
||||
console.log("Translating error "+errorMsg.error_key)
|
||||
if (errorMsg.error_key != null && errorMsg.error_key != '') {
|
||||
let parms = {};
|
||||
if (errorMsg.args != null && errorMsg.args.length > 0) {
|
||||
|
|
|
@ -328,7 +328,7 @@ export class UserService implements OnInit, OnDestroy {
|
|||
}), map((httpResponse: HttpResponse<string>) => httpResponse.status == 200));
|
||||
}
|
||||
|
||||
public userRoleTree(userid:String): Observable<RoleTree> {
|
||||
public userRoleTree(userid:string): Observable<RoleTree> {
|
||||
return this.rest.executeResponseCall<RoleTree>("get", "redback","users/"+userid+"/roletree", null).pipe(
|
||||
catchError((error: HttpErrorResponse)=>{
|
||||
if (error.status==404) {
|
||||
|
@ -341,4 +341,18 @@ export class UserService implements OnInit, OnDestroy {
|
|||
).pipe(map((httpResponse:HttpResponse<RoleTree>)=>httpResponse.body))
|
||||
}
|
||||
|
||||
public changeOwnPassword(current_password:string, password:string, confirm_password:string) {
|
||||
let data = {
|
||||
"user_id":this.userInfo.user_id,
|
||||
"current_password":current_password,
|
||||
"new_password":password,
|
||||
"new_password_confirmation":confirm_password
|
||||
}
|
||||
return this.rest.executeRestCall<any>("post", "redback", "users/me/password/update", data).pipe(
|
||||
catchError((error: HttpErrorResponse)=>{
|
||||
return throwError(this.rest.getTranslatedErrorResult(error));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"userid": "User Id",
|
||||
"submit": "Login"
|
||||
},
|
||||
"modal" : {
|
||||
"modal": {
|
||||
"close": "Close"
|
||||
},
|
||||
"menu": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
"error": {
|
||||
"modal": {
|
||||
"title": "Application Error",
|
||||
"info":"The backend does not answer as expected. Please check, if your archiva service is running. See detail messages below."
|
||||
"info": "The backend does not answer as expected. Please check, if your archiva service is running. See detail messages below."
|
||||
},
|
||||
"http": {
|
||||
"unknownError": "We got a bad response from the backend REST service. Maybe the connection is broken, or the service is down. Please check your network to the backend service and the archiva.log for any errors.",
|
||||
|
@ -47,7 +47,7 @@
|
|||
"user": {
|
||||
"section": "User",
|
||||
"users": "Manage Users",
|
||||
"roles":"Manage Roles",
|
||||
"roles": "Manage Roles",
|
||||
"config": "Security Configuration"
|
||||
},
|
||||
"doc": {
|
||||
|
@ -56,32 +56,38 @@
|
|||
"restapi": "REST API"
|
||||
}
|
||||
},
|
||||
"api" : {
|
||||
"api": {
|
||||
"rb.auth.invalid_credentials": "Invalid credentials given",
|
||||
"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."
|
||||
"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":{
|
||||
"id": "ID",
|
||||
"user_id": "Login Name",
|
||||
"email": "Email",
|
||||
"full_name": "Name",
|
||||
"validated": "User Validated",
|
||||
"locked": "User Locked",
|
||||
"password_change_required": "Password Change Required",
|
||||
"last_login": "Last Login",
|
||||
"created": "Created",
|
||||
"permanent": "Permanent",
|
||||
"last_password_change": "Last Password Change",
|
||||
"password": "Password",
|
||||
"confirm_password": "Confirm Password"
|
||||
"attributes": {
|
||||
"id": "ID",
|
||||
"user_id": "Login Name",
|
||||
"email": "Email",
|
||||
"full_name": "Name",
|
||||
"validated": "User Validated",
|
||||
"locked": "User Locked",
|
||||
"password_change_required": "Password Change Required",
|
||||
"last_login": "Last Login",
|
||||
"created": "Created",
|
||||
"permanent": "Permanent",
|
||||
"last_password_change": "Last Password Change",
|
||||
"timestamp_last_password_change": "Last Password Change",
|
||||
"timestamp_account_creation": "Account Created",
|
||||
"timestamp_last_login": "Last Login",
|
||||
"password": "Password",
|
||||
"new_password": "New Password",
|
||||
"current_password": "Current Password",
|
||||
"confirm_password": "Confirm Password",
|
||||
"language": "UI Language"
|
||||
},
|
||||
"input" : {
|
||||
"input": {
|
||||
"small": {
|
||||
"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"
|
||||
|
@ -92,7 +98,6 @@
|
|||
"password": "Enter password",
|
||||
"confirm_password": "Confirm password"
|
||||
},
|
||||
|
||||
"list": {
|
||||
"head": "List Users"
|
||||
},
|
||||
|
@ -107,10 +112,13 @@
|
|||
"head": "View/Edit User",
|
||||
"small": {
|
||||
"password": "If the password field is empty, it will not be updated."
|
||||
}
|
||||
},
|
||||
"submitPassword": "Change Password",
|
||||
"passwordChanged": "Password has been changed successfully",
|
||||
"changePasswordTitle": "Change password"
|
||||
},
|
||||
"delete": {
|
||||
"head":"Delete User",
|
||||
"head": "Delete User",
|
||||
"modal": {
|
||||
"title": "Delete User",
|
||||
"text": "Are you sure? <br/> Do you want to delete the user <strong>{user_id}</strong>?"
|
||||
|
@ -164,7 +172,6 @@
|
|||
"assignable": "Assignable"
|
||||
}
|
||||
},
|
||||
|
||||
"permissions": {
|
||||
"attributes": {
|
||||
"permission": "Permission",
|
||||
|
@ -182,7 +189,8 @@
|
|||
"error": {
|
||||
"required": "Value is empty. This is required.",
|
||||
"containsWhitespace": "Value must not contain whitespace.",
|
||||
"userexists": "This user exists already."
|
||||
"userexists": "This user exists already.",
|
||||
"invaliddata": "The data is not valid"
|
||||
},
|
||||
"button": {
|
||||
"yes": "Yes",
|
||||
|
@ -197,8 +205,10 @@
|
|||
"action": "Action"
|
||||
},
|
||||
"password": {
|
||||
"violations" : {
|
||||
|
||||
"violations": {
|
||||
}
|
||||
},
|
||||
"me": {
|
||||
"title": "Current User Information"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue