diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 67d7597f5f..0ecc9f021a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -28,7 +28,7 @@ - + diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 3219d7b43d..d1e5d13184 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -29,7 +29,7 @@ - + diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index ba3d1b7c93..029014e54a 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -24,12 +24,12 @@ - - - - - - + + + + + + diff --git a/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs b/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs index e4acf17f2f..81584e7e0f 100644 --- a/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs +++ b/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs @@ -10,8 +10,6 @@ using System.Text.Json; using Microsoft.Extensions.Options; using Squidex.Areas.Api.Controllers.UI; -using Squidex.Domain.Apps.Entities.History; -using Squidex.Web; namespace Squidex.Areas.Frontend.Middlewares; @@ -39,28 +37,12 @@ public static string AddOptions(this string html, HttpContext httpContext) { var clonedOptions = uiOptions with { - More = new Dictionary + More = new Dictionary(uiOptions.More) { ["culture"] = CultureInfo.CurrentUICulture.Name } }; - var jsonOptions = httpContext.RequestServices.GetRequiredService(); - - using var jsonDocument = JsonSerializer.SerializeToDocument(uiOptions, jsonOptions); - - if (httpContext.RequestServices.GetService() is ExposedValues values) - { - clonedOptions.More["info"] = values.ToString(); - } - - var notifo = httpContext.RequestServices!.GetService>()?.Value; - - if (notifo?.IsConfigured() == true) - { - clonedOptions.More["notifoApi"] = notifo.ApiUrl; - } - var options = httpContext.Features.Get(); if (options != null) diff --git a/backend/src/Squidex/Config/Domain/FontendServices.cs b/backend/src/Squidex/Config/Domain/FontendServices.cs new file mode 100644 index 0000000000..b16ec298ca --- /dev/null +++ b/backend/src/Squidex/Config/Domain/FontendServices.cs @@ -0,0 +1,58 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Text.Json; +using Microsoft.Extensions.Options; +using Squidex.Areas.Api.Controllers.UI; +using Squidex.Domain.Apps.Entities.History; +using Squidex.Text.ChatBots; +using Squidex.Text.Translations; +using Squidex.Web; + +namespace Squidex.Config.Domain; + +public static class FontendServices +{ + public static void AddSquidexFrontend(this IServiceCollection services) + { + services.Configure((services, options) => + { + var jsonOptions = services.GetRequiredService(); + + using var jsonDocument = JsonSerializer.SerializeToDocument(options, jsonOptions); + + if (services.GetService() is ExposedValues values) + { + options.More["info"] = values.ToString(); + } + }); + + services.Configure((services, options) => + { + var notifo = services.GetRequiredService>().Value; + + if (notifo.IsConfigured()) + { + options.More["notifoApi"] = notifo.ApiUrl; + } + }); + + services.Configure((services, options) => + { + var translator = services.GetRequiredService(); + + options.More["canUseTranslator"] = translator.IsConfigured; + }); + + services.Configure((services, options) => + { + var chatBot = services.GetRequiredService(); + + options.More["canUseChatBot"] = chatBot.IsConfigured; + }); + } +} diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 539b944028..be86b8a1a4 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -62,18 +62,18 @@ - - - - - - - - + + + + + + + + - - - + + + diff --git a/backend/src/Squidex/Startup.cs b/backend/src/Squidex/Startup.cs index 0edaf078d8..0ed289b9c1 100644 --- a/backend/src/Squidex/Startup.cs +++ b/backend/src/Squidex/Startup.cs @@ -50,6 +50,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSquidexContents(config); services.AddSquidexControllerServices(config); services.AddSquidexEventSourcing(config); + services.AddSquidexFrontend(); services.AddSquidexGraphQL(); services.AddSquidexHealthChecks(config); services.AddSquidexHistory(config); diff --git a/frontend/src/app/features/apps/pages/apps-page.component.ts b/frontend/src/app/features/apps/pages/apps-page.component.ts index 846c8ddad4..6ded4e8116 100644 --- a/frontend/src/app/features/apps/pages/apps-page.component.ts +++ b/frontend/src/app/features/apps/pages/apps-page.component.ts @@ -68,8 +68,8 @@ export class AppsPageComponent implements OnInit { private readonly tourState: TourState, private readonly uiOptions: UIOptions, ) { - if (uiOptions.get('showInfo')) { - this.info = uiOptions.get('info'); + if (uiOptions.value.showInfo) { + this.info = uiOptions.value.info; } } @@ -87,7 +87,7 @@ export class AppsPageComponent implements OnInit { this.tourState.complete(); } - if (!this.uiOptions.get('hideNews')) { + if (!this.uiOptions.value.hideNews) { const newsVersion = this.localStore.getInt(Settings.Local.NEWS_VERSION); this.newsService.getFeatures(newsVersion) diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.html b/frontend/src/app/features/content/pages/content/editor/content-field.component.html index 15d826b502..83a39b3ac5 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-field.component.html +++ b/frontend/src/app/features/content/pages/content/editor/content-field.component.html @@ -15,7 +15,7 @@ - + @@ -32,6 +32,7 @@ [formLevel]="formLevel" [formModel]="formModel.get(language)" [isComparing]="!!formModelCompare" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -46,6 +47,7 @@ [formLevel]="formLevel" [formModel]="getControl()" [isComparing]="!!formModelCompare" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -85,6 +87,7 @@ [formLevel]="formLevel" [formModel]="formModelCompare.get(language)" [isComparing]="!!formModelCompare" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -99,7 +102,8 @@ [formModel]="getControlCompare()!" [language]="language" [languages]="languages" - [isComparing]="!!formModelCompare"> + [isComparing]="!!formModelCompare" + [hasChatBot]="hasChatBot"> diff --git a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts b/frontend/src/app/features/content/pages/content/editor/content-field.component.ts index ad3f7d66c9..de4754e9eb 100644 --- a/frontend/src/app/features/content/pages/content/editor/content-field.component.ts +++ b/frontend/src/app/features/content/pages/content/editor/content-field.component.ts @@ -5,9 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, Component, EventEmitter, HostBinding, Input, numberAttribute, Output } from '@angular/core'; +import { booleanAttribute, Component, EventEmitter, HostBinding, inject, Input, numberAttribute, Output } from '@angular/core'; import { Observable } from 'rxjs'; -import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, TypedSimpleChanges } from '@app/shared'; +import { AppLanguageDto, AppsState, changed$, disabled$, EditContentForm, FieldForm, invalid$, LocalStoreService, SchemaDto, Settings, TranslationsService, TypedSimpleChanges, UIOptions } from '@app/shared'; @Component({ selector: 'sqx-content-field', @@ -54,6 +54,9 @@ export class ContentFieldComponent { public isInvalid?: Observable; public isDisabled?: Observable; + public readonly hasTranslator = inject(UIOptions).value.canUseTranslator; + public readonly hasChatBot = inject(UIOptions).value.canUseChatBot; + @HostBinding('class') public get class() { return this.isHalfWidth ? 'col-6 half-field' : 'col-12'; @@ -64,7 +67,7 @@ export class ContentFieldComponent { } public get isTranslatable() { - return this.formModel.field.properties.fieldType === 'String' && this.formModel.field.isLocalizable && this.languages.length > 1; + return this.formModel.field.properties.fieldType === 'String' && this.hasTranslator && this.formModel.field.isLocalizable && this.languages.length > 1; } constructor( diff --git a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts index f9da7f1604..7b1502e175 100644 --- a/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts +++ b/frontend/src/app/features/content/pages/schemas/schemas-page.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -19,7 +19,7 @@ import { AppsState, getCategoryTree, SchemaCategory, SchemasState, Settings, UIO export class SchemasPageComponent { public schemasFilter = new UntypedFormControl(); - public isEmbedded = false; + public readonly isEmbedded = inject(UIOptions).value.embedded; public schemas = this.schemasState.schemas.pipe( @@ -43,11 +43,10 @@ export class SchemasPageComponent { return getCategoryTree(schemas, categories, filter); }); - constructor(uiOptions: UIOptions, + constructor( public readonly schemasState: SchemasState, private readonly appsState: AppsState, ) { - this.isEmbedded = uiOptions.get('embedded'); } public trackByCategory(_index: number, category: SchemaCategory) { diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.html b/frontend/src/app/features/content/shared/forms/array-editor.component.html index efc4373a06..6318bd6582 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.html +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.html @@ -24,7 +24,8 @@ [isFirst]="isFirst" [isLast]="isLast" (itemRemove)="removeItem(i)" - (itemMove)="move(itemForm, $event)" + (itemMove)="move(itemForm, $event)" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -53,6 +54,7 @@ (itemExpanded)="scroll.invalidateCachedMeasurementAtIndex(scroll.viewPortInfo.startIndexWithBuffer + i)" (itemRemove)="removeItem(scroll.viewPortInfo.startIndexWithBuffer + i)" (itemMove)="move(itemForm, $event)" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> diff --git a/frontend/src/app/features/content/shared/forms/array-editor.component.ts b/frontend/src/app/features/content/shared/forms/array-editor.component.ts index 9ead4f4fc2..5272f2edc6 100644 --- a/frontend/src/app/features/content/shared/forms/array-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-editor.component.ts @@ -20,6 +20,9 @@ import { ArrayItemComponent } from './array-item.component'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ArrayEditorComponent { + @Input({ required: true }) + public hasChatBot!: boolean; + @Input({ required: true }) public form!: EditContentForm; diff --git a/frontend/src/app/features/content/shared/forms/array-item.component.html b/frontend/src/app/features/content/shared/forms/array-item.component.html index 0819c0f710..2b9aa3dc12 100644 --- a/frontend/src/app/features/content/shared/forms/array-item.component.html +++ b/frontend/src/app/features/content/shared/forms/array-item.component.html @@ -52,6 +52,7 @@ [formSection]="$any(section)" [index]="index" [isComparing]="isComparing" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> diff --git a/frontend/src/app/features/content/shared/forms/array-item.component.ts b/frontend/src/app/features/content/shared/forms/array-item.component.ts index 1ae46d1e56..2043fcee11 100644 --- a/frontend/src/app/features/content/shared/forms/array-item.component.ts +++ b/frontend/src/app/features/content/shared/forms/array-item.component.ts @@ -30,6 +30,9 @@ export class ArrayItemComponent { @Output() public clone = new EventEmitter(); + @Input({ required: true }) + public hasChatBot!: boolean; + @Input({ required: true }) public form!: EditContentForm; diff --git a/frontend/src/app/features/content/shared/forms/component-section.component.html b/frontend/src/app/features/content/shared/forms/component-section.component.html index 3294d60ae2..59ddb4209c 100644 --- a/frontend/src/app/features/content/shared/forms/component-section.component.html +++ b/frontend/src/app/features/content/shared/forms/component-section.component.html @@ -19,6 +19,7 @@ {{separator!.displayName}} [formModel]="child" [index]="index" [isComparing]="isComparing" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> diff --git a/frontend/src/app/features/content/shared/forms/component-section.component.ts b/frontend/src/app/features/content/shared/forms/component-section.component.ts index 005088dc70..4089067a72 100644 --- a/frontend/src/app/features/content/shared/forms/component-section.component.ts +++ b/frontend/src/app/features/content/shared/forms/component-section.component.ts @@ -16,6 +16,9 @@ import { FieldEditorComponent } from './field-editor.component'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComponentSectionComponent { + @Input({ required: true }) + public hasChatBot!: boolean; + @Input({ required: true }) public form!: EditContentForm; diff --git a/frontend/src/app/features/content/shared/forms/component.component.html b/frontend/src/app/features/content/shared/forms/component.component.html index f8ad865cdf..a7cf481cf9 100644 --- a/frontend/src/app/features/content/shared/forms/component.component.html +++ b/frontend/src/app/features/content/shared/forms/component.component.html @@ -12,6 +12,7 @@ [formLevel]="formLevel + 1" [formSection]="$any(section)" [isComparing]="isComparing" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> diff --git a/frontend/src/app/features/content/shared/forms/component.component.ts b/frontend/src/app/features/content/shared/forms/component.component.ts index 2418e281b4..57b7e13d1c 100644 --- a/frontend/src/app/features/content/shared/forms/component.component.ts +++ b/frontend/src/app/features/content/shared/forms/component.component.ts @@ -17,6 +17,9 @@ import { ComponentSectionComponent } from './component-section.component'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ComponentComponent extends ResourceOwner { + @Input({ required: true }) + public hasChatBot!: boolean; + @Input({ transform: booleanAttribute }) public canUnset?: boolean | null; diff --git a/frontend/src/app/features/content/shared/forms/field-editor.component.html b/frontend/src/app/features/content/shared/forms/field-editor.component.html index c6031418d6..859b9b264a 100644 --- a/frontend/src/app/features/content/shared/forms/field-editor.component.html +++ b/frontend/src/app/features/content/shared/forms/field-editor.component.html @@ -1,7 +1,7 @@ - + AI @@ -54,6 +54,7 @@ [formContext]="formContext" [isComparing]="isComparing" [isExpanded]="isExpanded" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -82,6 +83,7 @@ [formLevel]="formLevel" [formModel]="$any(formModel)" [isComparing]="isComparing" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> @@ -95,6 +97,7 @@ [formContext]="formContext" [isComparing]="isComparing" [isExpanded]="isExpanded" + [hasChatBot]="hasChatBot" [language]="language" [languages]="languages"> diff --git a/frontend/src/app/features/content/shared/forms/field-editor.component.ts b/frontend/src/app/features/content/shared/forms/field-editor.component.ts index 36cc8f023e..4d76f75b0b 100644 --- a/frontend/src/app/features/content/shared/forms/field-editor.component.ts +++ b/frontend/src/app/features/content/shared/forms/field-editor.component.ts @@ -21,6 +21,9 @@ export class FieldEditorComponent { @Output() public expandedChange = new EventEmitter(); + @Input({ required: true }) + public hasChatBot!: boolean; + @Input({ required: true }) public form!: EditContentForm; diff --git a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts index 068e5997ea..80eb6a6a65 100644 --- a/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts +++ b/frontend/src/app/features/content/shared/references/references-checkboxes.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, Input } from '@angular/core'; +import { booleanAttribute, ChangeDetectionStrategy, Component, forwardRef, inject, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import { AppsState, ContentDto, ContentsService, LanguageDto, LocalizerService, StatefulControlComponent, TypedSimpleChanges, UIOptions } from '@app/shared/internal'; import { ReferencesTagsConverter } from './references-tag-converter'; @@ -31,7 +31,7 @@ const NO_EMIT = { emitEvent: false }; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ReferencesCheckboxesComponent extends StatefulControlComponent> { - private readonly itemCount: number; + private readonly itemCount: number = inject(UIOptions).value.referencesDropdownItemCount; private contentItems: ReadonlyArray | null = null; @Input({ required: true }) @@ -51,15 +51,13 @@ export class ReferencesCheckboxesComponent extends StatefulControlComponent { diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts index c9aa4bb18c..001f87032b 100644 --- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { AfterViewInit, booleanAttribute, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, forwardRef, inject, Input, OnInit, Output, ViewChild } from '@angular/core'; import { NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import * as Pikaday from 'pikaday/pikaday'; import { DateHelper, DateTime, StatefulControlComponent, UIOptions } from '@app/framework/internal'; @@ -34,8 +34,8 @@ interface State { changeDetection: ChangeDetectionStrategy.OnPush, }) export class DateTimeEditorComponent extends StatefulControlComponent implements OnInit, AfterViewInit, FocusComponent { - private readonly hideDateButtonsSettings: boolean; - private readonly hideDateTimeModeButtonSetting: boolean; + private readonly hideDateButtonsSettings: boolean = !!inject(UIOptions).value.hideDateButtons; + private readonly hideDateTimeModeButtonSetting: boolean = !!inject(UIOptions).value.hideDateTimeModeButton; private picker: any; private dateTime?: DateTime | null; private suppressEvents = false; @@ -88,11 +88,8 @@ export class DateTimeEditorComponent extends StatefulControlComponent implements AfterViewInit { + private readonly googleMapsKey = inject(UIOptions).value.map.googleMaps.key; private marker: any; private map: any; private value: Geolocation | null = null; + @ViewChild('editor', { static: false }) + public editor!: ElementRef; + + @ViewChild('searchBox', { static: false }) + public searchBoxInput!: ElementRef; + @Input({ transform: booleanAttribute }) public set disabled(value: boolean | undefined | null) { this.setDisabledState(value === true); } - public readonly isGoogleMaps: boolean; - - public get hasValue() { - return !!this.value; - } + public readonly isGoogleMaps = inject(UIOptions).value.map.type !== 'OSM'; public geolocationForm = new ExtendedFormGroup({ @@ -63,23 +66,18 @@ export class GeolocationEditorComponent extends StatefulControlComponent; - - @ViewChild('searchBox', { static: false }) - public searchBoxInput!: ElementRef; + public get hasValue() { + return !!this.value; + } constructor(localStore: LocalStoreService, private readonly resourceLoader: ResourceLoaderService, - private readonly uiOptions: UIOptions, ) { super({ isMapHidden: localStore.getBoolean(Settings.Local.HIDE_MAP) }); this.project(x => x.isMapHidden).subscribe(isMapHidden => { localStore.setBoolean(Settings.Local.HIDE_MAP, isMapHidden); }); - - this.isGoogleMaps = uiOptions.get('map.type') !== 'OSM'; } public hideMap(isMapHidden: boolean) { @@ -161,7 +159,7 @@ export class GeolocationEditorComponent extends StatefulControlComponent= 0) { diff --git a/frontend/src/app/shared/guards/must-be-authenticated.guard.ts b/frontend/src/app/shared/guards/must-be-authenticated.guard.ts index 5f30f2cc6d..9efd816bb2 100644 --- a/frontend/src/app/shared/guards/must-be-authenticated.guard.ts +++ b/frontend/src/app/shared/guards/must-be-authenticated.guard.ts @@ -6,7 +6,7 @@ */ import { Location } from '@angular/common'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; import { map, take, tap } from 'rxjs/operators'; @@ -15,17 +15,16 @@ import { AuthService } from './../services/auth.service'; @Injectable() export class MustBeAuthenticatedGuard { + private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin; + constructor( private readonly authService: AuthService, private readonly location: Location, private readonly router: Router, - private readonly uiOptions: UIOptions, ) { } public canActivate(): Observable { - const redirect = this.uiOptions.get('redirectToLogin'); - return this.authService.userChanges.pipe( take(1), tap(user => { @@ -35,7 +34,7 @@ export class MustBeAuthenticatedGuard { const redirectPath = this.location.path(true); - if (redirect) { + if (this.redirectToLogin) { this.authService.loginRedirect(redirectPath); } else { this.router.navigate([''], { queryParams: { redirectPath } }); diff --git a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts index 5035945314..b8fea978c6 100644 --- a/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts +++ b/frontend/src/app/shared/guards/must-be-not-authenticated.guard.ts @@ -6,7 +6,7 @@ */ import { Location } from '@angular/common'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { map, take, tap } from 'rxjs/operators'; @@ -15,16 +15,17 @@ import { AuthService } from './../services/auth.service'; @Injectable() export class MustBeNotAuthenticatedGuard { + private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin; + constructor( private readonly authService: AuthService, private readonly location: Location, private readonly router: Router, - private readonly uiOptions: UIOptions, ) { } public canActivate(snapshot: ActivatedRouteSnapshot): Observable { - const redirect = this.uiOptions.get('redirectToLogin') && !snapshot.queryParams.logout; + const redirect = this.redirectToLogin && !snapshot.queryParams.logout; return this.authService.userChanges.pipe( take(1), diff --git a/frontend/src/app/shared/state/resolvers.ts b/frontend/src/app/shared/state/resolvers.ts index 959eeba56a..6093fa60fd 100644 --- a/frontend/src/app/shared/state/resolvers.ts +++ b/frontend/src/app/shared/state/resolvers.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { from, Observable, of, shareReplay } from 'rxjs'; import { UIOptions } from '@app/framework'; import { AssetDto, AssetsDto, AssetsService } from './../services/assets.service'; @@ -105,16 +105,13 @@ abstract class ResolverBase { private readonly schemas: { [name: string]: Observable } = {}; - private readonly itemCount; + private readonly itemCount = inject(UIOptions).value.referencesDropdownItemCount; constructor( - uiOptions: UIOptions, private readonly appsState: AppsState, private readonly contentsService: ContentsService, ) { super(); - - this.itemCount = uiOptions.get('referencesDropdownItemCount'); } public resolveAll(schema: string) { diff --git a/frontend/src/app/shared/state/tour.state.ts b/frontend/src/app/shared/state/tour.state.ts index 3db7fd3d39..2ef793de88 100644 --- a/frontend/src/app/shared/state/tour.state.ts +++ b/frontend/src/app/shared/state/tour.state.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Inject, Injectable } from '@angular/core'; +import { inject, Inject, Injectable } from '@angular/core'; import { filter, skip, take } from 'rxjs'; import { debug, State, TourService, UIOptions } from '@app/framework'; import { TASK_CONFIGURATION, TaskConfiguration, TaskDefinition } from './tour.tasks'; @@ -32,6 +32,8 @@ interface Snapshot { @Injectable() export class TourState extends State { + private readonly isDisabled = inject(UIOptions).value.hideOnboarding; + public completedTasks = this.project(x => x.completedTasks); @@ -48,7 +50,6 @@ export class TourState extends State { @Inject(TASK_CONFIGURATION) private readonly definition: TaskConfiguration, private readonly tourService: TourService, private readonly uiState: UIState, - private readonly uiOptions: UIOptions, ) { super({}); @@ -114,10 +115,6 @@ export class TourState extends State { private disableHintCore(key: string) { this.next(s => ({ ...s, shownHints: { ...s.shownHints || {}, [key]: true } }), 'Disable Hint'); } - - private get isDisabled() { - return this.uiOptions.get('hideOnboarding'); - } } const LoadedEvent = 'Loaded'; diff --git a/frontend/src/app/shell/pages/home/home-page.component.ts b/frontend/src/app/shell/pages/home/home-page.component.ts index bcf97fd81f..f31d5517f8 100644 --- a/frontend/src/app/shell/pages/home/home-page.component.ts +++ b/frontend/src/app/shell/pages/home/home-page.component.ts @@ -6,7 +6,7 @@ */ import { Location } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AuthService, UIOptions } from '@app/shared'; @@ -16,6 +16,8 @@ import { AuthService, UIOptions } from '@app/shared'; templateUrl: './home-page.component.html', }) export class HomePageComponent { + private readonly redirectToLogin = inject(UIOptions).value.redirectToLogin; + public showLoginError = false; constructor( @@ -23,7 +25,6 @@ export class HomePageComponent { private readonly location: Location, private readonly route: ActivatedRoute, private readonly router: Router, - private readonly uiOptions: UIOptions, ) { } @@ -32,7 +33,7 @@ export class HomePageComponent { this.route.snapshot.queryParams.redirectPath || this.location.path(); - if (this.isInternetExplorer() || this.uiOptions.get('redirectToLogin')) { + if (this.isInternetExplorer() || this.redirectToLogin) { this.authService.loginRedirect(redirectPath); return; } diff --git a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts b/frontend/src/app/shell/pages/internal/feedback-menu.component.ts index bf82d78010..49c9c181ce 100644 --- a/frontend/src/app/shell/pages/internal/feedback-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/feedback-menu.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit } from '@angular/core'; import markerSDK, { MarkerSdk } from '@marker.io/browser'; import { UIOptions } from '@app/shared'; @@ -18,12 +18,7 @@ import { UIOptions } from '@app/shared'; export class FeedbackMenuComponent implements OnInit, OnDestroy { private widget?: MarkerSdk; - public markerProject = ''; - - constructor(uiOptions: UIOptions, - ) { - this.markerProject = uiOptions.get('markerProject'); - } + public readonly markerProject = inject(UIOptions).value.markerProject; public ngOnDestroy() { this.widget?.unload(); diff --git a/frontend/src/app/shell/pages/internal/internal-area.component.ts b/frontend/src/app/shell/pages/internal/internal-area.component.ts index d7c9153e17..9234862803 100644 --- a/frontend/src/app/shell/pages/internal/internal-area.component.ts +++ b/frontend/src/app/shell/pages/internal/internal-area.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { DialogService, LoadingService, Notification, ResourceOwner, UIOptions } from '@app/shared'; @@ -15,16 +15,14 @@ import { DialogService, LoadingService, Notification, ResourceOwner, UIOptions } templateUrl: './internal-area.component.html', }) export class InternalAreaComponent extends ResourceOwner implements OnInit { - public isEmbedded = false; + public readonly isEmbedded = inject(UIOptions).value.embedded; - constructor(uiOptions: UIOptions, + constructor( public readonly loadingService: LoadingService, private readonly dialogs: DialogService, private readonly route: ActivatedRoute, ) { super(); - - this.isEmbedded = !!uiOptions.get('embedded'); } public ngOnInit() { diff --git a/frontend/src/app/shell/pages/internal/notifications-menu.component.ts b/frontend/src/app/shell/pages/internal/notifications-menu.component.ts index 20127c0d9e..e129ec35a9 100644 --- a/frontend/src/app/shell/pages/internal/notifications-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/notifications-menu.component.ts @@ -20,7 +20,7 @@ export class NotificationsMenuComponent { constructor(authService: AuthService, uiOptions: UIOptions, ) { const notifoApiKey = authService.user?.notifoToken; - const notifoApiUrl = uiOptions.get('notifoApi'); + const notifoApiUrl = uiOptions.value.notifoAPi; this.isNotifoConfigured = !!notifoApiKey && !!notifoApiUrl; } diff --git a/frontend/src/app/shell/pages/internal/profile-menu.component.ts b/frontend/src/app/shell/pages/internal/profile-menu.component.ts index dff5f6dda4..f882d5ca6e 100644 --- a/frontend/src/app/shell/pages/internal/profile-menu.component.ts +++ b/frontend/src/app/shell/pages/internal/profile-menu.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { ApiUrlConfig, AuthService, Cookies, ModalModel, StatefulComponent, UILanguages, UIOptions, UIState } from '@app/shared'; interface State { @@ -32,10 +32,10 @@ interface State { changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProfileMenuComponent extends StatefulComponent implements OnInit { - public modalMenu = new ModalModel(); + public readonly modalMenu = new ModalModel(); - public language = this.uiOptions.get('culture'); - public languages = UILanguages.ALL; + public readonly language = inject(UIOptions).value.culture; + public readonly languages = UILanguages.ALL; constructor(apiUrl: ApiUrlConfig, public readonly uiState: UIState,