mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:17:32 +00:00
feat(actions): add token customization flow and extend functionally with modules (#4337)
* fix: potential memory leak * feat(actions): possibility to parse json feat(actions): possibility to perform http calls * add query call * feat(api): list flow and trigger types fix(api): switch flow and trigger types to dynamic objects * fix(translations): add action translations * use `domain.FlowType` * localizers * localization * trigger types * options on `query.Action` * add functions for actions * feat: management api: add list flow and trigger (#4352) * console changes * cleanup * fix: wrong localization Co-authored-by: Max Peintner <max@caos.ch> * id token works * check if claims not nil * feat(actions): metadata api * refactor(actions): modules * fix: allow prerelease * fix: test * feat(actions): deny list for http hosts * feat(actions): deny list for http hosts * refactor: actions * fix: different error ids * fix: rename statusCode to status * Actions objects as options (#4418) * fix: rename statusCode to status * fix(actions): objects as options * fix(actions): objects as options * fix(actions): set fields * add http client to old actions * fix(actions): add log module * fix(actions): add user to context where possible * fix(actions): add user to ctx in external authorization/pre creation * fix(actions): query correct flow in claims * test: actions * fix(id-generator): panic if no machine id * tests * maybe this? * fix linting * refactor: improve code * fix: metadata and usergrant usage in actions * fix: appendUserGrant * fix: allowedToFail and timeout in action execution * fix: allowed to fail in token complement flow * docs: add action log claim * Update defaults.yaml * fix log claim * remove prerelease build Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -21,12 +21,12 @@
|
||||
<p class="desc cnsl-secondary-text">{{ 'FLOWS.FLOWSDESCRIPTION' | translate }}</p>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['org.flow.read']">
|
||||
<div *ngIf="flow" class="flow">
|
||||
<div class="flow">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FLOWS.FLOWTYPE' | translate }}</cnsl-label>
|
||||
<mat-select [formControl]="typeControl">
|
||||
<mat-option *ngFor="let type of typesForSelection" [value]="type">
|
||||
{{ 'FLOWS.TYPES.' + type | translate }}
|
||||
{{ type.name?.localizedMessage }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@@ -36,8 +36,14 @@
|
||||
|
||||
<div class="flow-type">
|
||||
<i class="type-icon las la-dot-circle"></i>
|
||||
<span>{{ 'FLOWS.TYPES.' + flow.type | translate }}</span>
|
||||
<button matTooltip="{{ 'ACTIONS.CLEAR' | translate }}" mat-icon-button color="warn" (click)="clearFlow()">
|
||||
<span>{{ flow.type?.name?.localizedMessage }}</span>
|
||||
<button
|
||||
*ngIf="flow.type && (flow.triggerActionsList?.length ?? 0) > 0"
|
||||
matTooltip="{{ 'ACTIONS.CLEAR' | translate }}"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="clearFlow(flow.type.id)"
|
||||
>
|
||||
<i class="type-button-icon las la-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -45,7 +51,11 @@
|
||||
<cnsl-card *ngFor="let trigger of flow.triggerActionsList; index as i" class="trigger">
|
||||
<div class="trigger-top">
|
||||
<mat-icon svgIcon="mdi_arrow_right_bottom" class="icon"></mat-icon>
|
||||
<span>{{ 'FLOWS.TRIGGERTYPES.' + trigger.triggerType | translate }}</span>
|
||||
<span>{{ trigger.triggerType?.name?.localizedMessage }}</span>
|
||||
<span class="fill-space"></span>
|
||||
<button color="warn" mat-icon-button (click)="removeTriggerActionsList(i)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="flow-action-wrapper" cdkDropList (cdkDropListDropped)="drop(i, trigger.actionsList, $event)">
|
||||
@@ -72,7 +82,13 @@
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
<button class="add-btn cnsl-action-button" mat-raised-button color="primary" (click)="openAddTrigger()">
|
||||
<button
|
||||
*ngIf="flow.type"
|
||||
class="add-btn cnsl-action-button"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
(click)="openAddTrigger(flow.type)"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{ 'FLOWS.ADDTRIGGER' | translate }}</span>
|
||||
<span *ngIf="selection && selection.length"> ({{ selection.length }})</span>
|
||||
|
@@ -68,6 +68,10 @@ h1 {
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 7px;
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component';
|
||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
@@ -18,13 +19,12 @@ import { AddFlowDialogComponent } from './add-flow-dialog/add-flow-dialog.compon
|
||||
templateUrl: './actions.component.html',
|
||||
styleUrls: ['./actions.component.scss'],
|
||||
})
|
||||
export class ActionsComponent {
|
||||
export class ActionsComponent implements OnDestroy {
|
||||
public flow!: Flow.AsObject;
|
||||
public flowType: FlowType = FlowType.FLOW_TYPE_EXTERNAL_AUTHENTICATION;
|
||||
|
||||
public typeControl: UntypedFormControl = new UntypedFormControl(FlowType.FLOW_TYPE_EXTERNAL_AUTHENTICATION);
|
||||
public typeControl: UntypedFormControl = new UntypedFormControl();
|
||||
|
||||
public typesForSelection: FlowType[] = [FlowType.FLOW_TYPE_EXTERNAL_AUTHENTICATION];
|
||||
public typesForSelection: FlowType.AsObject[] = [];
|
||||
|
||||
public selection: Action.AsObject[] = [];
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
@@ -32,7 +32,7 @@ export class ActionsComponent {
|
||||
|
||||
public maxActions: number | null = null;
|
||||
public ActionState: any = ActionState;
|
||||
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
@@ -45,16 +45,42 @@ export class ActionsComponent {
|
||||
};
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
|
||||
this.loadFlow();
|
||||
}
|
||||
this.getFlowTypes();
|
||||
|
||||
private loadFlow() {
|
||||
this.mgmtService.getFlow(this.flowType).then((flowResponse) => {
|
||||
if (flowResponse.flow) this.flow = flowResponse.flow;
|
||||
this.typeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
|
||||
this.loadFlow((value as FlowType.AsObject).id);
|
||||
});
|
||||
}
|
||||
|
||||
public clearFlow(): void {
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
private getFlowTypes(): Promise<void> {
|
||||
return this.mgmtService
|
||||
.listFlowTypes()
|
||||
.then((resp) => {
|
||||
this.typesForSelection = resp.resultList;
|
||||
if (!this.flow && resp.resultList[0]) {
|
||||
const type = resp.resultList[0];
|
||||
this.typeControl.setValue(type);
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private loadFlow(id: string) {
|
||||
this.mgmtService.getFlow(id).then((flowResponse) => {
|
||||
if (flowResponse.flow) {
|
||||
this.flow = flowResponse.flow;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public clearFlow(id: string): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.CLEAR',
|
||||
@@ -68,10 +94,10 @@ export class ActionsComponent {
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
this.mgmtService
|
||||
.clearFlow(this.flowType)
|
||||
.then((resp) => {
|
||||
.clearFlow(id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('FLOWS.FLOWCLEARED', true);
|
||||
this.loadFlow();
|
||||
this.loadFlow(id);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
@@ -80,11 +106,10 @@ export class ActionsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public openAddTrigger(): void {
|
||||
public openAddTrigger(flow: FlowType.AsObject, trigger?: TriggerType.AsObject): void {
|
||||
const dialogRef = this.dialog.open(AddFlowDialogComponent, {
|
||||
data: {
|
||||
flowType: this.flowType,
|
||||
triggerType: TriggerType.TRIGGER_TYPE_POST_AUTHENTICATION,
|
||||
flowType: flow,
|
||||
actions: this.selection && this.selection.length ? this.selection : [],
|
||||
},
|
||||
width: '400px',
|
||||
@@ -96,7 +121,7 @@ export class ActionsComponent {
|
||||
.setTriggerActions(req.getActionIdsList(), req.getFlowType(), req.getTriggerType())
|
||||
.then((resp) => {
|
||||
this.toast.showInfo('FLOWS.FLOWCHANGED', true);
|
||||
this.loadFlow();
|
||||
this.loadFlow(flow.id);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
@@ -111,17 +136,52 @@ export class ActionsComponent {
|
||||
}
|
||||
|
||||
saveFlow(index: number) {
|
||||
this.mgmtService
|
||||
.setTriggerActions(
|
||||
this.flow.triggerActionsList[index].actionsList.map((action) => action.id),
|
||||
this.flowType,
|
||||
this.flow.triggerActionsList[index].triggerType,
|
||||
)
|
||||
.then((updateResponse) => {
|
||||
this.toast.showInfo('FLOWS.TOAST.ACTIONSSET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
if (
|
||||
this.flow.type &&
|
||||
this.flow.triggerActionsList &&
|
||||
this.flow.triggerActionsList[index] &&
|
||||
this.flow.triggerActionsList[index]?.triggerType
|
||||
) {
|
||||
this.mgmtService
|
||||
.setTriggerActions(
|
||||
this.flow.triggerActionsList[index].actionsList.map((action) => action.id),
|
||||
this.flow.type.id,
|
||||
this.flow.triggerActionsList[index].triggerType?.id ?? '',
|
||||
)
|
||||
.then(() => {
|
||||
this.toast.showInfo('FLOWS.TOAST.ACTIONSSET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removeTriggerActionsList(index: number) {
|
||||
if (this.flow.type && this.flow.triggerActionsList && this.flow.triggerActionsList[index]) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.CLEAR',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'FLOWS.DIALOG.REMOVEACTIONSLIST.TITLE',
|
||||
descriptionKey: 'FLOWS.DIALOG.REMOVEACTIONSLIST.DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
this.mgmtService
|
||||
.setTriggerActions([], this.flow?.type?.id ?? '', this.flow.triggerActionsList[index].triggerType?.id ?? '')
|
||||
.then(() => {
|
||||
this.toast.showInfo('FLOWS.TOAST.ACTIONSSET', true);
|
||||
this.loadFlow(this.flow?.type?.id ?? '');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,20 +2,11 @@
|
||||
|
||||
<div mat-dialog-content>
|
||||
<form *ngIf="form" [formGroup]="form">
|
||||
<cnsl-form-field class="form-field">
|
||||
<cnsl-label>{{ 'FLOWS.FLOWTYPE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="flowType">
|
||||
<mat-option *ngFor="let type of typesForSelection" [value]="type">
|
||||
{{ 'FLOWS.TYPES.' + type | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="form-field">
|
||||
<cnsl-label>{{ 'FLOWS.TRIGGERTYPE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="triggerType" name="triggerType">
|
||||
<mat-option *ngFor="let type of triggerTypesForSelection" [value]="type">
|
||||
{{ 'FLOWS.TRIGGERTYPES.' + type | translate }}
|
||||
{{ type.name?.localizedMessage }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
@@ -12,13 +12,10 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
styleUrls: ['./add-flow-dialog.component.scss'],
|
||||
})
|
||||
export class AddFlowDialogComponent {
|
||||
public flowType?: FlowType.AsObject;
|
||||
public actions: Action.AsObject[] = [];
|
||||
public typesForSelection: FlowType[] = [FlowType.FLOW_TYPE_EXTERNAL_AUTHENTICATION];
|
||||
public triggerTypesForSelection: TriggerType[] = [
|
||||
TriggerType.TRIGGER_TYPE_POST_AUTHENTICATION,
|
||||
TriggerType.TRIGGER_TYPE_POST_CREATION,
|
||||
TriggerType.TRIGGER_TYPE_PRE_CREATION,
|
||||
];
|
||||
public typesForSelection: FlowType.AsObject[] = [];
|
||||
public triggerTypesForSelection: TriggerType.AsObject[] = [];
|
||||
|
||||
public form!: UntypedFormGroup;
|
||||
constructor(
|
||||
@@ -28,14 +25,30 @@ export class AddFlowDialogComponent {
|
||||
public dialogRef: MatDialogRef<AddFlowDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
if (data.flowType) {
|
||||
this.flowType = data.flowType as FlowType.AsObject;
|
||||
this.getTriggerTypes((data.flowType as FlowType.AsObject).id);
|
||||
}
|
||||
|
||||
this.form = this.fb.group({
|
||||
flowType: [data.flowType ? data.flowType : '', [Validators.required]],
|
||||
triggerType: [data.triggerType ? data.triggerType : '', [Validators.required]],
|
||||
actionIdsList: [data.actions ? (data.actions as Action.AsObject[]).map((a) => a.id) : [], [Validators.required]],
|
||||
});
|
||||
|
||||
this.getActionIds();
|
||||
}
|
||||
|
||||
private getTriggerTypes(id: string): Promise<void> {
|
||||
return this.mgmtService
|
||||
.listFlowTriggerTypes(id)
|
||||
.then((resp) => {
|
||||
this.triggerTypesForSelection = resp.resultList;
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private getActionIds(): Promise<void> {
|
||||
return this.mgmtService
|
||||
.listActions()
|
||||
@@ -52,11 +65,13 @@ export class AddFlowDialogComponent {
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
const req = new SetTriggerActionsRequest();
|
||||
req.setActionIdsList(this.form.get('actionIdsList')?.value);
|
||||
req.setFlowType(this.form.get('flowType')?.value);
|
||||
req.setTriggerType(this.form.get('triggerType')?.value);
|
||||
if (this.flowType) {
|
||||
const req = new SetTriggerActionsRequest();
|
||||
req.setActionIdsList(this.form.get('actionIdsList')?.value);
|
||||
req.setFlowType(this.flowType.id);
|
||||
req.setTriggerType((this.form.get('triggerType')?.value as TriggerType.AsObject).id);
|
||||
|
||||
this.dialogRef.close(req);
|
||||
this.dialogRef.close(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -192,6 +192,10 @@ import {
|
||||
ListAppKeysResponse,
|
||||
ListAppsRequest,
|
||||
ListAppsResponse,
|
||||
ListFlowTriggerTypesRequest,
|
||||
ListFlowTriggerTypesResponse,
|
||||
ListFlowTypesRequest,
|
||||
ListFlowTypesResponse,
|
||||
ListGrantedProjectRolesRequest,
|
||||
ListGrantedProjectRolesResponse,
|
||||
ListGrantedProjectsRequest,
|
||||
@@ -982,13 +986,24 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.listActions(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getFlow(type: FlowType): Promise<GetFlowResponse.AsObject> {
|
||||
public listFlowTypes(): Promise<ListFlowTypesResponse.AsObject> {
|
||||
const req = new ListFlowTypesRequest();
|
||||
return this.grpcService.mgmt.listFlowTypes(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public listFlowTriggerTypes(type: string): Promise<ListFlowTriggerTypesResponse.AsObject> {
|
||||
const req = new ListFlowTriggerTypesRequest();
|
||||
req.setType(type);
|
||||
return this.grpcService.mgmt.listFlowTriggerTypes(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getFlow(type: string): Promise<GetFlowResponse.AsObject> {
|
||||
const req = new GetFlowRequest();
|
||||
req.setType(type);
|
||||
return this.grpcService.mgmt.getFlow(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public clearFlow(type: FlowType): Promise<ClearFlowResponse.AsObject> {
|
||||
public clearFlow(type: string): Promise<ClearFlowResponse.AsObject> {
|
||||
const req = new ClearFlowRequest();
|
||||
req.setType(type);
|
||||
return this.grpcService.mgmt.clearFlow(req, null).then((resp) => resp.toObject());
|
||||
@@ -996,8 +1011,8 @@ export class ManagementService {
|
||||
|
||||
public setTriggerActions(
|
||||
actionIdsList: string[],
|
||||
type: FlowType,
|
||||
triggerType: TriggerType,
|
||||
type: string,
|
||||
triggerType: string,
|
||||
): Promise<SetTriggerActionsResponse.AsObject> {
|
||||
const req = new SetTriggerActionsRequest();
|
||||
req.setActionIdsList(actionIdsList);
|
||||
|
@@ -654,15 +654,6 @@
|
||||
"1": "inaktiv",
|
||||
"2": "aktiv"
|
||||
},
|
||||
"TYPES": {
|
||||
"0": "Unspezifisch",
|
||||
"1": "Externe Authentifizierung"
|
||||
},
|
||||
"TRIGGERTYPES": {
|
||||
"1": "Post Authentication",
|
||||
"2": "Pre Creation",
|
||||
"3": "Post Creation"
|
||||
},
|
||||
"ADDTRIGGER": "Trigger hinzufügen",
|
||||
"FLOWCHANGED": "Der Ablauf wurde geändert",
|
||||
"FLOWCLEARED": "Der Ablauf wurde zurückgesetzt",
|
||||
@@ -689,6 +680,10 @@
|
||||
"CLEAR": {
|
||||
"TITLE": "Flow zurücksetzen?",
|
||||
"DESCRIPTION": "Sie sind im Begriff den Flow mitsamt seinen Triggern und Aktionen zurückzusetzen. Diese Änderung kann nicht wiederhergestellt werden."
|
||||
},
|
||||
"REMOVEACTIONSLIST": {
|
||||
"TITLE": "Ausgewählte Aktionen löschen?",
|
||||
"DESCRIPTION": "Wollen Sie die gewählten Aktionen wirklich löschen?"
|
||||
}
|
||||
},
|
||||
"TOAST": {
|
||||
|
@@ -654,15 +654,6 @@
|
||||
"1": "inactive",
|
||||
"2": "active"
|
||||
},
|
||||
"TYPES": {
|
||||
"0": "Unspecified Type",
|
||||
"1": "External Authentication"
|
||||
},
|
||||
"TRIGGERTYPES": {
|
||||
"1": "Post Authentication",
|
||||
"2": "Pre Creation",
|
||||
"3": "Post Creation"
|
||||
},
|
||||
"ADDTRIGGER": "Add trigger",
|
||||
"FLOWCHANGED": "The flow was changed successfully",
|
||||
"FLOWCLEARED": "The flow was reset successfully",
|
||||
@@ -689,6 +680,10 @@
|
||||
"CLEAR": {
|
||||
"TITLE": "Clear flow?",
|
||||
"DESCRIPTION": "You are about to reset the flow along with its triggers and actions. This change cannot be restored. Are you sure?"
|
||||
},
|
||||
"REMOVEACTIONSLIST": {
|
||||
"TITLE": "Delete selected Actions?",
|
||||
"DESCRIPTION": "Are you sure you want to delete the selected actions from the flow?"
|
||||
}
|
||||
},
|
||||
"TOAST": {
|
||||
|
@@ -654,15 +654,6 @@
|
||||
"1": "inactif",
|
||||
"2": "actif"
|
||||
},
|
||||
"TYPES": {
|
||||
"0": "Type non spécifié",
|
||||
"1": "Authentification externe"
|
||||
},
|
||||
"TRIGGERTYPES": {
|
||||
"1": "Post Authentification",
|
||||
"2": "Pré création",
|
||||
"3": "Post création"
|
||||
},
|
||||
"ADDTRIGGER": "Ajouter un déclencheur",
|
||||
"FLOWCHANGED": "Le processus a été modifié",
|
||||
"FLOWCLEARED": "Le processus a été supprimé avec succès",
|
||||
@@ -689,6 +680,10 @@
|
||||
"CLEAR": {
|
||||
"TITLE": "Effacer le flux ?",
|
||||
"DESCRIPTION": "Vous êtes sur le point de réinitialiser le flux ainsi que ses déclencheurs et actions. Ce changement ne peut pas être restauré. Etes-vous sûr ?"
|
||||
},
|
||||
"REMOVEACTIONSLIST": {
|
||||
"TITLE": "Supprimer les actions sélectionnées ?",
|
||||
"DESCRIPTION": "Voulez-vous vraiment supprimer les actions sélectionnées du flux ?"
|
||||
}
|
||||
},
|
||||
"TOAST": {
|
||||
|
@@ -654,15 +654,6 @@
|
||||
"1": "inattivo",
|
||||
"2": "attivo"
|
||||
},
|
||||
"TYPES": {
|
||||
"0": "Non specifico",
|
||||
"1": "Autenticazione esterna"
|
||||
},
|
||||
"TRIGGERTYPES": {
|
||||
"1": "Post autenticazione",
|
||||
"2": "Pre creazione",
|
||||
"3": "Post creazione"
|
||||
},
|
||||
"ADDTRIGGER": "Aggiungi trigger",
|
||||
"FLOWCHANGED": "Il processo è stato modificato con successo.",
|
||||
"FLOWCLEARED": "Il processo è stato eliminato con successo.",
|
||||
@@ -689,6 +680,10 @@
|
||||
"CLEAR": {
|
||||
"TITLE": "Elimina processo",
|
||||
"DESCRIPTION": "Stai per eliminare un processo. Questa azione non può essere annullata. Vuoi continuare?"
|
||||
},
|
||||
"REMOVEACTIONSLIST": {
|
||||
"TITLE": "Elimina le azioni selezionate?",
|
||||
"DESCRIPTION": "Sei sicuro di voler eliminare le azioni selezionate dal processo?"
|
||||
}
|
||||
},
|
||||
"TOAST": {
|
||||
|
@@ -654,15 +654,6 @@
|
||||
"1": "停用",
|
||||
"2": "激活"
|
||||
},
|
||||
"TYPES": {
|
||||
"0": "未指定类型",
|
||||
"1": "外部认证"
|
||||
},
|
||||
"TRIGGERTYPES": {
|
||||
"1": "身份验证后",
|
||||
"2": "创建之前",
|
||||
"3": "创建时"
|
||||
},
|
||||
"ADDTRIGGER": "添加触发器",
|
||||
"FLOWCHANGED": "流量已成功更改",
|
||||
"FLOWCLEARED": "流已成功删除",
|
||||
@@ -689,6 +680,10 @@
|
||||
"CLEAR": {
|
||||
"TITLE": "清理流程?",
|
||||
"DESCRIPTION": "您即将重置流程及其触发器和动作。此更改无法恢复。你确定吗?"
|
||||
},
|
||||
"REMOVEACTIONSLIST": {
|
||||
"TITLE": "删除选定的操作?",
|
||||
"DESCRIPTION": "您确定要从流中删除选定的操作吗?"
|
||||
}
|
||||
},
|
||||
"TOAST": {
|
||||
|
Reference in New Issue
Block a user