diff --git a/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs b/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs index 8bf6d9666..e8d3e643c 100644 --- a/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs +++ b/src/Squidex.Infrastructure/Assets/FolderAssetStore.cs @@ -56,7 +56,7 @@ namespace Squidex.Infrastructure.Assets try { - using (var fileStream = file.OpenWrite()) + using (var fileStream = file.OpenRead()) { await fileStream.CopyToAsync(stream); } diff --git a/src/Squidex/Config/Domain/StoreMongoDbModule.cs b/src/Squidex/Config/Domain/StoreMongoDbModule.cs index 8ff868297..de5f51eef 100644 --- a/src/Squidex/Config/Domain/StoreMongoDbModule.cs +++ b/src/Squidex/Config/Domain/StoreMongoDbModule.cs @@ -137,7 +137,6 @@ namespace Squidex.Config.Domain builder.RegisterType() .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseRegistration)) .As() - .As() .As() .AsSelf() .SingleInstance(); diff --git a/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs b/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs index ea5b76c7b..5a606f5d2 100644 --- a/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs +++ b/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs @@ -7,6 +7,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; @@ -94,7 +95,7 @@ namespace Squidex.Controllers.Api.Apps { await CommandBus.PublishAsync(SimpleMapper.Map(request, new AddLanguage())); - var response = SimpleMapper.Map(request.Language, new AppLanguageDto()); + var response = SimpleMapper.Map(request.Language, new AppLanguageDto { Fallback = new List() }); return CreatedAtAction(nameof(GetLanguages), new { app }, response); } diff --git a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts index 40cdf4ba3..7007735e6 100644 --- a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts +++ b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts @@ -55,13 +55,10 @@ export class EventConsumersPageComponent extends ComponentBase implements OnInit public start(name: string) { this.eventConsumersService.startEventConsumer(name) .subscribe(() => { - this.eventConsumers = this.eventConsumers.map(e => { - if (e.name === name) { - return new EventConsumerDto(name, e.lastHandledEventNumber, false, e.isResetting, e.error); - } else { - return e; - } - }); + this.eventConsumers = + this.eventConsumers.replaceAll( + e => e.name === name, + e => new EventConsumerDto(name, e.lastHandledEventNumber, false, e.isResetting, e.error)); }, error => { this.notifyError(error); }); @@ -70,13 +67,10 @@ export class EventConsumersPageComponent extends ComponentBase implements OnInit public stop(name: string) { this.eventConsumersService.stopEventConsumer(name) .subscribe(() => { - this.eventConsumers = this.eventConsumers.map(e => { - if (e.name === name) { - return new EventConsumerDto(name, e.lastHandledEventNumber, true, e.isResetting, e.error); - } else { - return e; - } - }); + this.eventConsumers = + this.eventConsumers.replaceAll( + e => e.name === name, + e => new EventConsumerDto(name, e.lastHandledEventNumber, true, e.isResetting, e.error)); }, error => { this.notifyError(error); }); @@ -85,13 +79,10 @@ export class EventConsumersPageComponent extends ComponentBase implements OnInit public reset(name: string) { this.eventConsumersService.resetEventConsumer(name) .subscribe(() => { - this.eventConsumers = this.eventConsumers.map(e => { - if (e.name === name) { - return new EventConsumerDto(name, e.lastHandledEventNumber, e.isStopped, true, e.error); - } else { - return e; - } - }); + this.eventConsumers = + this.eventConsumers.replaceAll( + e => e.name === name, + e => new EventConsumerDto(name, e.lastHandledEventNumber, e.isStopped, true, e.error)); }, error => { this.notifyError(error); }); diff --git a/src/Squidex/app/features/administration/pages/users/users-page.component.ts b/src/Squidex/app/features/administration/pages/users/users-page.component.ts index f31ceb210..330db72da 100644 --- a/src/Squidex/app/features/administration/pages/users/users-page.component.ts +++ b/src/Squidex/app/features/administration/pages/users/users-page.component.ts @@ -68,13 +68,10 @@ export class UsersPageComponent extends ComponentBase implements OnInit { public lock(id: string) { this.userManagementService.lockUser(id) .subscribe(() => { - this.usersItems = this.usersItems.map(u => { - if (u.id === id) { - return new UserDto(u.id, u.email, u.displayName, u.pictureUrl, true); - } else { - return u; - } - }); + this.usersItems = + this.usersItems.replaceAll( + u => u.id === id, + u => new UserDto(u.id, u.email, u.displayName, u.pictureUrl, true)); }, error => { this.notifyError(error); }); @@ -83,13 +80,10 @@ export class UsersPageComponent extends ComponentBase implements OnInit { public unlock(id: string) { this.userManagementService.unlockUser(id) .subscribe(() => { - this.usersItems = this.usersItems.map(u => { - if (u.id === id) { - return new UserDto(u.id, u.email, u.displayName, u.pictureUrl, false); - } else { - return u; - } - }); + this.usersItems = + this.usersItems.replaceAll( + u => u.id === id, + u => new UserDto(u.id, u.email, u.displayName, u.pictureUrl, false)); }, error => { this.notifyError(error); }); diff --git a/src/Squidex/app/features/assets/pages/assets-page.component.ts b/src/Squidex/app/features/assets/pages/assets-page.component.ts index 0fd5b69e9..5950183c4 100644 --- a/src/Squidex/app/features/assets/pages/assets-page.component.ts +++ b/src/Squidex/app/features/assets/pages/assets-page.component.ts @@ -54,7 +54,10 @@ export class AssetsPageComponent extends AppComponentBase implements OnDestroy, this.messageBus.of(AssetUpdated) .subscribe(event => { if (event.sender !== this) { - this.assetsItems = this.assetsItems.map(x => x.id === event.assetDto.id ? event.assetDto : x); + this.assetsItems = + this.assetsItems.replaceAll( + a => a.id === event.assetDto.id, + a => event.assetDto); } }); diff --git a/src/Squidex/app/features/content/pages/contents/content-item.component.ts b/src/Squidex/app/features/content/pages/contents/content-item.component.ts index 0abbda076..661bd3420 100644 --- a/src/Squidex/app/features/content/pages/contents/content-item.component.ts +++ b/src/Squidex/app/features/content/pages/contents/content-item.component.ts @@ -74,7 +74,7 @@ export class ContentItemComponent extends AppComponentBase implements OnInit, On } } - public getValue(field: FieldDto): any { + private getValue(field: FieldDto): any { const contentField = this.content.data[field.name]; if (!contentField) { diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.html b/src/Squidex/app/features/schemas/pages/schema/field.component.html index 7ee970201..892009258 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.html @@ -1,5 +1,5 @@ -
-
+
+
@@ -17,7 +17,7 @@
- @@ -48,9 +48,9 @@
-
+
-
+
-
+
@@ -131,7 +131,7 @@
-
+
@@ -157,7 +157,7 @@
-
+
diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.scss b/src/Squidex/app/features/schemas/pages/schema/field.component.scss index 6b9f038a4..7a7fd0383 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.scss +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.scss @@ -3,17 +3,7 @@ $field-header: #e7ebef; -.table-items-row { - padding: 0; -} - .field { - &-summary { - padding: 1rem 1.25rem; - position: relative; - line-height: 2.5rem; - } - &-icon { margin-right: 1rem; color: $color-border-dark; @@ -22,25 +12,6 @@ $field-header: #e7ebef; vertical-align: middle; } - &-edit-button { - & { - color: $color-theme-blue; - line-height: 1rem; - font-size: 1rem; - font-weight: normal; - } - - &:hover { - color: $color-theme-blue-dark; - } - - &.active { - background: $color-theme-blue; - border-color: $color-theme-blue; - color: $color-dark-foreground; - } - } - &-name { @include truncate; } @@ -49,36 +20,6 @@ $field-header: #e7ebef; color: $color-border-dark; } - &-hints { - color: lighten($color-text, 50%); - font-weight: normal; - font-size: .95rem; - margin-left: .3rem; - } - - &-details { - & { - position: relative; - } - - &::before { - @include caret-top; - @include absolute(-.5rem, 5.5rem, auto, auto); - border-color: transparent transparent $color-border; - border-width: .6rem; - } - - &-tab { - padding: 1rem 1.25rem 1.25rem; - } - - &-tabs { - background: $color-border; - position: relative; - padding: 1rem 1.25rem; - } - } - .tag { @include opacity(.9); min-width: 4rem; diff --git a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts index 7b7b5a07e..7efc33e06 100644 --- a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts +++ b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts @@ -78,7 +78,7 @@ export class SchemasPageComponent extends AppComponentBase implements OnDestroy, this.schemaUpdatedSubscription = this.messageBus.of(SchemaUpdated) .subscribe(m => { - this.updateSchemas(this.schemas.map(s => s.name === m.name ? updateSchema(s, this.authService, m) : s)); + this.updateSchemas(this.schemas.replaceAll(s => s.name === m.name, s => updateSchema(s, this.authService, m))); }); this.schemaDeletedSubscription = diff --git a/src/Squidex/app/features/settings/pages/languages/language.component.html b/src/Squidex/app/features/settings/pages/languages/language.component.html index 58f2e25dc..4c2bc1c32 100644 --- a/src/Squidex/app/features/settings/pages/languages/language.component.html +++ b/src/Squidex/app/features/settings/pages/languages/language.component.html @@ -1,5 +1,5 @@ -
-
+
+
{{language.iso2Code}} @@ -9,7 +9,7 @@
- @@ -21,16 +21,16 @@
-
+
-
+
-
+
@@ -67,7 +67,7 @@
-
+
diff --git a/src/Squidex/app/features/settings/pages/languages/language.component.scss b/src/Squidex/app/features/settings/pages/languages/language.component.scss index d4f7b2d16..bcedc32dd 100644 --- a/src/Squidex/app/features/settings/pages/languages/language.component.scss +++ b/src/Squidex/app/features/settings/pages/languages/language.component.scss @@ -3,36 +3,7 @@ $field-header: #e7ebef; -.table-items-row { - padding: 0; -} - .language { - &-summary { - padding: 1rem 1.25rem; - position: relative; - line-height: 2.5rem; - } - - &-edit-button { - & { - color: $color-theme-blue; - line-height: 1rem; - font-size: 1rem; - font-weight: normal; - } - - &:hover { - color: $color-theme-blue-dark; - } - - &.active { - background: $color-theme-blue; - border-color: $color-theme-blue; - color: $color-dark-foreground; - } - } - &-name { @include truncate; } @@ -40,29 +11,6 @@ $field-header: #e7ebef; &-master { font-weight: bold; } - - &-details { - & { - position: relative; - } - - &::before { - @include caret-top; - @include absolute(-.5rem, 5.5rem, auto, auto); - border-color: transparent transparent $color-border; - border-width: .6rem; - } - - &-tab { - padding: 1rem 1.25rem 1.25rem; - } - - &-tabs { - background: $color-border; - position: relative; - padding: 1rem 1.25rem; - } - } } .btn-danger { diff --git a/src/Squidex/app/features/settings/pages/languages/language.component.ts b/src/Squidex/app/features/settings/pages/languages/language.component.ts index 88441683d..f15011a23 100644 --- a/src/Squidex/app/features/settings/pages/languages/language.component.ts +++ b/src/Squidex/app/features/settings/pages/languages/language.component.ts @@ -5,8 +5,9 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, EventEmitter, Input, OnChanges, Output, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Subscription } from 'rxjs'; import { AppLanguageDto, @@ -22,7 +23,9 @@ import { fadeAnimation ] }) -export class LanguageComponent implements OnInit, OnChanges { +export class LanguageComponent implements OnInit, OnChanges, OnDestroy { + private isMasterSubscription: Subscription; + @Input() public language: AppLanguageDto; @@ -40,6 +43,7 @@ export class LanguageComponent implements OnInit, OnChanges { public fallbackLanguages: AppLanguageDto[] = []; public isEditing = false; + public isMaster = false; public editFormSubmitted = false; public editForm: FormGroup = @@ -61,9 +65,20 @@ export class LanguageComponent implements OnInit, OnChanges { } public ngOnInit() { + this.isMasterSubscription = + this.editForm.get('isMaster').valueChanges + .subscribe(v => { + this.isMaster = v; + this.editForm.get('isOptional').setValue(false); + }); + this.resetForm(); } + public ngOnDestroy() { + this.isMasterSubscription.unsubscribe(); + } + public ngOnChanges() { this.resetForm(); } @@ -122,6 +137,8 @@ export class LanguageComponent implements OnInit, OnChanges { } if (this.language) { + this.isMaster = this.language.isMaster; + this.fallbackLanguages = this.allLanguages.filter(l => this.language.fallback.indexOf(l.iso2Code) >= 0).values; diff --git a/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts b/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts index 546cdc58a..bb0ec0615 100644 --- a/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts +++ b/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts @@ -52,13 +52,7 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit { } public ngOnInit() { - this.languagesService.getLanguages().retry(2) - .subscribe(languages => { - this.allLanguages = languages; - }, error => { - this.notifyError(error); - }); - + this.loadAllLanguages(); this.load(); } @@ -72,16 +66,6 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit { }); } - public updateLanguage(language: AppLanguageDto) { - this.appNameOnce() - .switchMap(app => this.appLanguagesService.updateLanguage(app, language.iso2Code, language, this.version)) - .subscribe(dto => { - this.updateLanguages(this.appLanguages.map(l => l.iso2Code === language.iso2Code ? language : l)); - }, error => { - this.notifyError(error); - }); - } - public removeLanguage(language: AppLanguageDto) { this.appNameOnce() .switchMap(app => this.appLanguagesService.deleteLanguage(app, language.iso2Code, this.version)) @@ -104,13 +88,58 @@ export class LanguagesPageComponent extends AppComponentBase implements OnInit { }); } - private updateLanguages(languages: ImmutableArray) { + public updateLanguage(language: AppLanguageDto) { + this.appNameOnce() + .switchMap(app => this.appLanguagesService.updateLanguage(app, language.iso2Code, language, this.version)) + .subscribe(dto => { + this.updateLanguages( + this.appLanguages.replaceAll( + l => l.iso2Code === language.iso2Code, + l => language), + language.isMaster ? language.iso2Code : undefined); + }, error => { + this.notifyError(error); + }); + } + + private loadAllLanguages() { + this.languagesService.getLanguages().retry(2) + .subscribe(languages => { + this.allLanguages = languages; + + this.updateNewLanguages(); + }, error => { + this.notifyError(error); + }); + } + + private updateLanguages(languages: ImmutableArray, masterId?: string) { this.addLanguageForm.reset(); - this.appLanguages = languages; - this.newLanguages = this.allLanguages.filter(x => !this.appLanguages.find(l => l.iso2Code === x.iso2Code)); + this.appLanguages = + languages.map(l => { + return new AppLanguageDto( + l.iso2Code, + l.englishName, + masterId ? l.iso2Code === masterId : l.isMaster, + l.isOptional, + l.fallback.filter(f => !!languages.find(l2 => l2.iso2Code === f)) + ); + }).sort((a, b) => { + if (a.isMaster === b.isMaster) { + return a.iso2Code.localeCompare(b.iso2Code); + } else { + return (a.isMaster ? 0 : 1) - (b.isMaster ? 0 : 1); + } + }); + + this.updateNewLanguages(); this.messageBus.publish(new HistoryChannelUpdated()); } + + private updateNewLanguages() { + this.newLanguages = this.allLanguages.filter(x => !this.appLanguages.find(l => l.iso2Code === x.iso2Code)); + } } diff --git a/src/Squidex/app/shared/components/assets-editor.component.ts b/src/Squidex/app/shared/components/assets-editor.component.ts index 6dae05aba..b79c95436 100644 --- a/src/Squidex/app/shared/components/assets-editor.component.ts +++ b/src/Squidex/app/shared/components/assets-editor.component.ts @@ -58,7 +58,10 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa this.messageBus.of(AssetUpdated) .subscribe(event => { if (event.sender !== this) { - this.oldAssets = this.oldAssets.map(x => x.id === event.assetDto.id ? event.assetDto : x); + this.oldAssets = + this.oldAssets.replaceAll( + a => a.id === event.assetDto.id, + a => event.assetDto); } }); } diff --git a/src/Squidex/app/theme/_lists.scss b/src/Squidex/app/theme/_lists.scss index 75e66c018..892c6937b 100644 --- a/src/Squidex/app/theme/_lists.scss +++ b/src/Squidex/app/theme/_lists.scss @@ -67,22 +67,74 @@ } } - &-row, + &-row { + & { + padding: 1rem 1.25rem; + background: $color-table; + border: 1px solid $color-border; + border-bottom-width: 2px; + margin-bottom: .25rem; + } + + &-summary { + padding: 1rem 1.25rem; + position: relative; + line-height: 2.5rem; + } + + &-expandable { + padding: 0; + } + + &-details { + & { + position: relative; + } + + &::before { + @include caret-top; + @include absolute(-.5rem, 5.5rem, auto, auto); + border-color: transparent transparent $color-border; + border-width: .6rem; + } + + &-tab { + padding: 1rem 1.25rem 1.25rem; + } + + &-tabs { + background: $color-border; + position: relative; + padding: 1rem 1.25rem; + } + } + } + &-footer { padding: 1rem 1.25rem; background: $color-table-footer; border: 1px solid $color-border; border-bottom-width: 2px; + margin-top: .8rem; } - &-row { - background: $color-table; - margin-bottom: .25rem; - } + &-edit-button { + & { + color: $color-theme-blue; + line-height: 1rem; + font-size: 1rem; + font-weight: normal; + } - &-footer { - background: $color-table-footer; - margin-top: .8rem; + &:hover { + color: $color-theme-blue-dark; + } + + &.active { + background: $color-theme-blue; + border-color: $color-theme-blue; + color: $color-dark-foreground; + } } .spacer {