>('/api/mail/config/template', defaultHttpOptionsFromConfig(config));
+ }
}
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
index 4524880098..2762e0d43a 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
+++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
@@ -37,111 +37,306 @@
{{ 'admin.mail-from-required' | translate }}
+
- admin.smtp-protocol
-
-
- {{protocol.toUpperCase()}}
-
-
-
-
-
- admin.smtp-host
-
-
- {{ 'admin.smtp-host-required' | translate }}
-
-
-
- admin.smtp-port
-
- {{smtpPortInput.value?.length || 0}}/5
-
- {{ 'admin.smtp-port-required' | translate }}
-
-
- {{ 'admin.smtp-port-invalid' | translate }}
-
-
-
-
- admin.timeout-msec
-
- {{timeoutInput.value?.length || 0}}/6
-
- {{ 'admin.timeout-required' | translate }}
-
-
- {{ 'admin.timeout-invalid' | translate }}
-
-
-
- {{ 'admin.enable-tls' | translate }}
-
-
- admin.tls-version
-
-
- {{ tlsVersion }}
+ admin.oauth2.smtp-provider
+
+
+ {{ templates.get(provider)?.name || 'Custom' }}
-
- {{ 'admin.enable-proxy' | translate }}
-
-
-
- common.username
-
-
-
- {{ 'admin.change-password' | translate }}
-
-
- common.password
-
-
-
+
+
+ {{ 'admin.oauth2.basic' | translate }}
+ {{ 'admin.oauth2.oauth2' | translate }}
+
+
+
+
+
+
+
+ admin.oauth2.access-token-status
+
+ {{ accessTokenStatus }}
+
+
+
+
+
-
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss
index 66df772d2d..cd6c18af9e 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss
@@ -13,6 +13,95 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+@import "../../../../../theme";
+
:host {
+ .fields-group {
+ padding: 0 8px 8px;
+ margin: 10px 0;
+ border: 1px groove rgba(0, 0, 0, .25);
+ border-radius: 4px;
+ legend {
+ margin-bottom: 8px;
+ color: rgba(0, 0, 0, .7);
+ width: fit-content;
+ }
+ }
+
+ .token-status {
+ font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif;
+ color: rgba(0,0,0, 0.6);
+ letter-spacing: 0.25px;
+ padding: 8px 0;
+ }
+
+ ::ng-deep{
+ .mat-expansion-panel {
+ .mat-expansion-panel-header {
+ height: 48px;
+ padding: 0 12px;
+ &.mat-expanded {
+ height: 48px;
+ }
+ }
+ .mat-expansion-panel-body {
+ padding: 0 12px;
+ }
+ &.configuration-panel {
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ }
+ }
+ .mat-button-toggle-group.tb-notification-unread-toggle-group {
+ &.mat-button-toggle-group-appearance-standard {
+ border: none;
+ border-radius: 14px;
+
+ .mat-button-toggle + .mat-button-toggle {
+ border-left: none;
+ }
+ }
+
+ .mat-button-toggle {
+ background: rgba(0, 0, 0, 0.06);
+ height: 28px;
+ align-items: center;
+ display: flex;
+
+ .mat-button-toggle-ripple {
+ top: 2px;
+ left: 2px;
+ right: 2px;
+ bottom: 2px;
+ border-radius: 12px;
+ }
+ }
+
+ .mat-button-toggle-button {
+ color: #959595;
+ }
+
+ .mat-button-toggle-focus-overlay {
+ border-radius: 14px;
+ margin: 2px;
+ }
+
+ .mat-button-toggle-checked .mat-button-toggle-button {
+ background-color: $tb-primary-color;
+ color: #fff;
+ border-radius: 14px;
+ margin-left: 2px;
+ margin-right: 2px;
+ }
+
+ .mat-button-toggle-appearance-standard .mat-button-toggle-label-content {
+ line-height: 24px;
+ font-size: 16px;
+ font-weight: 500;
+ }
+ .mat-button-toggle-checked.mat-button-toggle-appearance-standard:not(.mat-button-toggle-disabled):hover .mat-button-toggle-focus-overlay {
+ opacity: .01;
+ }
+ }
+ }
}
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts
index 7833c745f4..6815c81e31 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts
@@ -14,20 +14,32 @@
/// limitations under the License.
///
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
-import { Router } from '@angular/router';
-import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
-import { AdminSettings, MailServerSettings, smtpPortPattern } from '@shared/models/settings.models';
+import { FormBuilder, FormGroup, UntypedFormArray, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import {
+ AdminSettings,
+ MailConfigTemplate,
+ MailServerOauth2Provider,
+ MailServerSettings,
+ smtpPortPattern,
+ SmtpProtocol
+} from '@shared/models/settings.models';
import { AdminService } from '@core/http/admin.service';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
-import { isDefinedAndNotNull, isString } from '@core/utils';
-import { Subject } from 'rxjs';
+import { isDefined, isDefinedAndNotNull, isString } from '@core/utils';
+import { forkJoin, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
+import { DomainSchema, domainSchemaTranslations, } from '@shared/models/oauth2.models';
+import { WINDOW } from '@core/services/window.service';
+import { AuthService } from '@core/auth/auth.service';
+import { COMMA, ENTER } from '@angular/cdk/keycodes';
+import { MatChipInputEvent } from '@angular/material/chips';
@Component({
selector: 'tb-mail-server',
@@ -35,40 +47,138 @@ import { takeUntil } from 'rxjs/operators';
styleUrls: ['./mail-server.component.scss', './settings-card.scss']
})
export class MailServerComponent extends PageComponent implements OnInit, OnDestroy, HasConfirmForm {
-
- mailSettings: UntypedFormGroup;
adminSettings: AdminSettings;
- smtpProtocols = ['smtp', 'smtps'];
+ smtpProtocols = Object.values(SmtpProtocol);
showChangePassword = false;
+ protocols = Object.values(DomainSchema).filter(value => value !== DomainSchema.MIXED);
+ domainSchemaTranslations = domainSchemaTranslations;
+
+ mailServerOauth2Provider = MailServerOauth2Provider;
+
tlsVersions = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'];
+ helpLink: string;
+
+ templates = new Map();
+
+ templateProvider = ['CUSTOM'];
+
+ readonly separatorKeysCodes: number[] = [ENTER, COMMA];
+
private destroy$ = new Subject();
+ private DOMAIN_AND_PORT_REGEXP = /^(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?$/;
+ private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/;
+ private loginProcessingUrl: string;
+
+ mailSettings = this.fb.group({
+ mailFrom: ['', [Validators.required]],
+ smtpProtocol: [SmtpProtocol.SMTP],
+ smtpHost: ['localhost', [Validators.required]],
+ smtpPort: [25, [Validators.required,
+ Validators.pattern(smtpPortPattern),
+ Validators.maxLength(5)]],
+ timeout: [10000, [Validators.required,
+ Validators.pattern(/^[0-9]{1,6}$/),
+ Validators.maxLength(6)]],
+ enableTls: [false],
+ tlsVersion: [{ value: null, disabled: true }],
+ enableProxy: [false],
+ proxyHost: [{ value: '', disabled: true }, [Validators.required]],
+ proxyPort: [{ value: null, disabled: true }, [Validators.required, Validators.min(1), Validators.max(65535)]],
+ proxyUser: [{ value: '', disabled: true }],
+ proxyPassword: [{ value: '', disabled: true }],
+ username: [''],
+ changePassword: [false],
+ password: [''],
+ enableOauth2: [false],
+ providerId: ['CUSTOM', [Validators.required]],
+ clientId: [{ value:'', disabled: true }, [Validators.required, Validators.maxLength(255)]],
+ clientSecret: [{ value:'', disabled: true }, [Validators.required, Validators.maxLength(2048)]],
+ providerTenantId: [{value: '', disabled: true}, [Validators.required]],
+ authUri: [{value: '', disabled: true}, [Validators.required, Validators.pattern(this.URL_REGEXP)]],
+ tokenUri: [{value: '', disabled: true}, [Validators.required, Validators.pattern(this.URL_REGEXP)]],
+ scope: [],
+ redirectUri: [{ value:'', disabled: true}]
+ });
+
+ private defaultConfiguration = {
+ providerId: 'CUSTOM',
+ smtpProtocol: SmtpProtocol.SMTP,
+ smtpHost: '',
+ smtpPort: null,
+ timeout: null,
+ enableTls: false,
+ tlsVersion: null,
+ enableProxy: false,
+ proxyHost: '',
+ proxyPort: null,
+ proxyUser: '',
+ proxyPassword: '',
+ enableOauth2: false,
+ clientId: '',
+ clientSecret: '',
+ providerTenantId: '',
+ authUri: '',
+ tokenUri: '',
+ scope: [],
+ redirectUri: ''
+ };
+
+ domainForm = this.fb.group({
+ name: [this.window.location.hostname, [
+ Validators.required, Validators.maxLength(255),
+ Validators.pattern(this.DOMAIN_AND_PORT_REGEXP)]
+ ],
+ scheme: [DomainSchema.HTTPS, Validators.required]
+ });
constructor(protected store: Store,
private router: Router,
+ private route: ActivatedRoute,
private adminService: AdminService,
+ private authService: AuthService,
private translate: TranslateService,
- public fb: UntypedFormBuilder) {
+ public fb: FormBuilder,
+ @Inject(WINDOW) private window: Window) {
super(store);
}
ngOnInit() {
- this.buildMailServerSettingsForm();
- this.adminService.getAdminSettings('mail').subscribe(
- (adminSettings) => {
- this.adminSettings = adminSettings;
- if (this.adminSettings.jsonValue && isString(this.adminSettings.jsonValue.enableTls)) {
- this.adminSettings.jsonValue.enableTls = (this.adminSettings.jsonValue.enableTls as any) === 'true';
- }
- this.showChangePassword =
- isDefinedAndNotNull(this.adminSettings.jsonValue.showChangePassword) ? this.adminSettings.jsonValue.showChangePassword : true ;
- delete this.adminSettings.jsonValue.showChangePassword;
- this.mailSettings.reset(this.adminSettings.jsonValue);
- this.enableMailPassword(!this.showChangePassword);
- this.enableProxyChanged();
+ this.mailServerSettingsForm();
+ this.domainFormConfiguration();
+
+ forkJoin([
+ this.adminService.getLoginProcessingUrl(),
+ this.adminService.getMailConfigTemplate(),
+ this.adminService.getAdminSettings('mail')
+ ]).subscribe(([loginProcessingUrl, mailConfigTemplate, adminSettings]) => {
+ this.loginProcessingUrl = loginProcessingUrl;
+ this.initTemplates(mailConfigTemplate);
+ this.adminSettings = adminSettings;
+ if (this.adminSettings.jsonValue && isString(this.adminSettings.jsonValue.enableTls)) {
+ this.adminSettings.jsonValue.enableTls = (this.adminSettings.jsonValue.enableTls as any) === 'true';
}
- );
+ this.showChangePassword = isDefinedAndNotNull(this.adminSettings.jsonValue.showChangePassword)
+ ? this.adminSettings.jsonValue.showChangePassword : true;
+ delete this.adminSettings.jsonValue.showChangePassword;
+ if (!this.adminSettings.jsonValue.providerId) {
+ this.adminSettings.jsonValue.providerId = 'CUSTOM';
+ }
+ this.mailSettings.reset(this.adminSettings.jsonValue, {emitEvent: false});
+ this.enableMailPassword(!this.showChangePassword);
+ this.enableProxyChanged();
+ this.enableTls(this.adminSettings.jsonValue.enableTls);
+ this.helpLink = this.templates.get(this.adminSettings.jsonValue.providerId)?.helpLink || null;
+ if (this.adminSettings.jsonValue.enableOauth2) {
+ this.enableOauth2(!!this.adminSettings.jsonValue.enableOauth2);
+ this.enableProviderTenantIdChanged(this.adminSettings.jsonValue.providerId);
+ this.parseUrl(this.adminSettings.jsonValue.redirectUri);
+ this.mailSettings.get('redirectUri').patchValue(this.adminSettings.jsonValue.redirectUri, {emitEvent: false});
+ } else {
+ this.mailSettings.get('enableOauth2').patchValue(false, {emitEvent: false});
+ }
+ });
}
ngOnDestroy() {
@@ -77,56 +187,153 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest
super.ngOnDestroy();
}
- buildMailServerSettingsForm() {
- this.mailSettings = this.fb.group({
- mailFrom: ['', [Validators.required]],
- smtpProtocol: ['smtp'],
- smtpHost: ['localhost', [Validators.required]],
- smtpPort: ['25', [Validators.required,
- Validators.pattern(smtpPortPattern),
- Validators.maxLength(5)]],
- timeout: ['10000', [Validators.required,
- Validators.pattern(/^[0-9]{1,6}$/),
- Validators.maxLength(6)]],
- enableTls: [false],
- tlsVersion: [],
- enableProxy: [false, []],
- proxyHost: ['', [Validators.required]],
- proxyPort: ['', [Validators.required, Validators.min(1), Validators.max(65535)]],
- proxyUser: [''],
- proxyPassword: [''],
- username: [''],
- changePassword: [false],
- password: ['']
+ private initTemplates(templates): void {
+ templates.map(provider => {
+ delete provider.additionalInfo;
+ this.templates.set(provider.providerId, provider);
});
+ this.templateProvider.push(...Array.from(this.templates.keys()));
+ this.templateProvider.sort();
+ }
+
+ private mailServerSettingsForm(): void {
this.registerDisableOnLoadFormControl(this.mailSettings.get('smtpProtocol'));
this.registerDisableOnLoadFormControl(this.mailSettings.get('enableTls'));
this.registerDisableOnLoadFormControl(this.mailSettings.get('enableProxy'));
this.registerDisableOnLoadFormControl(this.mailSettings.get('changePassword'));
+
+ this.mailSettings.get('enableTls').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(value => this.enableTls(value));
+
this.mailSettings.get('enableProxy').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.enableProxyChanged();
});
+
this.mailSettings.get('changePassword').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.enableMailPassword(value);
});
+
+ this.mailSettings.get('enableOauth2').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe( value => {
+ this.enableOauth2(value);
+ this.enableProviderTenantIdChanged(this.mailSettings.get('providerId').value);
+ if (value && !this.mailSettings.get('redirectUri').value) {
+ this.mailSettings.get('redirectUri').patchValue(this.redirectURI(), {emitEvent: false});
+ }
+ });
+
+ this.mailSettings.get('providerTenantId').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(tenantId => {
+ const authorizationUri = this.templates.get(this.mailServerOauth2Provider.OFFICE_365).authorizationUri.replace('%s', `${tenantId}`);
+ const accessTokenUri = this.templates.get(this.mailServerOauth2Provider.OFFICE_365).accessTokenUri.replace('%s', `${tenantId}`);
+ this.mailSettings.get('authUri').patchValue(authorizationUri, {emitEvent: false});
+ this.mailSettings.get('tokenUri').patchValue(accessTokenUri, {emitEvent: false});
+ });
+
+ this.mailSettings.get('providerId').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe( value => {
+ if (value === this.mailServerOauth2Provider.CUSTOM || !value) {
+ this.mailSettings.reset({...this.adminSettings.jsonValue, ...this.defaultConfiguration}, {emitEvent: false});
+ } else {
+ const config = this.templates.get(value);
+ this.helpLink = config.helpLink;
+ this.mailSettings.patchValue({
+ smtpProtocol: SmtpProtocol[config.smtpProtocol],
+ smtpHost: config.smtpHost,
+ smtpPort: config.smtpPort,
+ timeout: config.timeout,
+ enableTls: config.enableTls,
+ tlsVersion: config.tlsVersion,
+ authUri: config.authorizationUri,
+ tokenUri: config.accessTokenUri,
+ scope: config.scope,
+ enableOauth2: false,
+ enableProxy: false,
+ proxyHost: '',
+ proxyPort: null,
+ proxyUser: '',
+ proxyPassword: '',
+ clientId: '',
+ clientSecret: '',
+ providerTenantId: '',
+ redirectUri: ''
+ }, {emitEvent: false});
+ }
+ this.enableTls(this.mailSettings.get('enableTls').value);
+ this.enableOauth2(this.mailSettings.get('enableOauth2').value);
+ this.enableProviderTenantIdChanged(value);
+ });
+ }
+
+ private domainFormConfiguration(): void {
+ this.domainForm.get('name').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(
+ value => this.mailSettings.get('redirectUri').patchValue(
+ this.redirectURI(this.domainForm.get('scheme').value, value),
+ {emitEvent: false}
+ )
+ );
+ this.domainForm.get('scheme').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(
+ (value) => this.mailSettings.get('redirectUri').patchValue(this.redirectURI(value), {emitEvent: false})
+ );
}
- enableProxyChanged(): void {
+ private enableOauth2(value: boolean): void {
+ if (value) {
+ this.mailSettings.get('clientId').enable({emitEvent: false});
+ this.mailSettings.get('clientSecret').enable({emitEvent: false});
+ this.mailSettings.get('redirectUri').enable({emitEvent: false});
+ if (this.mailSettings.get('providerId').value === this.mailServerOauth2Provider.CUSTOM) {
+ this.mailSettings.get('authUri').enable({emitEvent: false});
+ this.mailSettings.get('tokenUri').enable({emitEvent: false});
+ } else {
+ this.mailSettings.get('authUri').disable({emitEvent: false});
+ this.mailSettings.get('tokenUri').disable({emitEvent: false});
+ }
+ } else {
+ this.mailSettings.get('clientId').disable({emitEvent: false});
+ this.mailSettings.get('clientSecret').disable({emitEvent: false});
+ this.mailSettings.get('redirectUri').disable({emitEvent: false});
+ this.mailSettings.get('authUri').disable({emitEvent: false});
+ this.mailSettings.get('tokenUri').disable({emitEvent: false});
+ }
+ }
+
+ private enableProviderTenantIdChanged(value: string): void {
+ if (value === this.mailServerOauth2Provider.OFFICE_365 && this.mailSettings.get('enableOauth2').value) {
+ this.mailSettings.get('providerTenantId').enable({emitEvent: false});
+ } else {
+ this.mailSettings.get('providerTenantId').disable({emitEvent: false});
+ }
+ }
+
+ private enableProxyChanged(): void {
const enableProxy: boolean = this.mailSettings.get('enableProxy').value;
if (enableProxy) {
- this.mailSettings.get('proxyHost').enable();
- this.mailSettings.get('proxyPort').enable();
+ this.mailSettings.get('proxyHost').enable({emitEvent: false});
+ this.mailSettings.get('proxyPort').enable({emitEvent: false});
+ this.mailSettings.get('proxyUser').enable({emitEvent: false});
+ this.mailSettings.get('proxyPassword').enable({emitEvent: false});
} else {
- this.mailSettings.get('proxyHost').disable();
- this.mailSettings.get('proxyPort').disable();
+ this.mailSettings.get('proxyHost').disable({emitEvent: false});
+ this.mailSettings.get('proxyPort').disable({emitEvent: false});
+ this.mailSettings.get('proxyUser').disable({emitEvent: false});
+ this.mailSettings.get('proxyPassword').disable({emitEvent: false});
}
}
- enableMailPassword(enable: boolean) {
+ private enableMailPassword(enable: boolean) {
if (enable) {
this.mailSettings.get('password').enable({emitEvent: false});
} else {
@@ -134,14 +341,21 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest
}
}
+ private enableTls(enable: boolean): void {
+ if (enable) {
+ this.mailSettings.get('tlsVersion').enable({emitEvent: false});
+ } else {
+ this.mailSettings.get('tlsVersion').disable({emitEvent: false});
+ }
+ }
+
sendTestMail(): void {
this.adminSettings.jsonValue = {...this.adminSettings.jsonValue, ...this.mailSettingsFormValue};
- this.adminService.sendTestMail(this.adminSettings).subscribe(
- () => {
- this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('admin.test-mail-sent'),
- type: 'success' }));
- }
- );
+ this.adminService.sendTestMail(this.adminSettings).subscribe({
+ next: () => this.store.dispatch(new ActionNotificationShow({ message: this.translate.instant('admin.test-mail-sent'),
+ type: 'success' })),
+ error: error => this.store.dispatch(new ActionNotificationShow({message: error.error.message, type: 'error'}))
+ });
}
save(): void {
@@ -150,18 +364,88 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest
(adminSettings) => {
this.adminSettings = adminSettings;
this.showChangePassword = true;
- this.mailSettings.reset(this.adminSettings.jsonValue);
+ this.mailSettings.reset(this.adminSettings.jsonValue, {emitEvent: false});
+ this.domainForm.reset(this.domainForm.value);
+ this.parseUrl(this.adminSettings.jsonValue.redirectUri);
}
);
}
- confirmForm(): UntypedFormGroup {
+ generateAccessToken(): void {
+ this.adminService.generateAccessToken().subscribe(
+ uri => this.window.location.href = uri
+ );
+ }
+
+ redirectURI(schema?: DomainSchema, name?: string): string {
+ const domainInfo = this.domainForm.value;
+ if (domainInfo.name !== '') {
+ const protocol = isDefined(schema) ? schema.toLowerCase() : domainInfo.scheme.toLowerCase();
+ const domainName = isDefined(name) ? name : domainInfo.name;
+ return `${protocol}://${domainName}${this.loginProcessingUrl}`;
+ }
+ return '';
+ }
+
+ private parseUrl(value: string): void {
+ if (value) {
+ const url = new URL(value);
+ this.domainForm.get('scheme').patchValue(
+ url.protocol.startsWith('https') ? DomainSchema.HTTPS : DomainSchema.HTTP, {emitEvent: false}
+ );
+ this.domainForm.get('name').patchValue(url.host, {emitEvent: false});
+ }
+ }
+
+ get accessTokenButtonName(): string {
+ return this.translate.instant(
+ this.adminSettings.jsonValue.tokenGenerated ? 'admin.oauth2.update-access-token' : 'admin.oauth2.generate-access-token'
+ );
+ }
+
+ get accessTokenStatus(): string {
+ return this.translate.instant(
+ this.adminSettings.jsonValue.tokenGenerated ? 'admin.oauth2.token-status-generated' : 'admin.oauth2.token-status-not-generated'
+ );
+ }
+
+ confirmForm(): FormGroup {
return this.mailSettings;
}
private get mailSettingsFormValue(): MailServerSettings {
- const formValue = this.mailSettings.value;
+ const formValue = this.mailSettings.getRawValue() as Required;
delete formValue.changePassword;
return formValue;
}
+
+ trackByParams(index: number): number {
+ return index;
+ }
+
+ removeScope(i: number): void {
+ const controller = this.mailSettings.get('scope') as UntypedFormArray;
+ controller.removeAt(i);
+ controller.markAsTouched();
+ controller.markAsDirty();
+ }
+
+ addScope(event: MatChipInputEvent): void {
+ const input = event.chipInput.inputElement;
+ const value = event.value;
+ const controller = this.mailSettings.get('scope') as UntypedFormArray;
+ if ((value.trim() !== '')) {
+ controller.push(this.fb.control(value.trim()));
+ controller.markAsDirty();
+ }
+
+ if (input) {
+ input.value = '';
+ }
+ }
+
+ toggleEditMode(path: string): void {
+ this.mailSettings.get(path).disabled ? this.mailSettings.get(path).enable() : this.mailSettings.get(path).disable();
+ }
+
}
diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts
index 0cb02d6257..14f601dbe4 100644
--- a/ui-ngx/src/app/shared/models/settings.models.ts
+++ b/ui-ngx/src/app/shared/models/settings.models.ts
@@ -17,6 +17,7 @@
import { ValidatorFn } from '@angular/forms';
import { isNotEmptyStr, isNumber } from '@core/utils';
import { VersionCreateConfig } from '@shared/models/vc.models';
+import { HasUUID } from '@shared/models/id/has-uuid';
export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
@@ -25,16 +26,20 @@ export interface AdminSettings {
jsonValue: T;
}
-export declare type SmtpProtocol = 'smtp' | 'smtps';
+export enum SmtpProtocol {
+ SMTP = 'smtp',
+ SMTPS = 'smtps'
+}
export interface MailServerSettings {
- showChangePassword: boolean;
+ showChangePassword?: boolean;
mailFrom: string;
smtpProtocol: SmtpProtocol;
smtpHost: string;
smtpPort: number;
timeout: number;
enableTls: boolean;
+ tlsVersion: string;
username: string;
changePassword?: boolean;
password?: string;
@@ -43,6 +48,39 @@ export interface MailServerSettings {
proxyPort: number;
proxyUser: string;
proxyPassword: string;
+ enableOauth2: boolean;
+ providerId?: string;
+ clientId?: string;
+ clientSecret?: string;
+ providerTenantId?: string;
+ authUri?: string;
+ tokenUri?: string;
+ scope?: Array;
+ redirectUri?: string;
+ tokenGenerated?: boolean;
+}
+
+export enum MailServerOauth2Provider {
+ OFFICE_365 = 'OFFICE_365',
+ CUSTOM = 'CUSTOM'
+}
+
+export interface MailConfigTemplate {
+ id: HasUUID;
+ createdTime: number;
+ name: string;
+ providerId: string;
+ helpLink: string;
+ scope: Array;
+ accessTokenUri: string;
+ authorizationUri: string;
+ enableTls: boolean;
+ tlsVersion: string;
+ smtpProtocol: SmtpProtocol;
+ smtpHost: string;
+ smtpPort: number;
+ timeout: number;
+ additionalInfo: any;
}
export interface GeneralSettings {
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index bd05cfef00..a7c399a67f 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -174,6 +174,7 @@
"domain-name-unique": "Domain name and protocol need to unique.",
"domain-name-max-length": "Domain name should be less than 256",
"error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io",
+ "connection-settings": "Connection settings",
"oauth2": {
"access-token-uri": "Access token URI",
"access-token-uri-required": "Access token URI is required.",
@@ -264,7 +265,28 @@
"platform-android": "Android",
"platform-ios": "iOS",
"all-platforms": "All platforms",
- "allowed-platforms": "Allowed platforms"
+ "smtp-provider": "SMTP provider",
+ "allowed-platforms": "Allowed platforms",
+ "authentication": "Authentication",
+ "basic": "Basic",
+ "provider": "Provider",
+ "redirect-url": "Redirect URI",
+ "domain-name": "Domain name",
+ "redirect-url-template": "Redirect URI template",
+ "microsoft-tenant-id": "Directory (tenant) Id",
+ "microsoft-tenant-id-required": "Directory (tenant) Id is required",
+ "token-uri": "Token URI",
+ "token-uri-required": "Token URI is required",
+ "redirect-uri": "Redirect URI",
+ "google-provider": "Google",
+ "microsoft-provider": "Office 365",
+ "sendgrid-provider": "Sendgrid",
+ "custom-provider": "Custom",
+ "generate-access-token": "Generate access token",
+ "update-access-token": "Update access token",
+ "access-token-status": "Access token status:",
+ "token-status-generated": "generated",
+ "token-status-not-generated": "not generated"
},
"smpp-provider": {
"smpp-version": "SMPP version",