tenant configuration changes
This commit is contained in:
parent
73701b206c
commit
db90097c16
|
@ -57,11 +57,11 @@ public class DepositTenantConfigurationPersist {
|
||||||
this.spec()
|
this.spec()
|
||||||
.must(() -> !this.isListNullOrEmpty(item.getSources()))
|
.must(() -> !this.isListNullOrEmpty(item.getSources()))
|
||||||
.failOn(DepositTenantConfigurationPersist._sources).failWith(messageSource.getMessage("Validation_Required", new Object[]{DepositTenantConfigurationPersist._sources}, LocaleContextHolder.getLocale())),
|
.failOn(DepositTenantConfigurationPersist._sources).failWith(messageSource.getMessage("Validation_Required", new Object[]{DepositTenantConfigurationPersist._sources}, LocaleContextHolder.getLocale())),
|
||||||
this.refSpec()
|
this.navSpec()
|
||||||
.iff(() -> !this.isListNullOrEmpty(item.getSources()))
|
.iff(() -> !this.isListNullOrEmpty(item.getSources()))
|
||||||
.on(DepositTenantConfigurationPersist._sources)
|
.on(DepositTenantConfigurationPersist._sources)
|
||||||
.over(item.getSources())
|
.over(item.getSources())
|
||||||
.using(() -> this.validatorFactory.validator(DepositSourcePersist.DepositSourcePersistValidator.class))
|
.using((itm) -> this.validatorFactory.validator(DepositSourcePersist.DepositSourcePersistValidator.class))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,11 @@ public class FileTransformerTenantConfigurationPersist {
|
||||||
this.spec()
|
this.spec()
|
||||||
.must(() -> !this.isListNullOrEmpty(item.getSources()))
|
.must(() -> !this.isListNullOrEmpty(item.getSources()))
|
||||||
.failOn(FileTransformerTenantConfigurationPersist._sources).failWith(messageSource.getMessage("Validation_Required", new Object[]{FileTransformerTenantConfigurationPersist._sources}, LocaleContextHolder.getLocale())),
|
.failOn(FileTransformerTenantConfigurationPersist._sources).failWith(messageSource.getMessage("Validation_Required", new Object[]{FileTransformerTenantConfigurationPersist._sources}, LocaleContextHolder.getLocale())),
|
||||||
this.refSpec()
|
this.navSpec()
|
||||||
.iff(() -> !this.isListNullOrEmpty(item.getSources()))
|
.iff(() -> !this.isListNullOrEmpty(item.getSources()))
|
||||||
.on(FileTransformerTenantConfigurationPersist._sources)
|
.on(FileTransformerTenantConfigurationPersist._sources)
|
||||||
.over(item.getSources())
|
.over(item.getSources())
|
||||||
.using(() -> this.validatorFactory.validator(FileTransformerSourcePersist.FileTransformerSourcePersistValidator.class))
|
.using((itm) -> this.validatorFactory.validator(FileTransformerSourcePersist.FileTransformerSourcePersistValidator.class))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ public class LogoTenantConfigurationPersist {
|
||||||
protected List<Specification> specifications(LogoTenantConfigurationPersist item) {
|
protected List<Specification> specifications(LogoTenantConfigurationPersist item) {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
this.spec()
|
this.spec()
|
||||||
.must(() -> !this.isValidGuid(item.getStorageFileId()))
|
.must(() -> this.isValidGuid(item.getStorageFileId()))
|
||||||
.failOn(LogoTenantConfigurationPersist._storageFileId).failWith(messageSource.getMessage("Validation_Required", new Object[]{LogoTenantConfigurationPersist._storageFileId}, LocaleContextHolder.getLocale()))
|
.failOn(LogoTenantConfigurationPersist._storageFileId).failWith(messageSource.getMessage("Validation_Required", new Object[]{LogoTenantConfigurationPersist._storageFileId}, LocaleContextHolder.getLocale()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ export class CssColorsEditorComponent extends BasePendingChangesComponent implem
|
||||||
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
maxWidth: '300px',
|
maxWidth: '300px',
|
||||||
data: {
|
data: {
|
||||||
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
|
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.RESET-TO-DEFAULT'),
|
||||||
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||||
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ export class CssColorsEditorComponent extends BasePendingChangesComponent implem
|
||||||
|
|
||||||
console.log("Success Delete:", data);
|
console.log("Success Delete:", data);
|
||||||
|
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-RESET'), SnackBarNotificationLevel.Success);
|
||||||
this.prepareForm(null)
|
this.prepareForm(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
<div *ngIf="formGroup" class="container-fluid deposit">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h3>
|
||||||
|
{{label}}
|
||||||
|
<button mat-button class="action-btn" type="button" (click)="addSource()" [disabled]="formGroup.disabled">{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.ADD-SOURCE' | translate}}</button>
|
||||||
|
</h3>
|
||||||
|
<div *ngFor="let source of formGroup.get('depositPlugins').get('sources').controls; let sourceIndex=index;" class="row mb-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row mb-3 d-flex align-items-center">
|
||||||
|
<div class="col-auto d-flex">
|
||||||
|
<mat-card-title>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.DEPOSIT-PLUGINS' | translate}} {{sourceIndex + 1}}</mat-card-title>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto d-flex">
|
||||||
|
<button mat-icon-button class="action-list-icon" matTooltip="{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.REMOVE-SOURCE' | translate}}" (click)="removeSource(sourceIndex)" [disabled]="formGroup.disabled">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" >
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.REPOSITORY-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="repositoryId" [formControl]="source.get('repositoryId')" required>
|
||||||
|
<mat-error *ngIf="source.get('repositoryId').hasError('backendError')">{{source.get('repositoryId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('repositoryId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.URL' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="url" [formControl]="source.get('url')" required>
|
||||||
|
<mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
|
||||||
|
<mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
|
||||||
|
<mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
|
||||||
|
<mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
|
||||||
|
<mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PDF-TRANSFORMER-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="pdfTransformerId" [formControl]="source.get('pdfTransformerId')" required>
|
||||||
|
<mat-error *ngIf="source.get('pdfTransformerId').hasError('backendError')">{{source.get('pdfTransformerId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('pdfTransformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.RDA-TRANSFORMER-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="rdaTransformerId" [formControl]="source.get('rdaTransformerId')" required>
|
||||||
|
<mat-error *ngIf="source.get('rdaTransformerId').hasError('backendError')">{{source.get('rdaTransformerId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('rdaTransformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row actions-row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,20 @@
|
||||||
|
.deposit {
|
||||||
|
.action-btn {
|
||||||
|
border-radius: 30px;
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding-left: 2em;
|
||||||
|
padding-right: 2em;
|
||||||
|
box-shadow: 0px 3px 6px #1E202029;
|
||||||
|
|
||||||
|
transition-property: background-color, color;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
transition-delay: 50ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
&:disabled{
|
||||||
|
background-color: #CBCBCB;
|
||||||
|
color: #FFF;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
|
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
||||||
|
import { FormService } from '@common/forms/form-service';
|
||||||
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import { DepositSourceEditorModel, DepositTenantConfigurationEditorModel, TenantConfigurationEditorModel } from './deposit-editor.model';
|
||||||
|
import { TenantConfiguration, TenantConfigurationPersist } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { DepositEditorService } from './deposit-editor.service';
|
||||||
|
import { DepositEditorResolver } from './deposit-editor.resolver';
|
||||||
|
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
|
||||||
|
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tenant-configuration-deposit-editor',
|
||||||
|
templateUrl: 'deposit-editor.component.html',
|
||||||
|
styleUrls: ['./deposit-editor.component.scss'],
|
||||||
|
providers: [DepositEditorService]
|
||||||
|
})
|
||||||
|
export class DepositEditorComponent extends BasePendingChangesComponent implements OnInit {
|
||||||
|
|
||||||
|
isNew = true;
|
||||||
|
formGroup: UntypedFormGroup = null;
|
||||||
|
|
||||||
|
get editorModel(): TenantConfigurationEditorModel { return this._editorModel; }
|
||||||
|
set editorModel(value: TenantConfigurationEditorModel) { this._editorModel = value; }
|
||||||
|
private _editorModel: TenantConfigurationEditorModel;
|
||||||
|
|
||||||
|
protected get canDelete(): boolean {
|
||||||
|
return !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get canSave(): boolean {
|
||||||
|
return this.hasPermission(this.authService.permissionEnum.EditTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasPermission(permission: AppPermission): boolean {
|
||||||
|
return this.authService.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected dialog: MatDialog,
|
||||||
|
protected language: TranslateService,
|
||||||
|
protected formService: FormService,
|
||||||
|
protected uiNotificationService: UiNotificationService,
|
||||||
|
protected httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
private logger: LoggingService,
|
||||||
|
private tenantConfigurationService: TenantConfigurationService,
|
||||||
|
private depositEditorService: DepositEditorService,
|
||||||
|
private matomoService: MatomoService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canDeactivate(): boolean | Observable<boolean> {
|
||||||
|
return this.formGroup ? !this.formGroup.dirty : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.matomoService.trackPageView('Admin: TenantConfigurations');
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(successFunction: (item: TenantConfiguration) => void) {
|
||||||
|
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.DepositPlugins, DepositEditorResolver.lookupFields())
|
||||||
|
.pipe(map(data => data as TenantConfiguration), takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
data => successFunction(data),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
|
|
||||||
|
console.log("Error:", errorResponse);
|
||||||
|
|
||||||
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
|
if (error.statusCode === 400) {
|
||||||
|
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.TenantConfigurationTypeCanNotChange){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.MultipleTenantConfigurationTypeNotAllowed){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
} else {
|
||||||
|
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCallbackSuccess(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
||||||
|
this.refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
prepareForm(data: TenantConfiguration) {
|
||||||
|
try {
|
||||||
|
this.editorModel = data ? new TenantConfigurationEditorModel().fromModel(data) : new TenantConfigurationEditorModel();
|
||||||
|
|
||||||
|
this.buildForm();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Could not parse TenantConfiguration item: ' + data + error);
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm() {
|
||||||
|
this.formGroup = this.editorModel.buildForm(null, !this.authService.hasPermission(AppPermission.EditTenantConfiguration));
|
||||||
|
this.depositEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(): void {
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
persistEntity(onSuccess?: (response) => void): void {
|
||||||
|
const formData = this.formService.getValue(this.formGroup.value) as TenantConfigurationPersist;
|
||||||
|
|
||||||
|
this.tenantConfigurationService.persist(formData)
|
||||||
|
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
|
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formSubmit(): void {
|
||||||
|
this.clearErrorModel();
|
||||||
|
this.formService.removeAllBackEndErrors(this.formGroup);
|
||||||
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
|
if (!this.isFormValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.persistEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFormValid() {
|
||||||
|
return this.formGroup.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete() {
|
||||||
|
const value = this.formGroup.value;
|
||||||
|
if (value.id) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
maxWidth: '300px',
|
||||||
|
data: {
|
||||||
|
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.RESET-TO-DEFAULT'),
|
||||||
|
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||||
|
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.tenantConfigurationService.delete(value.id).pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
complete => this.onCallbackDeleteSuccessConfig(),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackDeleteSuccessConfig(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success Delete:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-RESET'), SnackBarNotificationLevel.Success);
|
||||||
|
this.prepareForm(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearErrorModel() {
|
||||||
|
this.editorModel.validationErrorModel.clear();
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addSource(): void {
|
||||||
|
const formArray = this.formGroup.get('depositPlugins').get('sources') as UntypedFormArray;
|
||||||
|
const source: DepositSourceEditorModel = new DepositSourceEditorModel(this.editorModel.validationErrorModel);
|
||||||
|
formArray.push(source.buildForm({ rootPath: 'depositPlugins.sources[' + formArray.length + '].' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSource(sourceIndex: number): void {
|
||||||
|
(this.formGroup.get('depositPlugins').get('sources') as UntypedFormArray).removeAt(sourceIndex);
|
||||||
|
|
||||||
|
// Reapply validators
|
||||||
|
DepositTenantConfigurationEditorModel.reapplySourcesFieldsValidators(
|
||||||
|
{
|
||||||
|
formArray: this.formGroup.get('depositPlugins').get('sources') as UntypedFormArray,
|
||||||
|
validationErrorModel: this.editorModel.validationErrorModel,
|
||||||
|
rootPath: 'depositPlugins.'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.formGroup.get('depositPlugins').get('sources').markAsDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
|
import { TenantConfigurationType } from "@app/core/common/enum/tenant-configuration-type";
|
||||||
|
import { DepositSource, DepositSourcePersist, DepositTenantConfiguration, DepositTenantConfigurationPersist, TenantConfiguration, TenantConfigurationPersist } from "@app/core/model/tenant-configuaration/tenant-configuration";
|
||||||
|
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
||||||
|
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
|
||||||
|
|
||||||
|
export class TenantConfigurationEditorModel extends BaseEditorModel implements TenantConfigurationPersist {
|
||||||
|
type: TenantConfigurationType;
|
||||||
|
depositPlugins: DepositTenantConfigurationEditorModel = new DepositTenantConfigurationEditorModel(this.validationErrorModel);
|
||||||
|
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor() { super(); this.type = TenantConfigurationType.DepositPlugins; }
|
||||||
|
|
||||||
|
public fromModel(item: TenantConfiguration): TenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
super.fromModel(item);
|
||||||
|
this.type = item.type;
|
||||||
|
if (item.depositPlugins) this.depositPlugins = new DepositTenantConfigurationEditorModel(this.validationErrorModel).fromModel(item.depositPlugins);
|
||||||
|
} else {
|
||||||
|
this.type = TenantConfigurationType.DepositPlugins;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||||
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||||
|
type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators],
|
||||||
|
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators],
|
||||||
|
depositPlugins: this.depositPlugins.buildForm({
|
||||||
|
rootPath: `depositPlugins.`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createValidationContext(): ValidationContext {
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||||
|
baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] });
|
||||||
|
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reApplyDepositSourcesValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, validationErrorModel } = params;
|
||||||
|
const control = formGroup?.get('config').get('deposit');
|
||||||
|
DepositTenantConfigurationEditorModel.reapplySourcesFieldsValidators({
|
||||||
|
formArray: control.get('sources') as UntypedFormArray,
|
||||||
|
rootPath: `depositPlugins.`,
|
||||||
|
validationErrorModel: validationErrorModel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DepositTenantConfigurationEditorModel implements DepositTenantConfigurationPersist {
|
||||||
|
sources: DepositSourceEditorModel[] = [];
|
||||||
|
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public fromModel(item: DepositTenantConfiguration): DepositTenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
if (item.sources) { item.sources.map(x => this.sources.push(new DepositSourceEditorModel(this.validationErrorModel).fromModel(x))); }
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(params?: {
|
||||||
|
context?: ValidationContext,
|
||||||
|
disabled?: boolean,
|
||||||
|
rootPath?: string
|
||||||
|
}): UntypedFormGroup {
|
||||||
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
|
if (context == null) {
|
||||||
|
context = DepositTenantConfigurationEditorModel.createValidationContext({
|
||||||
|
validationErrorModel: this.validationErrorModel,
|
||||||
|
rootPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const form: UntypedFormGroup = this.formBuilder.group({
|
||||||
|
sources: this.formBuilder.array(
|
||||||
|
(this.sources ?? []).map(
|
||||||
|
(item, index) => item.buildForm({
|
||||||
|
rootPath: `${rootPath}sources[${index}].`
|
||||||
|
})
|
||||||
|
), context.getValidation('sources')
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static createValidationContext(params: {
|
||||||
|
rootPath?: string,
|
||||||
|
validationErrorModel: ValidationErrorModel
|
||||||
|
}): ValidationContext {
|
||||||
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'sources', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}sources`)] });
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reapplySourcesFieldsValidators(params: {
|
||||||
|
formArray: UntypedFormArray,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
rootPath: string
|
||||||
|
}): void {
|
||||||
|
const { validationErrorModel, rootPath, formArray } = params;
|
||||||
|
formArray?.controls?.forEach(
|
||||||
|
(control, index) => DepositSourceEditorModel.reapplyValidators({
|
||||||
|
formGroup: control as UntypedFormGroup,
|
||||||
|
rootPath: `${rootPath}sources[${index}].`,
|
||||||
|
validationErrorModel: validationErrorModel
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class DepositSourceEditorModel implements DepositSourcePersist {
|
||||||
|
repositoryId: string;
|
||||||
|
url: string;
|
||||||
|
issuerUrl: string;
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
scope: string;
|
||||||
|
pdfTransformerId: string;
|
||||||
|
rdaTransformerId: string;
|
||||||
|
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public fromModel(item: DepositSource): DepositSourceEditorModel {
|
||||||
|
if (item) {
|
||||||
|
this.repositoryId = item.repositoryId;
|
||||||
|
this.url = item.url;
|
||||||
|
this.issuerUrl = item.issuerUrl;
|
||||||
|
this.clientId = item.clientId;
|
||||||
|
this.clientSecret = item.clientSecret;
|
||||||
|
this.scope = item.scope;
|
||||||
|
this.pdfTransformerId = item.pdfTransformerId;
|
||||||
|
this.rdaTransformerId = item.rdaTransformerId;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(params?: {
|
||||||
|
context?: ValidationContext,
|
||||||
|
disabled?: boolean,
|
||||||
|
rootPath?: string
|
||||||
|
}): UntypedFormGroup {
|
||||||
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
|
if (context == null) {
|
||||||
|
context = DepositSourceEditorModel.createValidationContext({
|
||||||
|
validationErrorModel: this.validationErrorModel,
|
||||||
|
rootPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators],
|
||||||
|
issuerUrl: [{ value: this.issuerUrl, disabled: disabled }, context.getValidation('issuerUrl').validators],
|
||||||
|
clientId: [{ value: this.clientId, disabled: disabled }, context.getValidation('clientId').validators],
|
||||||
|
clientSecret: [{ value: this.clientSecret, disabled: disabled }, context.getValidation('clientSecret').validators],
|
||||||
|
scope: [{ value: this.scope, disabled: disabled }, context.getValidation('scope').validators],
|
||||||
|
repositoryId: [{ value: this.repositoryId, disabled: disabled }, context.getValidation('repositoryId').validators],
|
||||||
|
pdfTransformerId: [{ value: this.pdfTransformerId, disabled: disabled }, context.getValidation('pdfTransformerId').validators],
|
||||||
|
rdaTransformerId: [{ value: this.rdaTransformerId, disabled: disabled }, context.getValidation('rdaTransformerId').validators],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static createValidationContext(params: {
|
||||||
|
rootPath?: string,
|
||||||
|
validationErrorModel: ValidationErrorModel
|
||||||
|
}): ValidationContext {
|
||||||
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'url', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}url`)] });
|
||||||
|
baseValidationArray.push({ key: 'repositoryId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}repositoryId`)] });
|
||||||
|
baseValidationArray.push({ key: 'issuerUrl', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}issuerUrl`)] });
|
||||||
|
baseValidationArray.push({ key: 'clientId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientId`)] });
|
||||||
|
baseValidationArray.push({ key: 'clientSecret', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientSecret`)] });
|
||||||
|
baseValidationArray.push({ key: 'scope', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}scope`)] });
|
||||||
|
baseValidationArray.push({ key: 'pdfTransformerId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}pdfTransformerId`)] });
|
||||||
|
baseValidationArray.push({ key: 'rdaTransformerId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}rdaTransformerId`)] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reapplyValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
rootPath: string
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, rootPath, validationErrorModel } = params;
|
||||||
|
const context = DepositSourceEditorModel.createValidationContext({
|
||||||
|
rootPath,
|
||||||
|
validationErrorModel
|
||||||
|
});
|
||||||
|
|
||||||
|
['url', 'repositoryId', 'issuerUrl', 'clientId', 'clientSecret', 'scope', 'pdfTransformerId', 'pdfTransformerId'].forEach(keyField => {
|
||||||
|
const control = formGroup?.get(keyField);
|
||||||
|
control?.clearValidators();
|
||||||
|
control?.addValidators(context.getValidation(keyField).validators);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { DepositSource, DepositTenantConfiguration, TenantConfiguration } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||||
|
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
|
||||||
|
import { takeUntil, tap } from 'rxjs/operators';
|
||||||
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DepositEditorResolver extends BaseEditorResolver {
|
||||||
|
|
||||||
|
constructor(private tenantConfigurationService: TenantConfigurationService, private breadcrumbService: BreadcrumbService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static lookupFields(): string[] {
|
||||||
|
return [
|
||||||
|
...BaseEditorResolver.lookupFields(),
|
||||||
|
nameof<TenantConfiguration>(x => x.id),
|
||||||
|
nameof<TenantConfiguration>(x => x.type),
|
||||||
|
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.clientId)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.clientSecret)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.issuerUrl)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.repositoryId)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.scope)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.url)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.pdfTransformerId)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.depositPlugins), nameof<DepositTenantConfiguration>(x => x.sources), nameof<DepositSource>(x => x.rdaTransformerId)].join('.'),
|
||||||
|
|
||||||
|
|
||||||
|
nameof<TenantConfiguration>(x => x.createdAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.updatedAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.hash),
|
||||||
|
nameof<TenantConfiguration>(x => x.isActive)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve() {
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
...DepositEditorResolver.lookupFields()
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.DepositPlugins, fields).pipe(takeUntil(this._destroyed));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DepositEditorService {
|
||||||
|
private validationErrorModel: ValidationErrorModel;
|
||||||
|
|
||||||
|
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
|
||||||
|
this.validationErrorModel = validationErrorModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValidationErrorModel(): ValidationErrorModel {
|
||||||
|
return this.validationErrorModel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<div *ngIf="formGroup" class="container-fluid file-transformer">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h3>
|
||||||
|
{{label}}
|
||||||
|
<button mat-button class="action-btn" type="button" (click)="addSource()" [disabled]="formGroup.disabled">{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.ADD-SOURCE' | translate}}</button>
|
||||||
|
</h3>
|
||||||
|
<div *ngFor="let source of formGroup.get('fileTransformerPlugins').get('sources').controls; let sourceIndex=index;" class="row mb-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row mb-3 d-flex align-items-center">
|
||||||
|
<div class="col-auto d-flex">
|
||||||
|
<mat-card-title>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.FILE-TRANSFORMER-PLUGINS' | translate}} {{sourceIndex + 1}}</mat-card-title>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto d-flex">
|
||||||
|
<button mat-icon-button class="action-list-icon" matTooltip="{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.REMOVE-SOURCE' | translate}}" (click)="removeSource(sourceIndex)" [disabled]="formGroup.disabled">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" >
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.TRANSFORMER-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="transformerId" [formControl]="source.get('transformerId')" required>
|
||||||
|
<mat-error *ngIf="source.get('transformerId').hasError('backendError')">{{source.get('transformerId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('transformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.URL' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="url" [formControl]="source.get('url')" required>
|
||||||
|
<mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
|
||||||
|
<mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
|
||||||
|
<mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
|
||||||
|
<mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<mat-form-field class="w-100">
|
||||||
|
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
|
||||||
|
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
|
||||||
|
<mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
|
||||||
|
<mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row actions-row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,20 @@
|
||||||
|
.file-transformer {
|
||||||
|
.action-btn {
|
||||||
|
border-radius: 30px;
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding-left: 2em;
|
||||||
|
padding-right: 2em;
|
||||||
|
box-shadow: 0px 3px 6px #1E202029;
|
||||||
|
|
||||||
|
transition-property: background-color, color;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
transition-delay: 50ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
&:disabled{
|
||||||
|
background-color: #CBCBCB;
|
||||||
|
color: #FFF;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
|
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
||||||
|
import { FormService } from '@common/forms/form-service';
|
||||||
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import { FileTransformerSourceEditorModel, FileTransformerTenantConfigurationEditorModel, TenantConfigurationEditorModel } from './file-transformer-editor.model';
|
||||||
|
import { TenantConfiguration, TenantConfigurationPersist } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { FileTransformerEditorService } from './file-transformer-editor.service';
|
||||||
|
import { FileTransformerEditorResolver } from './file-transformer-editor.resolver';
|
||||||
|
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
|
||||||
|
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tenant-configuration-file-transformer-editor',
|
||||||
|
templateUrl: 'file-transformer-editor.component.html',
|
||||||
|
styleUrls: ['./file-transformer-editor.component.scss'],
|
||||||
|
providers: [FileTransformerEditorService]
|
||||||
|
})
|
||||||
|
export class FileTransformerEditorComponent extends BasePendingChangesComponent implements OnInit {
|
||||||
|
|
||||||
|
isNew = true;
|
||||||
|
formGroup: UntypedFormGroup = null;
|
||||||
|
|
||||||
|
get editorModel(): TenantConfigurationEditorModel { return this._editorModel; }
|
||||||
|
set editorModel(value: TenantConfigurationEditorModel) { this._editorModel = value; }
|
||||||
|
private _editorModel: TenantConfigurationEditorModel;
|
||||||
|
|
||||||
|
protected get canDelete(): boolean {
|
||||||
|
return !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get canSave(): boolean {
|
||||||
|
return this.hasPermission(this.authService.permissionEnum.EditTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasPermission(permission: AppPermission): boolean {
|
||||||
|
return this.authService.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected dialog: MatDialog,
|
||||||
|
protected language: TranslateService,
|
||||||
|
protected formService: FormService,
|
||||||
|
protected uiNotificationService: UiNotificationService,
|
||||||
|
protected httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
private logger: LoggingService,
|
||||||
|
private tenantConfigurationService: TenantConfigurationService,
|
||||||
|
private fileTransformerEditorService: FileTransformerEditorService,
|
||||||
|
private matomoService: MatomoService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canDeactivate(): boolean | Observable<boolean> {
|
||||||
|
return this.formGroup ? !this.formGroup.dirty : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.matomoService.trackPageView('Admin: TenantConfigurations');
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(successFunction: (item: TenantConfiguration) => void) {
|
||||||
|
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.FileTransformerPlugins, FileTransformerEditorResolver.lookupFields())
|
||||||
|
.pipe(map(data => data as TenantConfiguration), takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
data => successFunction(data),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
|
|
||||||
|
console.log("Error:", errorResponse);
|
||||||
|
|
||||||
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
|
if (error.statusCode === 400) {
|
||||||
|
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.TenantConfigurationTypeCanNotChange){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.MultipleTenantConfigurationTypeNotAllowed){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
} else {
|
||||||
|
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCallbackSuccess(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
||||||
|
this.refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
prepareForm(data: TenantConfiguration) {
|
||||||
|
try {
|
||||||
|
this.editorModel = data ? new TenantConfigurationEditorModel().fromModel(data) : new TenantConfigurationEditorModel();
|
||||||
|
|
||||||
|
this.buildForm();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Could not parse TenantConfiguration item: ' + data + error);
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm() {
|
||||||
|
this.formGroup = this.editorModel.buildForm(null, !this.authService.hasPermission(AppPermission.EditTenantConfiguration));
|
||||||
|
this.fileTransformerEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(): void {
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
persistEntity(onSuccess?: (response) => void): void {
|
||||||
|
const formData = this.formService.getValue(this.formGroup.value) as TenantConfigurationPersist;
|
||||||
|
|
||||||
|
this.tenantConfigurationService.persist(formData)
|
||||||
|
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
|
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formSubmit(): void {
|
||||||
|
this.clearErrorModel();
|
||||||
|
this.formService.removeAllBackEndErrors(this.formGroup);
|
||||||
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
|
if (!this.isFormValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.persistEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFormValid() {
|
||||||
|
return this.formGroup.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete() {
|
||||||
|
const value = this.formGroup.value;
|
||||||
|
if (value.id) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
maxWidth: '300px',
|
||||||
|
data: {
|
||||||
|
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.RESET-TO-DEFAULT'),
|
||||||
|
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||||
|
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.tenantConfigurationService.delete(value.id).pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
complete => this.onCallbackDeleteSuccessConfig(),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackDeleteSuccessConfig(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success Delete:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-RESET'), SnackBarNotificationLevel.Success);
|
||||||
|
this.prepareForm(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearErrorModel() {
|
||||||
|
this.editorModel.validationErrorModel.clear();
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addSource(): void {
|
||||||
|
const formArray = this.formGroup.get('fileTransformerPlugins').get('sources') as UntypedFormArray;
|
||||||
|
const source: FileTransformerSourceEditorModel = new FileTransformerSourceEditorModel(this.editorModel.validationErrorModel);
|
||||||
|
formArray.push(source.buildForm({ rootPath: 'fileTransformerPlugins.sources[' + formArray.length + '].' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSource(sourceIndex: number): void {
|
||||||
|
(this.formGroup.get('fileTransformerPlugins').get('sources') as UntypedFormArray).removeAt(sourceIndex);
|
||||||
|
|
||||||
|
// Reapply validators
|
||||||
|
FileTransformerTenantConfigurationEditorModel.reapplySourcesFieldsValidators(
|
||||||
|
{
|
||||||
|
formArray: this.formGroup.get('fileTransformerPlugins').get('sources') as UntypedFormArray,
|
||||||
|
validationErrorModel: this.editorModel.validationErrorModel,
|
||||||
|
rootPath: 'fileTransformerPlugins.'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.formGroup.get('fileTransformerPlugins').get('sources').markAsDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,229 @@
|
||||||
|
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
|
import { TenantConfigurationType } from "@app/core/common/enum/tenant-configuration-type";
|
||||||
|
import { FileTransformerSource, FileTransformerSourcePersist, FileTransformerTenantConfiguration, FileTransformerTenantConfigurationPersist, TenantConfiguration, TenantConfigurationPersist } from "@app/core/model/tenant-configuaration/tenant-configuration";
|
||||||
|
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
||||||
|
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
|
||||||
|
|
||||||
|
export class TenantConfigurationEditorModel extends BaseEditorModel implements TenantConfigurationPersist {
|
||||||
|
type: TenantConfigurationType;
|
||||||
|
fileTransformerPlugins: FileTransformerTenantConfigurationEditorModel = new FileTransformerTenantConfigurationEditorModel(this.validationErrorModel);
|
||||||
|
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor() { super(); this.type = TenantConfigurationType.FileTransformerPlugins; }
|
||||||
|
|
||||||
|
public fromModel(item: TenantConfiguration): TenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
super.fromModel(item);
|
||||||
|
this.type = item.type;
|
||||||
|
if (item.fileTransformerPlugins) this.fileTransformerPlugins = new FileTransformerTenantConfigurationEditorModel(this.validationErrorModel).fromModel(item.fileTransformerPlugins);
|
||||||
|
} else {
|
||||||
|
this.type = TenantConfigurationType.FileTransformerPlugins;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||||
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||||
|
type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators],
|
||||||
|
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators],
|
||||||
|
fileTransformerPlugins: this.fileTransformerPlugins.buildForm({
|
||||||
|
rootPath: `fileTransformerPlugins.`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createValidationContext(): ValidationContext {
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||||
|
baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] });
|
||||||
|
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reApplyFileTransformerSourcesValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, validationErrorModel } = params;
|
||||||
|
const control = formGroup?.get('config').get('fileTransformer');
|
||||||
|
FileTransformerTenantConfigurationEditorModel.reapplySourcesFieldsValidators({
|
||||||
|
formArray: control.get('sources') as UntypedFormArray,
|
||||||
|
rootPath: `fileTransformerPlugins.`,
|
||||||
|
validationErrorModel: validationErrorModel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileTransformerTenantConfigurationEditorModel implements FileTransformerTenantConfigurationPersist {
|
||||||
|
sources: FileTransformerSourceEditorModel[] = [];
|
||||||
|
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public fromModel(item: FileTransformerTenantConfiguration): FileTransformerTenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
if (item.sources) { item.sources.map(x => this.sources.push(new FileTransformerSourceEditorModel(this.validationErrorModel).fromModel(x))); }
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(params?: {
|
||||||
|
context?: ValidationContext,
|
||||||
|
disabled?: boolean,
|
||||||
|
rootPath?: string
|
||||||
|
}): UntypedFormGroup {
|
||||||
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
|
if (context == null) {
|
||||||
|
context = FileTransformerTenantConfigurationEditorModel.createValidationContext({
|
||||||
|
validationErrorModel: this.validationErrorModel,
|
||||||
|
rootPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const form: UntypedFormGroup = this.formBuilder.group({
|
||||||
|
sources: this.formBuilder.array(
|
||||||
|
(this.sources ?? []).map(
|
||||||
|
(item, index) => item.buildForm({
|
||||||
|
rootPath: `${rootPath}sources[${index}].`
|
||||||
|
})
|
||||||
|
), context.getValidation('sources')
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static createValidationContext(params: {
|
||||||
|
rootPath?: string,
|
||||||
|
validationErrorModel: ValidationErrorModel
|
||||||
|
}): ValidationContext {
|
||||||
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'sources', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}sources`)] });
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reapplySourcesFieldsValidators(params: {
|
||||||
|
formArray: UntypedFormArray,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
rootPath: string
|
||||||
|
}): void {
|
||||||
|
const { validationErrorModel, rootPath, formArray } = params;
|
||||||
|
formArray?.controls?.forEach(
|
||||||
|
(control, index) => FileTransformerSourceEditorModel.reapplyValidators({
|
||||||
|
formGroup: control as UntypedFormGroup,
|
||||||
|
rootPath: `${rootPath}sources[${index}].`,
|
||||||
|
validationErrorModel: validationErrorModel
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class FileTransformerSourceEditorModel implements FileTransformerSourcePersist {
|
||||||
|
transformerId: string;
|
||||||
|
url: string;
|
||||||
|
issuerUrl: string;
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
scope: string;
|
||||||
|
pdfTransformerId: string;
|
||||||
|
rdaTransformerId: string;
|
||||||
|
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public fromModel(item: FileTransformerSource): FileTransformerSourceEditorModel {
|
||||||
|
if (item) {
|
||||||
|
this.transformerId = item.transformerId;
|
||||||
|
this.url = item.url;
|
||||||
|
this.issuerUrl = item.issuerUrl;
|
||||||
|
this.clientId = item.clientId;
|
||||||
|
this.clientSecret = item.clientSecret;
|
||||||
|
this.scope = item.scope;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(params?: {
|
||||||
|
context?: ValidationContext,
|
||||||
|
disabled?: boolean,
|
||||||
|
rootPath?: string
|
||||||
|
}): UntypedFormGroup {
|
||||||
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
|
if (context == null) {
|
||||||
|
context = FileTransformerSourceEditorModel.createValidationContext({
|
||||||
|
validationErrorModel: this.validationErrorModel,
|
||||||
|
rootPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators],
|
||||||
|
issuerUrl: [{ value: this.issuerUrl, disabled: disabled }, context.getValidation('issuerUrl').validators],
|
||||||
|
clientId: [{ value: this.clientId, disabled: disabled }, context.getValidation('clientId').validators],
|
||||||
|
clientSecret: [{ value: this.clientSecret, disabled: disabled }, context.getValidation('clientSecret').validators],
|
||||||
|
scope: [{ value: this.scope, disabled: disabled }, context.getValidation('scope').validators],
|
||||||
|
transformerId: [{ value: this.transformerId, disabled: disabled }, context.getValidation('transformerId').validators],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static createValidationContext(params: {
|
||||||
|
rootPath?: string,
|
||||||
|
validationErrorModel: ValidationErrorModel
|
||||||
|
}): ValidationContext {
|
||||||
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'url', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}url`)] });
|
||||||
|
baseValidationArray.push({ key: 'transformerId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}transformerId`)] });
|
||||||
|
baseValidationArray.push({ key: 'issuerUrl', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}issuerUrl`)] });
|
||||||
|
baseValidationArray.push({ key: 'clientId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientId`)] });
|
||||||
|
baseValidationArray.push({ key: 'clientSecret', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}clientSecret`)] });
|
||||||
|
baseValidationArray.push({ key: 'scope', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}scope`)] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static reapplyValidators(params: {
|
||||||
|
formGroup: UntypedFormGroup,
|
||||||
|
validationErrorModel: ValidationErrorModel,
|
||||||
|
rootPath: string
|
||||||
|
}): void {
|
||||||
|
|
||||||
|
const { formGroup, rootPath, validationErrorModel } = params;
|
||||||
|
const context = FileTransformerSourceEditorModel.createValidationContext({
|
||||||
|
rootPath,
|
||||||
|
validationErrorModel
|
||||||
|
});
|
||||||
|
|
||||||
|
['url', 'transformerId', 'issuerUrl', 'clientId', 'clientSecret', 'scope'].forEach(keyField => {
|
||||||
|
const control = formGroup?.get(keyField);
|
||||||
|
control?.clearValidators();
|
||||||
|
control?.addValidators(context.getValidation(keyField).validators);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { FileTransformerSource, FileTransformerTenantConfiguration, TenantConfiguration } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||||
|
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
|
||||||
|
import { takeUntil, tap } from 'rxjs/operators';
|
||||||
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FileTransformerEditorResolver extends BaseEditorResolver {
|
||||||
|
|
||||||
|
constructor(private tenantConfigurationService: TenantConfigurationService, private breadcrumbService: BreadcrumbService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static lookupFields(): string[] {
|
||||||
|
return [
|
||||||
|
...BaseEditorResolver.lookupFields(),
|
||||||
|
nameof<TenantConfiguration>(x => x.id),
|
||||||
|
nameof<TenantConfiguration>(x => x.type),
|
||||||
|
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.clientId)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.clientSecret)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.issuerUrl)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.transformerId)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.scope)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.fileTransformerPlugins), nameof<FileTransformerTenantConfiguration>(x => x.sources), nameof<FileTransformerSource>(x => x.url)].join('.'),
|
||||||
|
|
||||||
|
|
||||||
|
nameof<TenantConfiguration>(x => x.createdAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.updatedAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.hash),
|
||||||
|
nameof<TenantConfiguration>(x => x.isActive)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve() {
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
...FileTransformerEditorResolver.lookupFields()
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.FileTransformerPlugins, fields).pipe(takeUntil(this._destroyed));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FileTransformerEditorService {
|
||||||
|
private validationErrorModel: ValidationErrorModel;
|
||||||
|
|
||||||
|
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
|
||||||
|
this.validationErrorModel = validationErrorModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValidationErrorModel(): ValidationErrorModel {
|
||||||
|
return this.validationErrorModel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<div *ngIf="formGroup" class="container-fluid logo">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<ngx-dropzone #drop class="drop-file col-12" (change)="fileChangeEvent($event, true)" [multiple]="false" [accept]="'image/*'" [disabled]="formGroup.get('logo')?.get('storageFileId').disabled">
|
||||||
|
<ngx-dropzone-preview *ngIf="formGroup.get('logo')?.get('storageFileId')?.value" class="file-preview" [removable]="true" (removed)="onRemove()" (click)="download(formGroup.get('logo')?.get('storageFileId').value)">
|
||||||
|
<ngx-dropzone-label class="file-label">{{ fileNameDisplay }}</ngx-dropzone-label>
|
||||||
|
</ngx-dropzone-preview>
|
||||||
|
</ngx-dropzone>
|
||||||
|
<div class="col-12 d-flex justify-content-center attach-btn">
|
||||||
|
<button *ngIf="!formGroup.get('logo')?.get('storageFileId')?.value" mat-button (click)="drop.showFileSelector()" type="button" class="attach-file-btn" [disabled]="formGroup.get('logo')?.get('storageFileId')?.disabled">
|
||||||
|
<mat-icon class="mr-2">upload</mat-icon>
|
||||||
|
<mat-label>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.UPLOAD" | translate }}</mat-label>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button *ngIf="formGroup.get('logo')?.get('storageFileId')?.value" mat-button (click)="download(formGroup.get('logo')?.get('storageFileId')?.value)" type="button" class="attach-file-btn">
|
||||||
|
<mat-icon class="mr-2">download</mat-icon>
|
||||||
|
<mat-label>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.DOWNLOAD" | translate }}</mat-label>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row actions-row">
|
||||||
|
<div class="col"></div>
|
||||||
|
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()">
|
||||||
|
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,3 @@
|
||||||
|
.logo {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
|
||||||
|
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||||
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
||||||
|
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
||||||
|
import { FormService } from '@common/forms/form-service';
|
||||||
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
|
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import { TenantConfigurationEditorModel } from './logo-editor.model';
|
||||||
|
import { TenantConfiguration, TenantConfigurationPersist } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { LogoEditorService } from './logo-editor.service';
|
||||||
|
import { LogoEditorResolver } from './logo-editor.resolver';
|
||||||
|
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
|
||||||
|
import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
|
import { StorageFileService } from '@app/core/services/storage-file/storage-file.service';
|
||||||
|
import { Guid } from '@common/types/guid';
|
||||||
|
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||||
|
import * as FileSaver from 'file-saver';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tenant-configuration-logo-editor',
|
||||||
|
templateUrl: 'logo-editor.component.html',
|
||||||
|
styleUrls: ['./logo-editor.component.scss'],
|
||||||
|
providers: [LogoEditorService]
|
||||||
|
})
|
||||||
|
export class LogoEditorComponent extends BasePendingChangesComponent implements OnInit {
|
||||||
|
|
||||||
|
isNew = true;
|
||||||
|
formGroup: UntypedFormGroup = null;
|
||||||
|
fileNameDisplay: string = null;
|
||||||
|
|
||||||
|
filesToUpload: FileList;
|
||||||
|
|
||||||
|
get editorModel(): TenantConfigurationEditorModel { return this._editorModel; }
|
||||||
|
set editorModel(value: TenantConfigurationEditorModel) { this._editorModel = value; }
|
||||||
|
private _editorModel: TenantConfigurationEditorModel;
|
||||||
|
|
||||||
|
protected get canDelete(): boolean {
|
||||||
|
return !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get canSave(): boolean {
|
||||||
|
return this.hasPermission(this.authService.permissionEnum.EditTenantConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasPermission(permission: AppPermission): boolean {
|
||||||
|
return this.authService.hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected dialog: MatDialog,
|
||||||
|
protected language: TranslateService,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
protected formService: FormService,
|
||||||
|
protected uiNotificationService: UiNotificationService,
|
||||||
|
protected httpErrorHandlingService: HttpErrorHandlingService,
|
||||||
|
protected authService: AuthService,
|
||||||
|
private logger: LoggingService,
|
||||||
|
private tenantConfigurationService: TenantConfigurationService,
|
||||||
|
private logoEditorService: LogoEditorService,
|
||||||
|
private matomoService: MatomoService,
|
||||||
|
private fileUtils: FileUtils,
|
||||||
|
private storageFileService: StorageFileService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
canDeactivate(): boolean | Observable<boolean> {
|
||||||
|
return this.formGroup ? !this.formGroup.dirty : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.matomoService.trackPageView('Admin: TenantConfigurations');
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(successFunction: (item: TenantConfiguration) => void) {
|
||||||
|
this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.Logo, LogoEditorResolver.lookupFields())
|
||||||
|
.pipe(map(data => data as TenantConfiguration), takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
data => successFunction(data),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackError(errorResponse: HttpErrorResponse) {
|
||||||
|
|
||||||
|
console.log("Error:", errorResponse);
|
||||||
|
|
||||||
|
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
|
||||||
|
if (error.statusCode === 400) {
|
||||||
|
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.TenantConfigurationTypeCanNotChange){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
if(errorResponse.error.code === ResponseErrorCode.MultipleTenantConfigurationTypeNotAllowed){
|
||||||
|
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
} else {
|
||||||
|
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCallbackSuccess(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
|
||||||
|
this.refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
prepareForm(data: TenantConfiguration) {
|
||||||
|
try {
|
||||||
|
this.editorModel = data ? new TenantConfigurationEditorModel().fromModel(data) : new TenantConfigurationEditorModel();
|
||||||
|
this.fileNameDisplay = data?.logo?.storageFile?.fullName;
|
||||||
|
this.buildForm();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Could not parse TenantConfiguration item: ' + data + error);
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm() {
|
||||||
|
this.formGroup = this.editorModel.buildForm(null, !this.authService.hasPermission(AppPermission.EditTenantConfiguration));
|
||||||
|
this.logoEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(): void {
|
||||||
|
this.getItem((entity) => {
|
||||||
|
this.prepareForm(entity);
|
||||||
|
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
|
||||||
|
this.formGroup.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
persistEntity(onSuccess?: (response) => void): void {
|
||||||
|
const formData = this.formService.getValue(this.formGroup.value) as TenantConfigurationPersist;
|
||||||
|
|
||||||
|
this.tenantConfigurationService.persist(formData)
|
||||||
|
.pipe(takeUntil(this._destroyed)).subscribe(
|
||||||
|
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formSubmit(): void {
|
||||||
|
this.clearErrorModel();
|
||||||
|
this.formService.removeAllBackEndErrors(this.formGroup);
|
||||||
|
this.formService.touchAllFormFields(this.formGroup);
|
||||||
|
if (!this.isFormValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.persistEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFormValid() {
|
||||||
|
return this.formGroup.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete() {
|
||||||
|
const value = this.formGroup.value;
|
||||||
|
if (value.id) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
||||||
|
maxWidth: '300px',
|
||||||
|
data: {
|
||||||
|
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.RESET-TO-DEFAULT'),
|
||||||
|
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||||
|
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.tenantConfigurationService.delete(value.id).pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(
|
||||||
|
complete => this.onCallbackDeleteSuccessConfig(),
|
||||||
|
error => this.onCallbackError(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCallbackDeleteSuccessConfig(data?: any): void {
|
||||||
|
|
||||||
|
console.log("Success Delete:", data);
|
||||||
|
|
||||||
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-RESET'), SnackBarNotificationLevel.Success);
|
||||||
|
this.prepareForm(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clearErrorModel() {
|
||||||
|
this.editorModel.validationErrorModel.clear();
|
||||||
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileChangeEvent(fileInput: any, dropped: boolean = false) {
|
||||||
|
|
||||||
|
if (dropped) {
|
||||||
|
this.filesToUpload = fileInput.addedFiles;
|
||||||
|
} else {
|
||||||
|
this.filesToUpload = fileInput.target.files;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.upload();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public upload() {
|
||||||
|
this.storageFileService.uploadTempFiles(this.filesToUpload[0])
|
||||||
|
.pipe(takeUntil(this._destroyed)).subscribe((response) => {
|
||||||
|
this.formGroup.get('logo')?.get('storageFileId').patchValue(response[0].id.toString());
|
||||||
|
this.fileNameDisplay = response[0].name;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
}, error => {
|
||||||
|
this.onCallbackError(error.error);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
download(fileId: Guid): void {
|
||||||
|
|
||||||
|
if (fileId) {
|
||||||
|
|
||||||
|
this.storageFileService.download(fileId).pipe(takeUntil(this._destroyed))
|
||||||
|
.subscribe(response => {
|
||||||
|
const blob = new Blob([response.body]);
|
||||||
|
const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
|
||||||
|
FileSaver.saveAs(blob, filename);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(makeFilesNull: boolean = true) {
|
||||||
|
this.makeFilesNull()
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFilesNull() {
|
||||||
|
this.filesToUpload = null;
|
||||||
|
this.formGroup.get('logo')?.get('storageFileId').patchValue(null);
|
||||||
|
this.formGroup.updateValueAndValidity();
|
||||||
|
this.fileNameDisplay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
|
import { TenantConfigurationType } from "@app/core/common/enum/tenant-configuration-type";
|
||||||
|
import { LogoTenantConfiguration, LogoTenantConfigurationPersist, TenantConfiguration, TenantConfigurationPersist } from "@app/core/model/tenant-configuaration/tenant-configuration";
|
||||||
|
import { BaseEditorModel } from "@common/base/base-form-editor-model";
|
||||||
|
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
|
||||||
|
import { Guid } from "@common/types/guid";
|
||||||
|
|
||||||
|
export class TenantConfigurationEditorModel extends BaseEditorModel implements TenantConfigurationPersist {
|
||||||
|
type: TenantConfigurationType;
|
||||||
|
logo: LogoTenantConfigurationEditorModel = new LogoTenantConfigurationEditorModel(this.validationErrorModel);
|
||||||
|
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor() { super(); this.type = TenantConfigurationType.Logo; }
|
||||||
|
|
||||||
|
public fromModel(item: TenantConfiguration): TenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
super.fromModel(item);
|
||||||
|
this.type = item.type;
|
||||||
|
if (item.logo) this.logo = new LogoTenantConfigurationEditorModel(this.validationErrorModel).fromModel(item.logo);
|
||||||
|
} else {
|
||||||
|
this.type = TenantConfigurationType.Logo;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||||
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
|
||||||
|
type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators],
|
||||||
|
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators],
|
||||||
|
logo: this.logo.buildForm({
|
||||||
|
rootPath: `logo.`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createValidationContext(): ValidationContext {
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||||
|
baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] });
|
||||||
|
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LogoTenantConfigurationEditorModel implements LogoTenantConfigurationPersist {
|
||||||
|
storageFileId: Guid;
|
||||||
|
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public fromModel(item: LogoTenantConfiguration): LogoTenantConfigurationEditorModel {
|
||||||
|
if (item) {
|
||||||
|
if (item.storageFile) this.storageFileId = item.storageFile.id;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm(params?: {
|
||||||
|
context?: ValidationContext,
|
||||||
|
disabled?: boolean,
|
||||||
|
rootPath?: string
|
||||||
|
}): UntypedFormGroup {
|
||||||
|
let { context = null, disabled = false, rootPath } = params ?? {}
|
||||||
|
if (context == null) {
|
||||||
|
context = LogoTenantConfigurationEditorModel.createValidationContext({
|
||||||
|
validationErrorModel: this.validationErrorModel,
|
||||||
|
rootPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const form: UntypedFormGroup = this.formBuilder.group({
|
||||||
|
storageFileId: [{ value: this.storageFileId, disabled: disabled }, context.getValidation('storageFileId').validators],
|
||||||
|
});
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static createValidationContext(params: {
|
||||||
|
rootPath?: string,
|
||||||
|
validationErrorModel: ValidationErrorModel
|
||||||
|
}): ValidationContext {
|
||||||
|
const { rootPath = '', validationErrorModel } = params;
|
||||||
|
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'storageFileId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}storageFileId`)] });
|
||||||
|
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
|
||||||
|
import { StorageFile } from '@app/core/model/storage-file/storage-file';
|
||||||
|
import { LogoTenantConfiguration, TenantConfiguration } from '@app/core/model/tenant-configuaration/tenant-configuration';
|
||||||
|
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
|
||||||
|
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
|
||||||
|
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
|
||||||
|
import { takeUntil, tap } from 'rxjs/operators';
|
||||||
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LogoEditorResolver extends BaseEditorResolver {
|
||||||
|
|
||||||
|
constructor(private tenantConfigurationService: TenantConfigurationService, private breadcrumbService: BreadcrumbService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static lookupFields(): string[] {
|
||||||
|
return [
|
||||||
|
...BaseEditorResolver.lookupFields(),
|
||||||
|
nameof<TenantConfiguration>(x => x.id),
|
||||||
|
nameof<TenantConfiguration>(x => x.type),
|
||||||
|
nameof<TenantConfiguration>(x => x.cssColors),
|
||||||
|
|
||||||
|
[nameof<TenantConfiguration>(x => x.logo), nameof<LogoTenantConfiguration>(x => x.storageFile), nameof<StorageFile>(x => x.id)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.logo), nameof<LogoTenantConfiguration>(x => x.storageFile), nameof<StorageFile>(x => x.name)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.logo), nameof<LogoTenantConfiguration>(x => x.storageFile), nameof<StorageFile>(x => x.extension)].join('.'),
|
||||||
|
[nameof<TenantConfiguration>(x => x.logo), nameof<LogoTenantConfiguration>(x => x.storageFile), nameof<StorageFile>(x => x.fullName)].join('.'),
|
||||||
|
|
||||||
|
|
||||||
|
nameof<TenantConfiguration>(x => x.createdAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.updatedAt),
|
||||||
|
nameof<TenantConfiguration>(x => x.hash),
|
||||||
|
nameof<TenantConfiguration>(x => x.isActive)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve() {
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
...LogoEditorResolver.lookupFields()
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.tenantConfigurationService.getCurrentTenantType(TenantConfigurationType.Logo, fields).pipe(takeUntil(this._destroyed));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LogoEditorService {
|
||||||
|
private validationErrorModel: ValidationErrorModel;
|
||||||
|
|
||||||
|
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
|
||||||
|
this.validationErrorModel = validationErrorModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValidationErrorModel(): ValidationErrorModel {
|
||||||
|
return this.validationErrorModel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,33 @@
|
||||||
<app-tenant-configuration-css-colors-editor></app-tenant-configuration-css-colors-editor>
|
<app-tenant-configuration-css-colors-editor></app-tenant-configuration-css-colors-editor>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.DEPOSIT-PLUGINS.TITLE' | translate}}</mat-panel-title>
|
||||||
|
<mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.DEPOSIT-PLUGINS.HINT' | translate}}</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<app-tenant-configuration-deposit-editor></app-tenant-configuration-deposit-editor>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.FILE-TRANSFORMER-PLUGINS.TITLE' | translate}}</mat-panel-title>
|
||||||
|
<mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.FILE-TRANSFORMER-PLUGINS.HINT' | translate}}</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<app-tenant-configuration-file-transformer-editor></app-tenant-configuration-file-transformer-editor>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.LOGO.TITLE' | translate}}</mat-panel-title>
|
||||||
|
<mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.LOGO.HINT' | translate}}</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<app-tenant-configuration-logo-editor></app-tenant-configuration-logo-editor>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,6 +15,9 @@ import { TenantConfigurationEditorComponent } from './editor/tenant-configuratio
|
||||||
import { CssColorsEditorComponent } from './editor/css-colors/css-colors-editor.component';
|
import { CssColorsEditorComponent } from './editor/css-colors/css-colors-editor.component';
|
||||||
import { DefaultUserLocaleEditorComponent } from './editor/default-user-locale/default-user-locale-editor.component';
|
import { DefaultUserLocaleEditorComponent } from './editor/default-user-locale/default-user-locale-editor.component';
|
||||||
import { FormattingModule } from '@app/core/formatting.module';
|
import { FormattingModule } from '@app/core/formatting.module';
|
||||||
|
import { DepositEditorComponent } from './editor/deposit/deposit-editor.component';
|
||||||
|
import { FileTransformerEditorComponent } from './editor/file-transformer/file-transformer-editor.component';
|
||||||
|
import { LogoEditorComponent } from './editor/logo/logo-editor.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -35,7 +38,10 @@ import { FormattingModule } from '@app/core/formatting.module';
|
||||||
declarations: [
|
declarations: [
|
||||||
TenantConfigurationEditorComponent,
|
TenantConfigurationEditorComponent,
|
||||||
CssColorsEditorComponent,
|
CssColorsEditorComponent,
|
||||||
DefaultUserLocaleEditorComponent
|
DefaultUserLocaleEditorComponent,
|
||||||
|
DepositEditorComponent,
|
||||||
|
FileTransformerEditorComponent,
|
||||||
|
LogoEditorComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class TenantConfigurationModule { }
|
export class TenantConfigurationModule { }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormArray, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TenantService } from '@app/core/services/tenant/tenant.service';
|
import { TenantService } from '@app/core/services/tenant/tenant.service';
|
||||||
|
@ -16,7 +16,6 @@ import { LoggingService } from '@app/core/services/logging/logging-service';
|
||||||
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
||||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||||
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
|
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
|
||||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
|
||||||
import { BaseEditor } from '@common/base/base-editor';
|
import { BaseEditor } from '@common/base/base-editor';
|
||||||
import { FormService } from '@common/forms/form-service';
|
import { FormService } from '@common/forms/form-service';
|
||||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
|
@ -188,5 +187,4 @@ export class TenantEditorComponent extends BaseEditor<TenantEditorModel, Tenant>
|
||||||
this.formService.validateAllFormFields(this.formGroup);
|
this.formService.validateAllFormFields(this.formGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue