fix(console): move org domains into settings page of the organization (#6612)

* fix: hide domains settings for unauthorized users

* refine sidenav object mapping

* move domains to settings

* change docs

* set anchor to list element

* remove canwrite check in ngif

---------

Co-authored-by: Miguel A. C <doncicuto@gmail.com>
This commit is contained in:
Max Peintner
2023-09-29 09:31:35 +02:00
committed by GitHub
parent eb31c2a3be
commit d01f4d229f
42 changed files with 161 additions and 202 deletions

View File

@@ -158,14 +158,6 @@ const routes: Routes = [
requiresAll: true,
},
},
{
path: 'domains',
loadChildren: () => import('./pages/domains/domains.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['org.read'],
},
},
{
path: 'org-settings',
loadChildren: () => import('./pages/org-settings/org-settings.module'),

View File

@@ -0,0 +1,67 @@
<ng-container *ngIf="['org.write$'] | hasRole as canwrite$">
<div class="domain-top-view">
<div>
<div class="domain-title-row">
<h2>{{ 'ORG.DOMAINS.TITLE' | translate }}</h2>
<a
mat-icon-button
href="https://zitadel.com/docs/guides/manage/console/organizations#how-zitadel-handles-usernames"
rel="noreferrer"
target="_blank"
>
<i class="las la-info-circle"></i>
</a>
</div>
<p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}</p>
</div>
<span class="fill-space"></span>
<button
[disabled]="(canwrite$ | async) === false"
matTooltip="Add domain"
mat-raised-button
color="primary"
class="cnsl-action-button"
(click)="addNewDomain()"
>
<mat-icon>add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys (actionTriggered)="addNewDomain()"> </cnsl-action-keys>
</button>
</div>
<cnsl-card *ngFor="let domain of domains" class="domain-card">
<div class="domain">
<span class="title">{{ domain.domainName }}</span>
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
<a
*ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)"
class="primaryset"
(click)="setPrimary(domain)"
>{{ 'ORG.DOMAINS.SETPRIMARY' | translate }}</a
>
<span class="fill-space"></span>
<button
mat-icon-button
[disabled]="(canwrite$ | async) === false || domain.isVerified"
*ngIf="verifyOrgDomains"
(click)="verifyDomain(domain)"
>
<i class="las la-pen"></i>
</button>
<button
class="domain-rem-button"
[disabled]="(canwrite$ | async) === false || domain.isPrimary"
matTooltip="Remove domain"
color="warn"
mat-icon-button
(click)="removeDomain(domain.domainName)"
>
<i class="las la-trash"></i>
</button>
</div>
</cnsl-card>
</ng-container>

View File

@@ -1,7 +1,6 @@
.domain-top-view {
display: flex;
align-items: center;
padding-top: 2rem;
.domain-title-row {
display: flex;

View File

@@ -14,13 +14,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { AddDomainDialogModule } from './add-domain-dialog/add-domain-dialog.module';
import { DomainVerificationComponent } from './domain-verification/domain-verification.component';
import { DomainsRoutingModule } from './domains-routing.module';
import { DomainsComponent } from './domains.component';
@NgModule({
declarations: [DomainsComponent, DomainVerificationComponent],
imports: [
DomainsRoutingModule,
AddDomainDialogModule,
CommonModule,
MatIconModule,
@@ -36,5 +34,6 @@ import { DomainsComponent } from './domains.component';
InfoSectionModule,
MatProgressSpinnerModule,
],
exports: [DomainsComponent],
})
export default class DomainsModule {}

View File

@@ -177,17 +177,6 @@
</a>
</ng-template>
<ng-template cnslHasRole [hasRole]="['org.read']">
<a
class="nav-item"
[routerLinkActive]="['active']"
[routerLink]="['/domains']"
[routerLinkActiveOptions]="{ exact: true }"
>
<span class="label">{{ 'MENU.DOMAINS' | translate }}</span>
</a>
</ng-template>
<ng-template cnslHasRole [hasRole]="['org.read']">
<a
class="nav-item"

View File

@@ -5,7 +5,6 @@
<h2>{{ 'POLICY.DOMAIN_POLICY.TITLE' | translate }}</h2>
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
<!-- <ng-template cnslHasRole [hasRole]="['domain.policy.delete']"> -->
<button
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}"
@@ -26,7 +25,6 @@
>
{{ 'POLICY.RESET' | translate }}
</button>
<!-- </ng-template> -->
<div class="domain-policy-content" *ngIf="domainData">
<div class="row">

View File

@@ -18,6 +18,9 @@
<ng-container *ngIf="currentSetting === 'login'">
<cnsl-login-policy [serviceType]="serviceType"></cnsl-login-policy>
</ng-container>
<ng-container *ngIf="currentSetting === 'verified_domains'">
<cnsl-domains></cnsl-domains>
</ng-container>
<ng-container *ngIf="currentSetting === 'domain' && (['iam.policy.write'] | hasRole | async) === true">
<cnsl-domain-policy [serviceType]="serviceType"></cnsl-domain-policy>
</ng-container>

View File

@@ -5,6 +5,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../card/card.module';
import DomainsModule from '../domains/domains.module';
import { DomainPolicyModule } from '../policies/domain-policy/domain-policy.module';
import { GeneralSettingsModule } from '../policies/general-settings/general-settings.module';
import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module';
@@ -40,6 +41,7 @@ import { SettingsListComponent } from './settings-list.component';
PrivacyPolicyModule,
MessageTextsPolicyModule,
SecurityPolicyModule,
DomainsModule,
LoginTextsPolicyModule,
DomainPolicyModule,
TranslateModule,

View File

@@ -43,6 +43,15 @@ export const LOGIN: SidenavSetting = {
},
};
export const VERIFIED_DOMAINS: SidenavSetting = {
id: 'verified_domains',
i18nKey: 'SETTINGS.LIST.VERIFIED_DOMAINS',
groupI18nKey: 'SETTINGS.GROUPS.DOMAIN',
requiredRoles: {
[PolicyComponentServiceType.MGMT]: ['org.read'],
},
};
export const DOMAIN: SidenavSetting = {
id: 'domain',
i18nKey: 'SETTINGS.LIST.DOMAIN',

View File

@@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DomainsComponent } from './domains.component';
const routes: Routes = [
{
path: '',
component: DomainsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class DomainsRoutingModule {}

View File

@@ -1,69 +0,0 @@
<div class="max-width-container">
<ng-container *ngIf="['org.write$'] | hasRole as canwrite$">
<div class="domain-top-view">
<div>
<div class="domain-title-row">
<h1>{{ 'ORG.DOMAINS.TITLE' | translate }}</h1>
<a
mat-icon-button
href="https://zitadel.com/docs/guides/manage/console/organizations#how-zitadel-handles-usernames"
rel="noreferrer"
target="_blank"
>
<i class="las la-info-circle"></i>
</a>
</div>
<p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}</p>
</div>
<span class="fill-space"></span>
<button
[disabled]="(canwrite$ | async) === false"
matTooltip="Add domain"
mat-raised-button
color="primary"
class="cnsl-action-button"
(click)="addNewDomain()"
>
<mat-icon>add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys (actionTriggered)="addNewDomain()"> </cnsl-action-keys>
</button>
</div>
<cnsl-card *ngFor="let domain of domains" class="domain-card">
<div class="domain">
<span class="title">{{ domain.domainName }}</span>
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
<a
*ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)"
class="primaryset"
(click)="setPrimary(domain)"
>{{ 'ORG.DOMAINS.SETPRIMARY' | translate }}</a
>
<span class="fill-space"></span>
<button
mat-icon-button
[disabled]="(canwrite$ | async) === false || domain.isVerified"
*ngIf="(canwrite$ | async) && verifyOrgDomains"
(click)="verifyDomain(domain)"
>
<i class="las la-pen"></i>
</button>
<button
class="domain-rem-button"
[disabled]="(canwrite$ | async) === false || domain.isPrimary"
matTooltip="Remove domain"
color="warn"
mat-icon-button
(click)="removeDomain(domain.domainName)"
>
<i class="las la-trash"></i>
</button>
</div>
</cnsl-card>
</ng-container>
</div>

View File

@@ -8,9 +8,11 @@
</div>
<span class="fill-space"></span>
</div>
<cnsl-settings-list
[selectedId]="id"
[serviceType]="PolicyComponentServiceType.ADMIN"
[settingsList]="settingsList"
></cnsl-settings-list>
<ng-container *ngIf="settingsList | async as list">
<cnsl-settings-list
[selectedId]="id"
[serviceType]="PolicyComponentServiceType.ADMIN"
[settingsList]="list"
></cnsl-settings-list>
</ng-container>
</div>

View File

@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Subject, takeUntil } from 'rxjs';
import { Observable, of, Subject, takeUntil } from 'rxjs';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
@@ -22,7 +22,6 @@ import {
SECRETS,
SECURITY,
} from '../../modules/settings-list/settings';
import { checkSettingsPermissions } from '../org-settings/org-settings.component';
@Component({
selector: 'cnsl-instance-settings',
@@ -55,7 +54,7 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
SECURITY,
];
public settingsList: SidenavSetting[] = [];
public settingsList: Observable<SidenavSetting[]> = of([]);
private destroy$: Subject<void> = new Subject();
constructor(
@@ -81,13 +80,7 @@ export class InstanceSettingsComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
checkSettingsPermissions(this.defaultSettingsList, PolicyComponentServiceType.ADMIN, this.authService).subscribe(
(allowed) => {
this.settingsList = this.defaultSettingsList.filter((setting, index) => {
return allowed[index];
});
},
);
this.settingsList = this.authService.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.admin);
}
ngOnDestroy(): void {

View File

@@ -8,9 +8,11 @@
</div>
<span class="fill-space"></span>
</div>
<cnsl-settings-list
[selectedId]="id"
[serviceType]="PolicyComponentServiceType.MGMT"
[settingsList]="settingsList"
></cnsl-settings-list>
<ng-container *ngIf="settingsList | async as list">
<cnsl-settings-list
[selectedId]="id"
[serviceType]="PolicyComponentServiceType.MGMT"
[settingsList]="list"
></cnsl-settings-list>
</ng-container>
</div>

View File

@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { forkJoin, of, take } from 'rxjs';
import { Observable, of, take } from 'rxjs';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
@@ -17,6 +17,7 @@ import {
MESSAGETEXTS,
NOTIFICATION_POLICY,
PRIVACYPOLICY,
VERIFIED_DOMAINS,
} from '../../modules/settings-list/settings';
@Component({
@@ -34,6 +35,7 @@ export class OrgSettingsComponent implements OnInit {
COMPLEXITY,
LOCKOUT,
NOTIFICATION_POLICY,
VERIFIED_DOMAINS,
DOMAIN,
BRANDING,
MESSAGETEXTS,
@@ -41,7 +43,7 @@ export class OrgSettingsComponent implements OnInit {
PRIVACYPOLICY,
];
public settingsList: SidenavSetting[] = [];
public settingsList: Observable<Array<SidenavSetting>> = of([]);
constructor(
breadcrumbService: BreadcrumbService,
@@ -65,40 +67,8 @@ export class OrgSettingsComponent implements OnInit {
}
ngOnInit(): void {
checkSettingsPermissions(this.defaultSettingsList, PolicyComponentServiceType.MGMT, this.authService).subscribe(
(allowed) => {
this.settingsList = this.defaultSettingsList.filter((setting, index) => {
return allowed[index];
});
},
);
this.settingsList = this.authService
.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.mgmt)
.pipe(take(1));
}
}
// Return a Observables<boolean>[] that will wait till all service calls are finished to then check if user is allowed to see a setting
export function checkSettingsPermissions(settings: SidenavSetting[], serviceType: string, authService: GrpcAuthService) {
return forkJoin(
settings
.filter((setting) => {
if (serviceType === PolicyComponentServiceType.ADMIN) {
return setting.requiredRoles && setting.requiredRoles.admin;
} else {
return setting.requiredRoles && setting.requiredRoles.mgmt;
}
})
.map((setting) => {
if (!setting.requiredRoles) {
return of(true);
}
if (!setting.requiredRoles.mgmt) {
return of(true);
}
if (setting.requiredRoles.mgmt) {
return authService.isAllowed(setting.requiredRoles.mgmt).pipe(take(1));
}
return of(false);
}),
);
}

View File

@@ -309,10 +309,7 @@ export class GrpcAuthService {
filter(([hL, p]) => {
return hL === true && !!p.length;
}),
map(([_, zroles]) => {
const what = this.hasRoles(zroles, roles, requiresAll);
return what;
}),
map(([_, zroles]) => this.hasRoles(zroles, roles, requiresAll)),
distinctUntilChanged(),
);
} else {
@@ -320,6 +317,31 @@ export class GrpcAuthService {
}
}
/**
* filters objects based on roles
* @param objects array of objects
* @param mapper mapping function which maps to a string[] or Regexp[] of roles
* @param requiresAll wheter all, or just a single roles is required to fulfill
*/
public isAllowedMapper<T>(
objects: T[],
mapper: (attr: any) => string[] | RegExp[],
requiresAll: boolean = false,
): Observable<T[]> {
return this.fetchedZitadelPermissions.pipe(
withLatestFrom(this.zitadelPermissions),
filter(([hL, p]) => {
return hL === true && !!p.length;
}),
map(([_, zroles]) => {
return objects.filter((obj) => {
const roles = mapper(obj);
return this.hasRoles(zroles, roles, requiresAll);
});
}),
);
}
/**
* returns true if user has one of the provided roles
* @param userRoles roles of the user

View File

@@ -95,7 +95,6 @@
"EVENTS": "Събития",
"FAILEDEVENTS": "Неуспешни събития",
"ORGANIZATION": "Организация",
"DOMAINS": "Домейни",
"PROJECT": "Проекти",
"PROJECTOVERVIEW": "Преглед",
"PROJECTGRANTS": "Грантове",
@@ -947,7 +946,7 @@
},
"DOMAINS": {
"NEW": "Добавете домейн",
"TITLE": "Домейни",
"TITLE": "Проверени домейни",
"DESCRIPTION": "Конфигурирайте вашите домейни. ",
"SETPRIMARY": "Задайте като основен",
"DELETE": {
@@ -1017,6 +1016,7 @@
"NOTIFICATIONS_DESC": "Настройки за SMTP и SMS",
"MESSAGETEXTS": "Текстове на съобщения",
"IDP": "Доставчици на идентичност",
"VERIFIED_DOMAINS": "Проверени домейни",
"DOMAIN": "Настройки на домейна",
"LOGINTEXTS": "Текстове на интерфейса за влизане",
"BRANDING": "Брандиране",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Events",
"FAILEDEVENTS": "Fehlerhafte Events",
"ORGANIZATION": "Organisation",
"DOMAINS": "Domänen",
"PROJECT": "Projekte",
"PROJECTOVERVIEW": "Übersicht",
"PROJECTGRANTS": "Berechtigte Organisationen",
@@ -953,7 +952,7 @@
},
"DOMAINS": {
"NEW": "Domain hinzufügen",
"TITLE": "Domänen",
"TITLE": "Verifizierte Domains",
"DESCRIPTION": "Konfiguriere die Domains, die für Domain discovery und als Suffix für die Benutzer verwendet werden können.",
"SETPRIMARY": "Primäre Domain setzen",
"DELETE": {
@@ -1023,6 +1022,7 @@
"NOTIFICATIONS_DESC": "SMTP und SMS Einstellungen",
"MESSAGETEXTS": "Benachrichtigungstexte",
"IDP": "Identitätsanbieter",
"VERIFIED_DOMAINS": "Verifizierte Domains",
"DOMAIN": "Domain Einstellungen",
"LOGINTEXTS": "Login Interface Texte",
"BRANDING": "Branding",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Events",
"FAILEDEVENTS": "Failed Events",
"ORGANIZATION": "Organization",
"DOMAINS": "Domains",
"PROJECT": "Projects",
"PROJECTOVERVIEW": "Overview",
"PROJECTGRANTS": "Grants",
@@ -954,7 +953,7 @@
},
"DOMAINS": {
"NEW": "Add Domain",
"TITLE": "Domains",
"TITLE": "Verified domains",
"DESCRIPTION": "Configure your organization domains. This domain can be used for domain discovery and username suffixing.",
"SETPRIMARY": "Set as Primary",
"DELETE": {
@@ -1024,6 +1023,7 @@
"NOTIFICATIONS_DESC": "SMTP and SMS Settings",
"MESSAGETEXTS": "Message Texts",
"IDP": "Identity Providers",
"VERIFIED_DOMAINS": "Verified domains",
"DOMAIN": "Domain settings",
"LOGINTEXTS": "Login Interface Texts",
"BRANDING": "Branding",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Eventos",
"FAILEDEVENTS": "Eventos fallidos",
"ORGANIZATION": "Organización",
"DOMAINS": "Dominios",
"PROJECT": "Proyectos",
"PROJECTOVERVIEW": "Resumen",
"PROJECTGRANTS": "Concesiones",
@@ -954,7 +953,7 @@
},
"DOMAINS": {
"NEW": "Añadir dominio",
"TITLE": "Dominios",
"TITLE": "Dominios verificados",
"DESCRIPTION": "Configura tus dominios. Este dominio puede usarse para iniciar sesión con tus usuarios.",
"SETPRIMARY": "Establecer como primario",
"DELETE": {
@@ -1024,6 +1023,7 @@
"NOTIFICATIONS_DESC": "Ajustes SMTP y SMS",
"MESSAGETEXTS": "Mensajes de texto",
"IDP": "Proveedores de identidad",
"VERIFIED_DOMAINS": "Dominios verificados",
"DOMAIN": "Ajustes de dominio",
"LOGINTEXTS": "Textos de interfaz de inicio de sesión",
"BRANDING": "Imagen de marca",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Événements",
"FAILEDEVENTS": "Événements échoués",
"ORGANIZATION": "Organisation",
"DOMAINS": "Domaines",
"PROJECT": "Projets",
"PROJECTOVERVIEW": "Vue d'ensemble",
"PROJECTGRANTS": "Subventions",
@@ -953,7 +952,7 @@
},
"DOMAINS": {
"NEW": "Ajouter un domaine",
"TITLE": "Domaines",
"TITLE": "Domaines vérifiés",
"DESCRIPTION": "Configurez vos domaines. Ce domaine peut être utilisé pour se connecter avec vos utilisateurs.",
"SETPRIMARY": "Définir comme primaire",
"DELETE": {
@@ -1023,6 +1022,7 @@
"NOTIFICATIONS_DESC": "Paramètres SMTP et SMS",
"MESSAGETEXTS": "Textes des messages",
"IDP": "Fournisseurs d'identité",
"VERIFIED_DAMAINS": "Domaines vérifiés",
"DOMAIN": "Paramètres du domaine",
"LOGINTEXTS": "Textes de l'interface de connexion",
"BRANDING": "Image de marque",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Eventi",
"FAILEDEVENTS": "Eventi falliti",
"ORGANIZATION": "Organizzazione",
"DOMAINS": "Domini",
"PROJECT": "Progetti",
"PROJECTOVERVIEW": "Progetto",
"PROJECTGRANTS": "Organizzazioni ammissibili",
@@ -953,7 +952,7 @@
},
"DOMAINS": {
"NEW": "Aggiungi dominio",
"TITLE": "Domini",
"TITLE": "Domini verificati",
"DESCRIPTION": "Configura i tuoi domini. Questo dominio pu\u00f2 essere utilizzato per accedere con i tuoi utenti.",
"SETPRIMARY": "Impostato come primario",
"DELETE": {
@@ -1023,6 +1022,7 @@
"NOTIFICATIONS_DESC": "Impostazioni SMTP e SMS",
"MESSAGETEXTS": "Testi di notifica",
"IDP": "Fornitori di identità",
"VERIFIED_DAMAINS": "Domini verificati",
"DOMAIN": "Impostazioni del dominio",
"LOGINTEXTS": "Testi dell'interfaccia login",
"BRANDING": "Branding",

View File

@@ -95,7 +95,6 @@
"EVENTS": "イベント",
"FAILEDEVENTS": "失敗したイベント",
"ORGANIZATION": "組織",
"DOMAINS": "ドメイン",
"PROJECT": "プロジェクト",
"PROJECTOVERVIEW": "概要",
"PROJECTGRANTS": "グラント",
@@ -954,7 +953,7 @@
},
"DOMAINS": {
"NEW": "ドメインを追加する",
"TITLE": "ドメイン",
"TITLE": "検証済みドメイン",
"DESCRIPTION": "ドメインを設定します。このドメインは、ユーザーのログインで使用できます。",
"SETPRIMARY": "プライマリとして設定する",
"DELETE": {
@@ -1024,6 +1023,7 @@
"NOTIFICATIONS_DESC": "SMTPおよびSMS設定",
"MESSAGETEXTS": "メッセージテキスト",
"IDP": "IDプロバイダー",
"VERIFIED_DAMAINS": "検証済みドメイン",
"DOMAIN": "ドメイン設定",
"LOGINTEXTS": "ログイン画面のテキスト",
"BRANDING": "ブランディング",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Настани",
"FAILEDEVENTS": "Неуспешни настани",
"ORGANIZATION": "Организација",
"DOMAINS": "Домени",
"PROJECT": "Проекти",
"PROJECTOVERVIEW": "Преглед",
"PROJECTGRANTS": "Овластувања",
@@ -954,7 +953,7 @@
},
"DOMAINS": {
"NEW": "Додади домен",
"TITLE": "Домени",
"TITLE": "Потврдени домени",
"DESCRIPTION": "Конфигурирајте ги вашите домени. Овој домен може да се користи за најава на вашите корисници.",
"SETPRIMARY": "Постави како основен",
"DELETE": {
@@ -1025,6 +1024,7 @@
"NOTIFICATIONS_DESC": "Подесувања за SMTP и SMS",
"MESSAGETEXTS": "Текстови на пораки",
"IDP": "Identity Providers",
"VERIFIED_DAMAINS": "Потврдени домени",
"DOMAIN": "Подесувања за домен",
"LOGINTEXTS": "Текстови на интерфејс за најава",
"BRANDING": "Брендирање",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Zdarzenia",
"FAILEDEVENTS": "Nieudane Zdarzenia",
"ORGANIZATION": "Organizacja",
"DOMAINS": "Domeny",
"PROJECT": "Projekty",
"PROJECTOVERVIEW": "Przegląd",
"PROJECTGRANTS": "Uprawnienia",
@@ -953,7 +952,7 @@
},
"DOMAINS": {
"NEW": "Dodaj domenę",
"TITLE": "Domeny",
"TITLE": "Zweryfikowane domeny",
"DESCRIPTION": "Skonfiguruj swoje domeny. Ta domena może być używana do logowania się z Twoimi użytkownikami.",
"SETPRIMARY": "Ustaw jako główną",
"DELETE": {
@@ -1023,6 +1022,7 @@
"NOTIFICATIONS_DESC": "Ustawienia SMTP i SMS",
"MESSAGETEXTS": "Teksty wiadomości",
"IDP": "Dostawcy tożsamości",
"VERIFIED_DAMAINS": "Zweryfikowane domeny",
"DOMAIN": "Ustawienia domeny",
"LOGINTEXTS": "Teksty interfejsu logowania",
"BRANDING": "Marka",

View File

@@ -95,7 +95,6 @@
"EVENTS": "Eventos",
"FAILEDEVENTS": "Eventos com Falha",
"ORGANIZATION": "Organização",
"DOMAINS": "Domínios",
"PROJECT": "Projetos",
"PROJECTOVERVIEW": "Visão Geral",
"PROJECTGRANTS": "Autorizações",
@@ -954,7 +953,7 @@
},
"DOMAINS": {
"NEW": "Adicionar Domínio",
"TITLE": "Domínios",
"TITLE": "Domínios verificados",
"DESCRIPTION": "Configure seus domínios. Este domínio pode ser usado para o login dos seus usuários.",
"SETPRIMARY": "Definir como Principal",
"DELETE": {
@@ -1025,6 +1024,7 @@
"NOTIFICATIONS_DESC": "Configurações de SMTP e SMS",
"MESSAGETEXTS": "Textos de Mensagem",
"IDP": "Provedores de Identidade",
"VERIFIED_DAMAINS": "Domínios verificados",
"DOMAIN": "Configurações de Domínio",
"LOGINTEXTS": "Textos da Interface de Login",
"BRANDING": "Marca",

View File

@@ -95,7 +95,6 @@
"EVENTS": "活动",
"FAILEDEVENTS": "失败事件",
"ORGANIZATION": "组织",
"DOMAINS": "域名",
"PROJECT": "项目",
"PROJECTOVERVIEW": "概览",
"PROJECTGRANTS": "授予",
@@ -953,7 +952,7 @@
},
"DOMAINS": {
"NEW": "添加域名",
"TITLE": "域名",
"TITLE": "已验证的域名",
"DESCRIPTION": "配置您的域名。此域名可用于您的用户登录。",
"SETPRIMARY": "设置为主域名",
"DELETE": {
@@ -1023,6 +1022,7 @@
"NOTIFICATIONS_DESC": "SMTP 和 SMS 设置",
"MESSAGETEXTS": "消息文本",
"IDP": "身份提供者",
"VERIFIED_DAMAINS": "已验证的域名",
"DOMAIN": "域名设置",
"LOGINTEXTS": "登录界面文本",
"BRANDING": "品牌标识",