profile improvements, aliases feature
This commit is contained in:
parent
35411505f8
commit
40ade2ca06
@ -20,6 +20,7 @@ import {SecurityComponent} from './pages/account/security/security.component';
|
|||||||
import {UnavailableComponent} from './pages/unavailable/unavailable.component';
|
import {UnavailableComponent} from './pages/unavailable/unavailable.component';
|
||||||
import {NotfoundComponent} from './pages/notfound/notfound.component';
|
import {NotfoundComponent} from './pages/notfound/notfound.component';
|
||||||
import {UserComponent} from './pages/user/user.component'
|
import {UserComponent} from './pages/user/user.component'
|
||||||
|
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
||||||
@ -38,8 +39,9 @@ const routes: Routes = [
|
|||||||
|
|
||||||
{path: 'info', component: InfoComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'info', component: InfoComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'profile', component: ProfileComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'profile', component: ProfileComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
{path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard]}
|
{path: 'aliases', component: AliasesComponent, canActivate: [AuthenticatedGuard]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
||||||
|
@ -25,6 +25,7 @@ import {SecurityComponent, SecurityTotpDialog} from './pages/account/security/se
|
|||||||
import {VoucherComponent} from './pages/account/voucher/voucher.component';
|
import {VoucherComponent} from './pages/account/voucher/voucher.component';
|
||||||
import {VoucherDialog} from './pages/account/voucher/voucher.component';
|
import {VoucherDialog} from './pages/account/voucher/voucher.component';
|
||||||
import {InfoComponent} from './pages/account/info/info.component';
|
import {InfoComponent} from './pages/account/info/info.component';
|
||||||
|
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
||||||
import {ProfileComponent} from './pages/account/profile/profile.component';
|
import {ProfileComponent} from './pages/account/profile/profile.component';
|
||||||
import {PasswordComponent} from './pages/password/password.component';
|
import {PasswordComponent} from './pages/password/password.component';
|
||||||
import {PasswordResetComponent} from './pages/password-reset/password-reset.component';
|
import {PasswordResetComponent} from './pages/password-reset/password-reset.component';
|
||||||
@ -75,6 +76,7 @@ export class XhrInterceptor implements HttpInterceptor {
|
|||||||
VoucherComponent,
|
VoucherComponent,
|
||||||
VoucherDialog,
|
VoucherDialog,
|
||||||
InfoComponent,
|
InfoComponent,
|
||||||
|
AliasesComponent,
|
||||||
ProfileComponent,
|
ProfileComponent,
|
||||||
PasswordComponent,
|
PasswordComponent,
|
||||||
PasswordResetComponent,
|
PasswordResetComponent,
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
<nav mat-tab-nav-bar>
|
<nav mat-tab-nav-bar>
|
||||||
<a mat-tab-link routerLink="info" routerLinkActive="active">{{'info' | i18n}}</a>
|
<a mat-tab-link routerLink="info" routerLinkActive="active">{{'info' | i18n}}</a>
|
||||||
<a mat-tab-link routerLink="profile" routerLinkActive="active">{{'profile' | i18n}}</a>
|
<a mat-tab-link routerLink="profile" routerLinkActive="active">{{'profile' | i18n}}</a>
|
||||||
<a mat-tab-link routerLink="voucher" routerLinkActive="active">{{'vouchers' | i18n}}</a>
|
|
||||||
<a mat-tab-link routerLink="security" routerLinkActive="active">{{'security' | i18n}}</a>
|
<a mat-tab-link routerLink="security" routerLinkActive="active">{{'security' | i18n}}</a>
|
||||||
|
<a mat-tab-link routerLink="voucher" routerLinkActive="active">{{'vouchers' | i18n}}</a>
|
||||||
|
<a mat-tab-link routerLink="aliases" routerLinkActive="active">{{'user.aliases' | i18n}}</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
77
src/app/pages/account/aliases/aliases.component.html
Normal file
77
src/app/pages/account/aliases/aliases.component.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<h3>{{'user.aliases' | i18n}}</h3>
|
||||||
|
|
||||||
|
<table mat-table matSort [dataSource]="aliases" (matSortChange)="sortData($event)">
|
||||||
|
|
||||||
|
<ng-container matColumnDef="alias">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="alias"> {{'user.aliases.alias' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let alias"> {{ alias.alias}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="visibility">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let alias">
|
||||||
|
<mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)"
|
||||||
|
placeholder="{{'visibility' | i18n}}">
|
||||||
|
<mat-option *ngFor="let visibility of visibilities" [value]="visibility">
|
||||||
|
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
visibility | i18n}}
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-select-trigger>
|
||||||
|
<mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
alias.visibility | i18n}}
|
||||||
|
</mat-select-trigger>
|
||||||
|
</mat-select>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.aliases.delete' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let alias" class="text-right">
|
||||||
|
<a mat-icon-button>
|
||||||
|
<mat-icon (click)="confirmDelete(alias)">delete</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="aliasesColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let myRowData; columns: aliasesColumns"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'user.aliases.info' | i18n}}</p>
|
||||||
|
<p *ngIf="!aliasCreation">{{'user.aliases.noQuota' | i18n}}</p>
|
||||||
|
<div *ngIf="aliasCreation">
|
||||||
|
<p>{{'user.aliases.left' | i18n:aliasCreation}}</p>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{'user.aliases.alias' | i18n}}" formControlName="alias"
|
||||||
|
[(ngModel)]="alias.alias" required>
|
||||||
|
<mat-error>
|
||||||
|
{{'user.aliases.error' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select [(ngModel)]="alias.visibility" formControlName="visibility" placeholder="{{'visibility' | i18n}}">
|
||||||
|
<mat-option *ngFor="let visibility of visibilities" [value]="visibility">
|
||||||
|
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
visibility | i18n}}
|
||||||
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-select-trigger>
|
||||||
|
<mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
alias.visibility | i18n}}
|
||||||
|
</mat-select-trigger>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button *ngIf="aliasCreation && !working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||||
|
{{'user.aliases.create' | i18n}}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
</form>
|
16
src/app/pages/account/aliases/aliases.component.scss
Normal file
16
src/app/pages/account/aliases/aliases.component.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-header-cell,
|
||||||
|
.mat-cell {
|
||||||
|
&.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right{
|
||||||
|
display: flex;
|
||||||
|
padding: 21px 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
25
src/app/pages/account/aliases/aliases.component.spec.ts
Normal file
25
src/app/pages/account/aliases/aliases.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AliasesComponent } from './aliases.component';
|
||||||
|
|
||||||
|
describe('AliasesComponent', () => {
|
||||||
|
let component: AliasesComponent;
|
||||||
|
let fixture: ComponentFixture<AliasesComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ AliasesComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AliasesComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
130
src/app/pages/account/aliases/aliases.component.ts
Normal file
130
src/app/pages/account/aliases/aliases.component.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {QuotaService} from '../../../services/quota.service';
|
||||||
|
import {I18nService} from '../../../services/i18n.service';
|
||||||
|
import {Sort} from '@angular/material/sort';
|
||||||
|
import {UserAliasService} from '../../../services/useralias.service';
|
||||||
|
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
||||||
|
import {MatDialog} from '@angular/material/dialog';
|
||||||
|
import {ConfirmDialog} from '../../../ui/confirm/confirm.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-account-aliases',
|
||||||
|
templateUrl: './aliases.component.html',
|
||||||
|
styleUrls: ['./aliases.component.scss']
|
||||||
|
})
|
||||||
|
export class AliasesComponent implements OnInit {
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
@ViewChild('formDirective') private formDirective: NgForm;
|
||||||
|
aliasCreation: number = 0;
|
||||||
|
aliases: any[] = [];
|
||||||
|
alias: any = {
|
||||||
|
visibility: "PROTECTED"
|
||||||
|
};
|
||||||
|
success: boolean;
|
||||||
|
working: boolean;
|
||||||
|
|
||||||
|
aliasesColumns = ["alias", "visibility", "delete"];
|
||||||
|
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private quotaService: QuotaService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private userAliasService: UserAliasService,
|
||||||
|
private i18n : I18nService,
|
||||||
|
public dialog: MatDialog) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
alias: ['', Validators.required],
|
||||||
|
visibility: ['', Validators.required],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.working = true;
|
||||||
|
this.userAliasService.create(this.alias).subscribe(response => {
|
||||||
|
this.update();
|
||||||
|
this.formDirective.resetForm();
|
||||||
|
this.alias = {
|
||||||
|
visibility: "PROTECTED"
|
||||||
|
};
|
||||||
|
this.working = false;
|
||||||
|
}, (error) => {
|
||||||
|
this.working = false;
|
||||||
|
if(error.status == 409) {
|
||||||
|
let errors = {};
|
||||||
|
for(let code of error.error) {
|
||||||
|
errors[code.field] = errors[code.field] || {};
|
||||||
|
errors[code.field][code.code] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let code in errors) {
|
||||||
|
this.form.get(code).setErrors(errors[code]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAlias(alias) {
|
||||||
|
this.userAliasService.update(alias).subscribe((response: any) => {
|
||||||
|
alias = response;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.aliasCreation = 0;
|
||||||
|
this.quotaService.quotas().subscribe((data: any) => {
|
||||||
|
for(let quota of data) {
|
||||||
|
if(quota.name == "alias_creation") {
|
||||||
|
this.aliasCreation = quota.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.userAliasService.get().subscribe((data: any) => {
|
||||||
|
this.aliases = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(alias) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||||
|
data: {
|
||||||
|
'label': 'user.aliases.confirmDelete',
|
||||||
|
'args': [alias.alias]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if(result) {
|
||||||
|
this.userAliasService.delete(alias.id).subscribe((result: any) => {
|
||||||
|
this.update();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sortData(sort: Sort) {
|
||||||
|
const data = this.aliases.slice();
|
||||||
|
if(!sort.active || sort.direction === '') {
|
||||||
|
this.aliases = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.aliases = data.sort((a, b) => {
|
||||||
|
const isAsc = sort.direction === 'asc';
|
||||||
|
switch(sort.active) {
|
||||||
|
case 'alias': return this.compare(a.alias, b.alias, isAsc);
|
||||||
|
case 'visibility': return this.compare(this.i18n.get('visibility.' + a.visibility, []), this.i18n.get('visibility.' + b.visibility, []), isAsc);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compare(a: number | string | String, b: number | string | String, isAsc: boolean) {
|
||||||
|
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,4 @@
|
|||||||
<h3>{{'quotas' | i18n}}</h3>
|
<h3>{{'quotas' | i18n}}</h3>
|
||||||
<app-quotas [quotas]="quotas"></app-quotas>
|
<app-quotas [quotas]="quotas"></app-quotas>
|
||||||
<h3>{{'profile' | i18n}}</h3>
|
<h3>{{'profile' | i18n}}</h3>
|
||||||
<app-profilefields [profileFields]="profileFields"></app-profilefields>
|
<app-profilefields></app-profilefields>
|
@ -1,7 +1,6 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {PermissionService} from './../../../services/permission.service';
|
import {PermissionService} from './../../../services/permission.service';
|
||||||
import {QuotaService} from './../../../services/quota.service';
|
import {QuotaService} from './../../../services/quota.service';
|
||||||
import { ProfileService } from './../../../services/profile.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account-info',
|
selector: 'app-account-info',
|
||||||
@ -12,9 +11,8 @@ export class InfoComponent implements OnInit {
|
|||||||
|
|
||||||
permissions = [];
|
permissions = [];
|
||||||
quotas = [];
|
quotas = [];
|
||||||
profileFields = [];
|
|
||||||
|
|
||||||
constructor(private permissionService: PermissionService, private quotaService: QuotaService, private profileService : ProfileService) { }
|
constructor(private permissionService: PermissionService, private quotaService: QuotaService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.permissionService.permissions().subscribe((data: any) => {
|
this.permissionService.permissions().subscribe((data: any) => {
|
||||||
@ -25,9 +23,6 @@ export class InfoComponent implements OnInit {
|
|||||||
this.quotas = data;
|
this.quotas = data;
|
||||||
})
|
})
|
||||||
|
|
||||||
this.profileService.getAll().subscribe((data: any) => {
|
|
||||||
this.profileFields = data;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
<app-profilefields [profileFields]="profileFields" [edit]="true"></app-profilefields>
|
<app-profilefields [edit]="true"></app-profilefields>
|
@ -1,6 +1,4 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ProfileService} from '../../../services/profile.service';
|
|
||||||
import {I18nService} from '../../../services/i18n.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account-profile',
|
selector: 'app-account-profile',
|
||||||
@ -9,16 +7,11 @@ import {I18nService} from '../../../services/i18n.service';
|
|||||||
})
|
})
|
||||||
export class ProfileComponent implements OnInit {
|
export class ProfileComponent implements OnInit {
|
||||||
|
|
||||||
profileFields = [];
|
|
||||||
types = ["TEXT", "NUMBER", "DATE", "URL", "EMAIL", "BOOL"];
|
|
||||||
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
|
||||||
|
|
||||||
constructor(private profileService: ProfileService, private i18n: I18nService) {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.profileService.getAll().subscribe((data: any) => {
|
|
||||||
this.profileFields = data;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ export class SecurityComponent implements OnInit {
|
|||||||
passwordForm: FormGroup;
|
passwordForm: FormGroup;
|
||||||
statusForm: FormGroup;
|
statusForm: FormGroup;
|
||||||
@ViewChild('passwordFormDirective') private passwordFormDirective: NgForm;
|
@ViewChild('passwordFormDirective') private passwordFormDirective: NgForm;
|
||||||
@ViewChild('statusFormDirective') private statusFormDirective: NgForm;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
|
@ -34,9 +34,10 @@
|
|||||||
|
|
||||||
<mat-slide-toggle formControlName="primaryEmail" [(ngModel)]="model.primaryEmail"
|
<mat-slide-toggle formControlName="primaryEmail" [(ngModel)]="model.primaryEmail"
|
||||||
(change)="onPrimaryChange()">
|
(change)="onPrimaryChange()">
|
||||||
{{'email.primary' | i18n}} <mat-icon inline=true
|
{{'email.primary' | i18n}}
|
||||||
matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon>
|
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
|
<mat-icon #primaryHint="matTooltip" (click)="primaryHint.toggle()" inline="true"
|
||||||
|
matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon>
|
||||||
|
|
||||||
<mat-form-field *ngIf="model.primaryEmail">
|
<mat-form-field *ngIf="model.primaryEmail">
|
||||||
<input matInput type="email" placeholder="{{'email' | i18n}}" formControlName="email"
|
<input matInput type="email" placeholder="{{'email' | i18n}}" formControlName="email"
|
||||||
|
@ -23,9 +23,9 @@ var openpgp = require('openpgp');
|
|||||||
export class RegisterComponent implements OnInit {
|
export class RegisterComponent implements OnInit {
|
||||||
|
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
public missingToken: boolean;
|
missingToken: boolean;
|
||||||
public success: boolean;
|
success: boolean;
|
||||||
public working: boolean;
|
working: boolean;
|
||||||
items = [];
|
items = [];
|
||||||
permissions = [];
|
permissions = [];
|
||||||
quotas = [];
|
quotas = [];
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
i18n}}</a>
|
i18n}}</a>
|
||||||
<a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' |
|
<a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' |
|
||||||
i18n}}</a>
|
i18n}}</a>
|
||||||
<button mat-icon-button [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after">
|
<button mat-icon-button #privateKeyHelp="matTooltip" (click)="privateKeyHelp.toggle()" [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after">
|
||||||
<mat-icon>help</mat-icon>
|
<mat-icon>help</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<h3>{{'services' | i18n}}</h3>
|
<h3>{{'services' | i18n}}</h3>
|
||||||
|
|
||||||
|
<p *ngIf="!services || services.length == 0">{{'services.empty' | i18n}}</p>
|
||||||
|
|
||||||
<div fxLayout="row wrap" fxLayoutGap="16px grid">
|
<div fxLayout="row wrap" fxLayoutGap="16px grid">
|
||||||
<div fxFlex="33.33%" fxFlex.sm="50%" fxFlex.xs="100%" *ngFor="let service of services">
|
<div fxFlex="33.33%" fxFlex.sm="50%" fxFlex.xs="100%" *ngFor="let service of services">
|
||||||
<mat-card>
|
<mat-card>
|
||||||
|
@ -2,8 +2,14 @@
|
|||||||
<mat-progress-bar *ngIf="!success && !error" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="!success && !error" mode="indeterminate"></mat-progress-bar>
|
||||||
|
|
||||||
<div *ngIf="success">
|
<div *ngIf="success">
|
||||||
<h3>{{username}}</h3>
|
<h3>{{model.username}}</h3>
|
||||||
<app-profilefields [profileFields]="profileFields"></app-profilefields>
|
<app-profilefields [username]="model.username"></app-profilefields>
|
||||||
|
<div *ngIf="model.aliases && model.aliases.length > 0">
|
||||||
|
<h4>{{'user.aliases' | i18n}}</h4>
|
||||||
|
<mat-list>
|
||||||
|
<mat-list-item *ngFor="let alias of model.aliases">{{alias}}</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-card class="warn" *ngIf="error">
|
<mat-card class="warn" *ngIf="error">
|
||||||
|
@ -11,20 +11,19 @@ import {ProfileService} from '../../services/profile.service';
|
|||||||
export class UserComponent implements OnInit {
|
export class UserComponent implements OnInit {
|
||||||
|
|
||||||
username;
|
username;
|
||||||
profileFields = [];
|
model: any;
|
||||||
error = false;
|
error = false;
|
||||||
success = false;
|
success = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private profileService: ProfileService,
|
private profileService: ProfileService,
|
||||||
private router: Router,
|
|
||||||
private route: ActivatedRoute) {}
|
private route: ActivatedRoute) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.username = this.route.snapshot.paramMap.get('username');
|
this.username = this.route.snapshot.paramMap.get('username');
|
||||||
this.profileService.getAllForUser(this.username).subscribe((data: any) => {
|
this.profileService.getAllForUser(this.username).subscribe((data: any) => {
|
||||||
this.profileFields = data;
|
|
||||||
this.success = true;
|
this.success = true;
|
||||||
|
this.model = data;
|
||||||
}, error => {
|
}, error => {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
})
|
})
|
||||||
|
@ -67,6 +67,7 @@ export class I18nService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getInternal(key, args: string[], from): string {
|
getInternal(key, args: string[], from): string {
|
||||||
|
key += '';
|
||||||
if(!from) {
|
if(!from) {
|
||||||
return key;
|
return key;
|
||||||
} else if(from[key]) {
|
} else if(from[key]) {
|
||||||
|
30
src/app/services/useralias.service.ts
Normal file
30
src/app/services/useralias.service.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class UserAliasService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.http.get(environment.apiUrl + "/users/aliases");
|
||||||
|
}
|
||||||
|
|
||||||
|
create(alias) {
|
||||||
|
return this.http.post(environment.apiUrl + "/users/aliases", alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(alias) {
|
||||||
|
return this.http.patch(environment.apiUrl + "/users/aliases", alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id) {
|
||||||
|
return this.http.delete(environment.apiUrl + "/users/aliases/" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,35 +4,52 @@
|
|||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput type="text" min="3" [(ngModel)]="profileField.name" formControlName="name"
|
<input matInput type="text" min="3" [(ngModel)]="profileField.name" formControlName="name"
|
||||||
placeholder="{{'profileField.name' | i18n}}">
|
placeholder="{{'profileField.name' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.name' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [(ngModel)]="profileField.type" formControlName="type" placeholder="{{'profileField.type' | i18n}}">
|
<mat-select [(ngModel)]="profileField.type" formControlName="type" placeholder="{{'profileField.type' | i18n}}">
|
||||||
<mat-option *ngFor="let type of types" [value]="type">
|
<mat-option *ngFor="let type of types" [value]="type">
|
||||||
{{'profileField.type.' + type | i18n}}
|
{{'profileField.type.' + type | i18n}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.type' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<div [ngSwitch]="profileField.type">
|
<div [ngSwitch]="profileField.type">
|
||||||
<mat-form-field *ngSwitchCase="'TEXT'">
|
<mat-form-field *ngSwitchCase="'TEXT'">
|
||||||
<input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value"
|
<input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value"
|
||||||
placeholder="{{'profileField.value' | i18n}}">
|
placeholder="{{'profileField.value' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.TEXT' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field *ngSwitchCase="'DATE'">
|
<mat-form-field *ngSwitchCase="'DATE'">
|
||||||
<input matInput [matDatepicker]="picker" [(ngModel)]="profileField.value" formControlName="value"
|
<input matInput [matDatepicker]="picker" [(ngModel)]="profileField.value" formControlName="value"
|
||||||
placeholder="{{'profileField.value' | i18n}}">
|
placeholder="{{'profileField.value' | i18n}}">
|
||||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||||
<mat-datepicker #picker></mat-datepicker>
|
<mat-datepicker #picker></mat-datepicker>
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.DATE' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field *ngSwitchCase="'URL'">
|
<mat-form-field *ngSwitchCase="'URL'">
|
||||||
<input matInput type="url" [(ngModel)]="profileField.value" formControlName="value"
|
<input matInput type="url" [(ngModel)]="profileField.value" formControlName="value"
|
||||||
placeholder="{{'profileField.value' | i18n}}">
|
placeholder="{{'profileField.value' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.URL' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field *ngSwitchCase="'EMAIL'">
|
<mat-form-field *ngSwitchCase="'EMAIL'">
|
||||||
<input matInput type="email" [(ngModel)]="profileField.value" formControlName="value"
|
<input matInput type="email" [(ngModel)]="profileField.value" formControlName="value"
|
||||||
placeholder="{{'profileField.value' | i18n}}">
|
placeholder="{{'profileField.value' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.EMAIL' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-slide-toggle *ngSwitchCase="'BOOL'" (change)="booleanChange(profileField)"
|
<mat-slide-toggle *ngSwitchCase="'BOOL'" (change)="booleanChange(profileField)"
|
||||||
[checked]="profileField.value == 'true'">
|
[checked]="profileField.value == 'true'">
|
||||||
@ -41,31 +58,49 @@
|
|||||||
<mat-form-field *ngSwitchCase="'NUMBER'">
|
<mat-form-field *ngSwitchCase="'NUMBER'">
|
||||||
<input matInput type="number" [(ngModel)]="profileField.value" formControlName="value"
|
<input matInput type="number" [(ngModel)]="profileField.value" formControlName="value"
|
||||||
placeholder="{{'profileField.value' | i18n}}">
|
placeholder="{{'profileField.value' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.NUMBER' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field *ngSwitchCase="'BLOB'">
|
<mat-form-field *ngSwitchCase="'BLOB'">
|
||||||
<textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"
|
<textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"
|
||||||
placeholder="{{'profileField.value' | i18n}}"></textarea>
|
placeholder="{{'profileField.value' | i18n}}"></textarea>
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.BLOB' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [(ngModel)]="profileField.visibility" formControlName="visibility"
|
<mat-select [(ngModel)]="profileField.visibility" formControlName="visibility"
|
||||||
placeholder="{{'profileField.visibility' | i18n}}">
|
placeholder="{{'visibility' | i18n}}">
|
||||||
<mat-option *ngFor="let visibility of visibilities" [value]="visibility">
|
<mat-option *ngFor="let visibility of visibilities" [value]="visibility">
|
||||||
{{'profileField.visibility.' + visibility | i18n}}
|
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
visibility | i18n}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|
||||||
|
<mat-select-trigger>
|
||||||
|
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||||
|
profileField.visibility | i18n}}
|
||||||
|
</mat-select-trigger>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.visibility' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input matInput type="number" min="0" [(ngModel)]="profileField.index" formControlName="index"
|
<input matInput type="number" min="0" [(ngModel)]="profileField.index" formControlName="index"
|
||||||
placeholder="{{'profileField.index' | i18n}}">
|
placeholder="{{'profileField.index' | i18n}}">
|
||||||
|
<mat-error>
|
||||||
|
{{'profileField.error.index' | i18n}}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
|
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
|
||||||
<button [disabled]="form.invalid" mat-raised-button [mat-dialog-close]="profileField" color="accent">{{'save' |
|
<button [disabled]="form.invalid" mat-raised-button (click)="save(profileField)" color="accent">{{'save' |
|
||||||
i18n}}</button>
|
i18n}}</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
@ -24,14 +24,14 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="visibility" *ngIf="edit">
|
<ng-container matColumnDef="visibility" *ngIf="edit">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'profileField.visibility' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let profileField"> {{'profileField.visibility.' + profileField.visibility | i18n}}
|
<td mat-cell *matCellDef="let profileField"> <mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + profileField.visibility | i18n}}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="edit" *ngIf="edit">
|
<ng-container matColumnDef="edit" *ngIf="edit">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'profileField.edit' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef class="text-right"> {{'profileField.edit' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let profileField">
|
<td mat-cell *matCellDef="let profileField" class="text-right">
|
||||||
<a mat-icon-button>
|
<a mat-icon-button>
|
||||||
<mat-icon (click)="openEdit(profileField)">edit</mat-icon>
|
<mat-icon (click)="openEdit(profileField)">edit</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
@ -39,8 +39,8 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="delete" *ngIf="edit">
|
<ng-container matColumnDef="delete" *ngIf="edit">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{'profileField.delete' | i18n}} </th>
|
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'profileField.delete' | i18n}} </th>
|
||||||
<td mat-cell *matCellDef="let profileField">
|
<td mat-cell *matCellDef="let profileField" class="text-right">
|
||||||
<a mat-icon-button>
|
<a mat-icon-button>
|
||||||
<mat-icon (click)="confirmDelete(profileField)">delete</mat-icon>
|
<mat-icon (click)="confirmDelete(profileField)">delete</mat-icon>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-header-cell,
|
||||||
|
.mat-cell {
|
||||||
|
&.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
display: flex;
|
||||||
|
padding: 21px 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
@ -2,9 +2,9 @@ import {Component, OnInit, Inject, Input} from '@angular/core';
|
|||||||
import {Sort} from '@angular/material/sort';
|
import {Sort} from '@angular/material/sort';
|
||||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||||
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
||||||
|
import {ConfirmDialog} from '../confirm/confirm.component';
|
||||||
import {I18nService} from '../../services/i18n.service';
|
import {I18nService} from '../../services/i18n.service';
|
||||||
import {ProfileService} from '../../services/profile.service';
|
import {ProfileService} from '../../services/profile.service';
|
||||||
import {ConfirmDialog} from '../confirm/confirm.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profilefields',
|
selector: 'app-profilefields',
|
||||||
@ -13,9 +13,10 @@ import {ConfirmDialog} from '../confirm/confirm.component';
|
|||||||
})
|
})
|
||||||
export class ProfileFieldsComponent implements OnInit {
|
export class ProfileFieldsComponent implements OnInit {
|
||||||
|
|
||||||
@Input() profileFields: Array<any>;
|
@Input() username;
|
||||||
@Input() edit;
|
@Input() edit;
|
||||||
profileFieldColumns = ["name", "value"];
|
profileFieldColumns = ["name", "value"];
|
||||||
|
profileFields: Array<any> = [];
|
||||||
|
|
||||||
constructor(private i18n: I18nService, private profileService: ProfileService, public dialog: MatDialog) {}
|
constructor(private i18n: I18nService, private profileService: ProfileService, public dialog: MatDialog) {}
|
||||||
|
|
||||||
@ -25,6 +26,20 @@ export class ProfileFieldsComponent implements OnInit {
|
|||||||
this.profileFieldColumns.push("edit");
|
this.profileFieldColumns.push("edit");
|
||||||
this.profileFieldColumns.push("delete");
|
this.profileFieldColumns.push("delete");
|
||||||
}
|
}
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if(this.username) {
|
||||||
|
this.profileService.getAllForUser(this.username).subscribe((data: any) => {
|
||||||
|
this.profileFields = data.profileFields;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.profileService.getAll().subscribe((data: any) => {
|
||||||
|
this.profileFields = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sortData(sort: Sort) {
|
sortData(sort: Sort) {
|
||||||
@ -40,6 +55,7 @@ export class ProfileFieldsComponent implements OnInit {
|
|||||||
case 'name': return this.compare(this.i18n.get('profileField.name.' + a.name, []), this.i18n.get('profileField.name.' + b.name, []), isAsc);
|
case 'name': return this.compare(this.i18n.get('profileField.name.' + a.name, []), this.i18n.get('profileField.name.' + b.name, []), isAsc);
|
||||||
case 'value': return this.compare(a.value, b.value, isAsc);
|
case 'value': return this.compare(a.value, b.value, isAsc);
|
||||||
case 'index': return this.compare(a.index, b.index, isAsc);
|
case 'index': return this.compare(a.index, b.index, isAsc);
|
||||||
|
case 'visibility': return this.compare(this.i18n.get('visibility.' + a.visibility, []), this.i18n.get('visibility.' + b.visibility, []), isAsc);
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -85,8 +101,7 @@ export class ProfileFieldsComponent implements OnInit {
|
|||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if(result) {
|
if(result) {
|
||||||
this.profileService.delete(profileField.name).subscribe((result: any) => {
|
this.profileService.delete(profileField.name).subscribe((result: any) => {
|
||||||
this.profileFields.splice(this.profileFields.indexOf(profileField), 1);
|
this.update();
|
||||||
this.profileFields = [...this.profileFields];
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -100,10 +115,7 @@ export class ProfileFieldsComponent implements OnInit {
|
|||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if(result) {
|
if(result) {
|
||||||
this.profileService.createOrUpdate(result).subscribe((result: any) => {
|
this.update();
|
||||||
this.profileFields.push(result);
|
|
||||||
this.profileFields = [...this.profileFields];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -132,6 +144,7 @@ export class ProfileFieldDialog {
|
|||||||
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private profileService: ProfileService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
public dialogRef: MatDialogRef<ProfileFieldDialog>,
|
public dialogRef: MatDialogRef<ProfileFieldDialog>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
@ -157,6 +170,25 @@ export class ProfileFieldDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
save(profileField) {
|
||||||
|
this.profileService.createOrUpdate(profileField).subscribe((result: any) => {
|
||||||
|
this.dialogRef.close(profileField);
|
||||||
|
}, (error) => {
|
||||||
|
if(error.status == 409) {
|
||||||
|
let errors = {};
|
||||||
|
for(let code of error.error) {
|
||||||
|
errors[code.field] = errors[code.field] || {};
|
||||||
|
errors[code.field][code.code] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let code in errors) {
|
||||||
|
this.form.get(code).setErrors(errors[code]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
"email": {
|
"email": {
|
||||||
".": "E-Mail Adresse",
|
".": "E-Mail Adresse",
|
||||||
"invalid": "ungültige E-Mail Adresse",
|
"invalid": "ungültige E-Mail Adresse",
|
||||||
"primary": "primäre E-Mail Adresse"
|
"primary": {
|
||||||
|
".": "primäre E-Mail Adresse",
|
||||||
|
"hint": "Eine primäre E-Mail Adresse dient dazu eine andere Kontaktmöglichkeit als deine we.bstly Adresse anzugeben."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"greet": "Hallo {0}",
|
"greet": "Hallo {0}",
|
||||||
"help": "Hilfe",
|
"help": "Hilfe",
|
||||||
@ -91,6 +94,32 @@
|
|||||||
"create": "Neues Profilfeld hinzufügen",
|
"create": "Neues Profilfeld hinzufügen",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
|
"error": {
|
||||||
|
"BLOB": {
|
||||||
|
".": "Kein gültiger Binärblob"
|
||||||
|
},
|
||||||
|
"BOOL": {
|
||||||
|
".": "Kein gültiger Boolean"
|
||||||
|
},
|
||||||
|
"DATE": {
|
||||||
|
".": "Kein gültiges Datum"
|
||||||
|
},
|
||||||
|
"EMAIL": {
|
||||||
|
".": "Keine gültige E-Mail Adresse"
|
||||||
|
},
|
||||||
|
"name": "Name zu kurz oder ungültig",
|
||||||
|
"NUMBER": {
|
||||||
|
".": "Keine gültige Nummer"
|
||||||
|
},
|
||||||
|
"TEXT": {
|
||||||
|
".": "Textfeld zu lang"
|
||||||
|
},
|
||||||
|
"type": "Ungültiger Typ für dieses Profilfeld",
|
||||||
|
"URL": {
|
||||||
|
".": "Keine gültige URL"
|
||||||
|
},
|
||||||
|
"visibility": "Keine gültige Sichtbarkeit für Profilfeld"
|
||||||
|
},
|
||||||
"index": "Index",
|
"index": "Index",
|
||||||
"name": {
|
"name": {
|
||||||
".": "Key",
|
".": "Key",
|
||||||
@ -126,19 +155,7 @@
|
|||||||
".": "URL"
|
".": "URL"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"value": "Wert",
|
"value": "Wert"
|
||||||
"visibility": {
|
|
||||||
".": "Sichtbarkeit",
|
|
||||||
"PRIVATE": {
|
|
||||||
".": "Privat"
|
|
||||||
},
|
|
||||||
"PROTECTED": {
|
|
||||||
".": "Geschützt"
|
|
||||||
},
|
|
||||||
"PUBLIC": {
|
|
||||||
".": "Öffentlich"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"quotas": {
|
"quotas": {
|
||||||
".": "Quotas",
|
".": "Quotas",
|
||||||
@ -207,6 +224,13 @@
|
|||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
".": "Dienste",
|
".": "Dienste",
|
||||||
|
"alias_creation": {
|
||||||
|
"icon": "alternate_email",
|
||||||
|
"subtitle": "Anlegen von alternativen Namen",
|
||||||
|
"text": "Du kannst zusätzlich zu deinem Usernamen noch alternative Namen anlegen.",
|
||||||
|
"title": "Alternative Namen"
|
||||||
|
},
|
||||||
|
"empty": "Du hast aktuell keine Berechtigungen zur Nutzung von Diensten.",
|
||||||
"gitea": {
|
"gitea": {
|
||||||
"icon": "code",
|
"icon": "code",
|
||||||
"subtitle": "Git-Repositories",
|
"subtitle": "Git-Repositories",
|
||||||
@ -284,6 +308,17 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
".": "User",
|
".": "User",
|
||||||
|
"aliases": {
|
||||||
|
".": "Alternative Namen",
|
||||||
|
"alias": "Alternativer Name",
|
||||||
|
"confirmDelete": "Möchtest du wirklich den alternativen Namen '{0}' löschen?",
|
||||||
|
"create": "Alternativen Namen anlegen",
|
||||||
|
"delete": "Löschen",
|
||||||
|
"error": "Dieser alternative Name ist leider nicht zulässig.",
|
||||||
|
"info": "Du kannst hier alternative Namen erstellen. Die Anzahl wird über eine Quota begrenzt.",
|
||||||
|
"left": "Du kannst noch {0} alternative(n) Namen anlegen.",
|
||||||
|
"noQuota": "Deine Quota für alternative Namen ist leider aufgebraucht."
|
||||||
|
},
|
||||||
"unavailable": {
|
"unavailable": {
|
||||||
".": "Zugriff verweigert",
|
".": "Zugriff verweigert",
|
||||||
"text": "Dieser Benutzer existiert nicht oder du hast keine Berechtigungen, um auf das Profil zuzugreifen."
|
"text": "Dieser Benutzer existiert nicht oder du hast keine Berechtigungen, um auf das Profil zuzugreifen."
|
||||||
@ -294,6 +329,21 @@
|
|||||||
"error": "Bitte wähle einen anderen Usernamen aus.",
|
"error": "Bitte wähle einen anderen Usernamen aus.",
|
||||||
"missing": "Bitte gebe einen Usernamen an."
|
"missing": "Bitte gebe einen Usernamen an."
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
".": "Sichtbarkeit",
|
||||||
|
"PRIVATE": {
|
||||||
|
".": "Privat",
|
||||||
|
"icon" : "lock"
|
||||||
|
},
|
||||||
|
"PROTECTED": {
|
||||||
|
".": "Geschützt",
|
||||||
|
"icon" : "shield"
|
||||||
|
},
|
||||||
|
"PUBLIC": {
|
||||||
|
".": "Öffentlich",
|
||||||
|
"icon" : "public"
|
||||||
|
}
|
||||||
|
},
|
||||||
"voucher": {
|
"voucher": {
|
||||||
".": "Gutscheincode",
|
".": "Gutscheincode",
|
||||||
"code": "Code",
|
"code": "Code",
|
||||||
|
@ -7,7 +7,10 @@
|
|||||||
"email": {
|
"email": {
|
||||||
".": "Email address",
|
".": "Email address",
|
||||||
"invalid": "invalid email address",
|
"invalid": "invalid email address",
|
||||||
"primary": "primary email address"
|
"primary": {
|
||||||
|
".": "primary email address",
|
||||||
|
"hint": "A primary email address is used for contact you instead of you we.bstly address."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"greet": "Hello {0}",
|
"greet": "Hello {0}",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
@ -91,6 +94,32 @@
|
|||||||
"create": "Add new profile field",
|
"create": "Add new profile field",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
|
"error": {
|
||||||
|
"BLOB": {
|
||||||
|
".": "Invalid binary blob"
|
||||||
|
},
|
||||||
|
"BOOL": {
|
||||||
|
".": "Invalid boolean"
|
||||||
|
},
|
||||||
|
"DATE": {
|
||||||
|
".": "Invalid date"
|
||||||
|
},
|
||||||
|
"EMAIL": {
|
||||||
|
".": "Invalid email address"
|
||||||
|
},
|
||||||
|
"name": "name invalid or too short",
|
||||||
|
"NUMBER": {
|
||||||
|
".": "Invalid numeric"
|
||||||
|
},
|
||||||
|
"TEXT": {
|
||||||
|
".": "Text too long"
|
||||||
|
},
|
||||||
|
"type": "Invalid type for this profile field",
|
||||||
|
"URL": {
|
||||||
|
".": "Invalid URL"
|
||||||
|
},
|
||||||
|
"visibility": "Invalid visibility for profile field"
|
||||||
|
},
|
||||||
"index": "Index",
|
"index": "Index",
|
||||||
"name": {
|
"name": {
|
||||||
".": "Key",
|
".": "Key",
|
||||||
@ -126,19 +155,7 @@
|
|||||||
".": "URL"
|
".": "URL"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"value": "Value",
|
"value": "Value"
|
||||||
"visibility": {
|
|
||||||
".": "Visibility",
|
|
||||||
"PRIVATE": {
|
|
||||||
".": "Private"
|
|
||||||
},
|
|
||||||
"PROTECTED": {
|
|
||||||
".": "Protected"
|
|
||||||
},
|
|
||||||
"PUBLIC": {
|
|
||||||
".": "Public"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"quotas": {
|
"quotas": {
|
||||||
".": "Quotas",
|
".": "Quotas",
|
||||||
@ -207,6 +224,13 @@
|
|||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
".": "Services",
|
".": "Services",
|
||||||
|
"alias_creation": {
|
||||||
|
"icon": "alternate_email",
|
||||||
|
"subtitle": "Creation of Aliases",
|
||||||
|
"text": "You can add additional aliases besides your username.",
|
||||||
|
"title": "Aliases"
|
||||||
|
},
|
||||||
|
"empty" : "You have insufficient permissions to use any services.",
|
||||||
"gitea": {
|
"gitea": {
|
||||||
"icon": "code",
|
"icon": "code",
|
||||||
"subtitle": "Git-Repositories",
|
"subtitle": "Git-Repositories",
|
||||||
@ -284,6 +308,17 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
".": "User",
|
".": "User",
|
||||||
|
"aliases": {
|
||||||
|
".": "Aliases",
|
||||||
|
"alias": "Alias",
|
||||||
|
"confirmDelete": "Are you sure you want to delete your alias '{0}'?",
|
||||||
|
"create": "Add Alias",
|
||||||
|
"delete": "Delete",
|
||||||
|
"error": "This alias is invalid.",
|
||||||
|
"info": "You can add Aliases here. The number is limited due to a quota.",
|
||||||
|
"left": "You have {0} Alias(es) left.",
|
||||||
|
"noQuota": "Your quota for Aliases is depleted."
|
||||||
|
},
|
||||||
"unavailable": {
|
"unavailable": {
|
||||||
".": "Access denied",
|
".": "Access denied",
|
||||||
"text": "The provided user does not exists or you have insufficient privileges to access profile."
|
"text": "The provided user does not exists or you have insufficient privileges to access profile."
|
||||||
@ -294,6 +329,21 @@
|
|||||||
"error": "Please choose a different username.",
|
"error": "Please choose a different username.",
|
||||||
"missing": "Please enter a valid username."
|
"missing": "Please enter a valid username."
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
".": "Visibility",
|
||||||
|
"PRIVATE": {
|
||||||
|
".": "Private",
|
||||||
|
"icon" : "lock"
|
||||||
|
},
|
||||||
|
"PROTECTED": {
|
||||||
|
".": "Protected",
|
||||||
|
"icon" : "shield"
|
||||||
|
},
|
||||||
|
"PUBLIC": {
|
||||||
|
".": "Public",
|
||||||
|
"icon" : "public"
|
||||||
|
}
|
||||||
|
},
|
||||||
"voucher": {
|
"voucher": {
|
||||||
".": "Voucher",
|
".": "Voucher",
|
||||||
"code": "Code",
|
"code": "Code",
|
||||||
|
@ -194,6 +194,16 @@ mat-sidenav-container {
|
|||||||
color: $warn;
|
color: $warn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.align-right{
|
||||||
|
display: flex;
|
||||||
|
padding: 21px 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-tooltip-trigger {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
mat-card.warn,
|
mat-card.warn,
|
||||||
mat-card.accent {
|
mat-card.accent {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user