diff --git a/src/Squidex.Infrastructure/CQRS/EventStore/EventStoreBus.cs b/src/Squidex.Infrastructure/CQRS/EventStore/EventStoreBus.cs index e13e78367..d2c7a6af9 100644 --- a/src/Squidex.Infrastructure/CQRS/EventStore/EventStoreBus.cs +++ b/src/Squidex.Infrastructure/CQRS/EventStore/EventStoreBus.cs @@ -30,6 +30,7 @@ namespace Squidex.Infrastructure.CQRS.EventStore private readonly IStreamPositionStorage positions; private readonly List catchSubscriptions = new List(); private EventStoreSubscription liveSubscription; + private string streamName; private bool isSubscribed; public EventStoreBus( @@ -71,22 +72,24 @@ namespace Squidex.Infrastructure.CQRS.EventStore } } - public void Subscribe(string streamName = "$all") + public void Subscribe(string streamToConnect = "$all") { - Guard.NotNullOrEmpty(streamName, nameof(streamName)); + Guard.NotNullOrEmpty(streamToConnect, nameof(streamToConnect)); if (isSubscribed) { return; } - SubscribeLive(streamName); - SubscribeCatch(streamName); + this.streamName = streamToConnect; + + SubscribeLive(); + SubscribeCatch(); isSubscribed = true; } - private void SubscribeLive(string streamName) + private void SubscribeLive() { Task.Run(async () => { @@ -94,20 +97,37 @@ namespace Squidex.Infrastructure.CQRS.EventStore await connection.SubscribeToStreamAsync(streamName, true, (subscription, resolvedEvent) => { - OnLiveEvent(streamName, resolvedEvent); - }, userCredentials: credentials); + OnLiveEvent(resolvedEvent); + }, (subscription, dropped, ex) => + { + OnConnectionDropped(); + }, credentials); }).Wait(); } - private void SubscribeCatch(string streamName) + private void OnConnectionDropped() + { + try + { + liveSubscription.Close(); + + logger.LogError("Subscription closed"); + } + finally + { + SubscribeLive(); + } + } + + private void SubscribeCatch() { foreach (var catchConsumer in catchConsumers) { - SubscribeCatchFor(catchConsumer, streamName); + SubscribeCatchFor(catchConsumer); } } - private void SubscribeCatchFor(IEventConsumer consumer, string streamName) + private void SubscribeCatchFor(IEventConsumer consumer) { var subscriptionName = consumer.GetType().GetTypeInfo().Name; @@ -134,7 +154,7 @@ namespace Squidex.Infrastructure.CQRS.EventStore } } - private void OnLiveEvent(string streamName, ResolvedEvent resolvedEvent) + private void OnLiveEvent(ResolvedEvent resolvedEvent) { Envelope @event = null; diff --git a/src/Squidex.Write/Apps/AppContributors.cs b/src/Squidex.Write/Apps/AppContributors.cs index d8eae44a3..0c6922abb 100644 --- a/src/Squidex.Write/Apps/AppContributors.cs +++ b/src/Squidex.Write/Apps/AppContributors.cs @@ -24,7 +24,7 @@ namespace Squidex.Write.Apps { Func message = () => "Cannot assign contributor"; - ThrowIfFound(contributorId, message); + ThrowIfFound(contributorId, permission, message); ThrowIfNoOwner(c => c[contributorId] = permission, message); contributors[contributorId] = permission; @@ -48,11 +48,11 @@ namespace Squidex.Write.Apps } } - private void ThrowIfFound(string contributorId, Func message) + private void ThrowIfFound(string contributorId, PermissionLevel permission, Func message) { PermissionLevel currentPermission; - if (contributors.TryGetValue(contributorId, out currentPermission)) + if (contributors.TryGetValue(contributorId, out currentPermission) && currentPermission == permission) { var error = new ValidationError("Contributor is already part of the app with same permissions", "ContributorId"); diff --git a/src/Squidex/Controllers/Api/Apps/AppClientsController.cs b/src/Squidex/Controllers/Api/Apps/AppClientsController.cs index 88d941c4d..dd9ca45ee 100644 --- a/src/Squidex/Controllers/Api/Apps/AppClientsController.cs +++ b/src/Squidex/Controllers/Api/Apps/AppClientsController.cs @@ -82,7 +82,7 @@ namespace Squidex.Controllers.Api.Apps [HttpPost] [Route("apps/{app}/clients/")] [ProducesResponseType(typeof(ClientDto[]), 201)] - public async Task PostClient(string app, [FromBody] AttachClientDto request) + public async Task PostClient(string app, [FromBody] CreateAppClientDto request) { var context = await CommandBus.PublishAsync(SimpleMapper.Map(request, new AttachClient())); var result = context.Result(); @@ -105,7 +105,7 @@ namespace Squidex.Controllers.Api.Apps [HttpPut] [Route("apps/{app}/clients/{clientId}/")] [ProducesResponseType(typeof(ClientDto[]), 201)] - public async Task PutClient(string app, string clientId, [FromBody] RenameClientDto request) + public async Task PutClient(string app, string clientId, [FromBody] UpdateAppClientDto request) { await CommandBus.PublishAsync(SimpleMapper.Map(request, new RenameClient { Id = clientId })); diff --git a/src/Squidex/Controllers/Api/Apps/AppContributorsController.cs b/src/Squidex/Controllers/Api/Apps/AppContributorsController.cs index aacfc6750..29735c411 100644 --- a/src/Squidex/Controllers/Api/Apps/AppContributorsController.cs +++ b/src/Squidex/Controllers/Api/Apps/AppContributorsController.cs @@ -75,7 +75,7 @@ namespace Squidex.Controllers.Api.Apps [HttpPost] [Route("apps/{app}/contributors/")] [ProducesResponseType(typeof(ErrorDto), 400)] - public async Task PostContributor(string app, [FromBody] AssignContributorDto request) + public async Task PostContributor(string app, [FromBody] AssignAppContributorDto request) { await CommandBus.PublishAsync(SimpleMapper.Map(request, new AssignContributor())); diff --git a/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs b/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs index 958dfb79d..92ff518a0 100644 --- a/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs +++ b/src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs @@ -82,7 +82,7 @@ namespace Squidex.Controllers.Api.Apps [Route("apps/{app}/languages/")] [ProducesResponseType(typeof(AppLanguageDto), 201)] [ProducesResponseType(typeof(ErrorDto), 400)] - public async Task PostLanguage(string app, [FromBody] AddLanguageDto request) + public async Task PostLanguage(string app, [FromBody] AddAppLanguageDto request) { await CommandBus.PublishAsync(SimpleMapper.Map(request, new AddLanguage())); @@ -104,7 +104,7 @@ namespace Squidex.Controllers.Api.Apps /// [HttpPut] [Route("apps/{app}/languages/{language}")] - public async Task Update(string app, string language, [FromBody] SetMasterLanguageDto model) + public async Task Update(string app, string language, [FromBody] UpdateAppLanguageDto model) { if (model.IsMasterLanguage) { diff --git a/src/Squidex/Controllers/Api/Apps/Models/AddLanguageDto.cs b/src/Squidex/Controllers/Api/Apps/Models/AddAppLanguageDto.cs similarity index 90% rename from src/Squidex/Controllers/Api/Apps/Models/AddLanguageDto.cs rename to src/Squidex/Controllers/Api/Apps/Models/AddAppLanguageDto.cs index 3cde113d6..60f59a825 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/AddLanguageDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/AddAppLanguageDto.cs @@ -1,5 +1,5 @@ // ========================================================================== -// AddLanguageDto.cs +// AddAppLanguageDto.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -11,7 +11,7 @@ using Squidex.Infrastructure; namespace Squidex.Controllers.Api.Apps.Models { - public class AddLanguageDto + public class AddAppLanguageDto { /// /// The language to add. diff --git a/src/Squidex/Controllers/Api/Apps/Models/AssignContributorDto.cs b/src/Squidex/Controllers/Api/Apps/Models/AssignAppContributorDto.cs similarity index 91% rename from src/Squidex/Controllers/Api/Apps/Models/AssignContributorDto.cs rename to src/Squidex/Controllers/Api/Apps/Models/AssignAppContributorDto.cs index de1e9b98d..26aba56d6 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/AssignContributorDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/AssignAppContributorDto.cs @@ -1,19 +1,19 @@ // ========================================================================== -// AssignContributorDto.cs +// AssignAppContributorDto.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== +using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Squidex.Core.Apps; -using System.ComponentModel.DataAnnotations; namespace Squidex.Controllers.Api.Apps.Models { - public sealed class AssignContributorDto + public sealed class AssignAppContributorDto { /// /// The id of the user to add to the app (GUID). diff --git a/src/Squidex/Controllers/Api/Apps/Models/AttachClientDto.cs b/src/Squidex/Controllers/Api/Apps/Models/CreateAppClientDto.cs similarity index 90% rename from src/Squidex/Controllers/Api/Apps/Models/AttachClientDto.cs rename to src/Squidex/Controllers/Api/Apps/Models/CreateAppClientDto.cs index 9c2fde7ff..91ccf179f 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/AttachClientDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/CreateAppClientDto.cs @@ -1,5 +1,5 @@ // ========================================================================== -// AttachClientDto.cs +// CreateAppClientDto.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations; namespace Squidex.Controllers.Api.Apps.Models { - public sealed class AttachClientDto + public sealed class CreateAppClientDto { /// /// The id of the client. diff --git a/src/Squidex/Controllers/Api/Apps/Models/RenameClientDto.cs b/src/Squidex/Controllers/Api/Apps/Models/UpdateAppClientDto.cs similarity index 90% rename from src/Squidex/Controllers/Api/Apps/Models/RenameClientDto.cs rename to src/Squidex/Controllers/Api/Apps/Models/UpdateAppClientDto.cs index 64336350e..69b546ca0 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/RenameClientDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/UpdateAppClientDto.cs @@ -1,5 +1,5 @@ // ========================================================================== -// RenameClientDto.cs +// UpdateAppClientDto.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations; namespace Squidex.Controllers.Api.Apps.Models { - public class RenameClientDto + public class UpdateAppClientDto { /// /// The new display name of the client. diff --git a/src/Squidex/Controllers/Api/Apps/Models/SetMasterLanguageDto.cs b/src/Squidex/Controllers/Api/Apps/Models/UpdateAppLanguageDto.cs similarity index 89% rename from src/Squidex/Controllers/Api/Apps/Models/SetMasterLanguageDto.cs rename to src/Squidex/Controllers/Api/Apps/Models/UpdateAppLanguageDto.cs index 6379b6ba1..ef54c58c8 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/SetMasterLanguageDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/UpdateAppLanguageDto.cs @@ -1,14 +1,13 @@ // ========================================================================== -// SetMasterLanguageDto.cs +// UpdateAppLanguageDto.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== - namespace Squidex.Controllers.Api.Apps.Models { - public class SetMasterLanguageDto + public class UpdateAppLanguageDto { /// /// Set the value to true to make the language to the master language. diff --git a/src/Squidex/app/app.module.ts b/src/Squidex/app/app.module.ts index 9cfd7ccc7..3c4a12d5c 100644 --- a/src/Squidex/app/app.module.ts +++ b/src/Squidex/app/app.module.ts @@ -16,18 +16,17 @@ import { AppContributorsService, AppLanguagesService, AppMustExistGuard, - AppsStoreService, AppsService, + AppsStoreService, AuthService, CurrencyConfig, - DragService, - DragServiceFactory, DecimalSeparatorConfig, + DragService, + LanguageService, + LocalStoreService, MustBeAuthenticatedGuard, MustBeNotAuthenticatedGuard, NotificationService, - LanguageService, - LocalStoreService, SqxFrameworkModule, TitlesConfig, TitleService, diff --git a/src/Squidex/app/app.routes.ts b/src/Squidex/app/app.routes.ts index 4b4afec3c..f2e0c4c5c 100644 --- a/src/Squidex/app/app.routes.ts +++ b/src/Squidex/app/app.routes.ts @@ -9,13 +9,13 @@ import * as Ng2 from '@angular/core'; import * as Ng2Router from '@angular/router'; import { - AppsPageComponent, AppAreaComponent, + AppsPageComponent, ClientsPageComponent, ContributorsPageComponent, DashboardPageComponent, - InternalAreaComponent, HomePageComponent, + InternalAreaComponent, LanguagesPageComponent, LogoutPageComponent, NotFoundPageComponent, diff --git a/src/Squidex/app/components/internal/app/settings/client.component.html b/src/Squidex/app/components/internal/app/settings/client.component.html index 10d0a2ace..c11502cd7 100644 --- a/src/Squidex/app/components/internal/app/settings/client.component.html +++ b/src/Squidex/app/components/internal/app/settings/client.component.html @@ -13,9 +13,9 @@
-
+
- +
@@ -39,10 +39,10 @@ Client Id: - + - @@ -50,10 +50,10 @@ Client Secret: - + - diff --git a/src/Squidex/app/components/internal/app/settings/client.component.scss b/src/Squidex/app/components/internal/app/settings/client.component.scss index 952e45566..ff2f1c01c 100644 --- a/src/Squidex/app/components/internal/app/settings/client.component.scss +++ b/src/Squidex/app/components/internal/app/settings/client.component.scss @@ -19,8 +19,9 @@ display: none; font-size: .9rem; font-weight: normal; - padding: .3rem; + padding: .6rem .3rem; background: transparent; + vertical-align: top; } &-name { diff --git a/src/Squidex/app/components/internal/app/settings/client.component.ts b/src/Squidex/app/components/internal/app/settings/client.component.ts index 93162f872..841d4e01c 100644 --- a/src/Squidex/app/components/internal/app/settings/client.component.ts +++ b/src/Squidex/app/components/internal/app/settings/client.component.ts @@ -10,15 +10,9 @@ import * as Ng2Forms from '@angular/forms'; import { AccessTokenDto, - AppsStoreService, AppClientDto, - AppClientCreateDto, - AppClientsService, fadeAnimation, - ModalView, - Notification, - NotificationService, - TitleService + ModalView } from 'shared'; @Ng2.Component({ @@ -29,99 +23,59 @@ import { fadeAnimation ] }) -export class ClientComponent { - private oldName: string; - +export class ClientComponent implements Ng2.OnChanges { public isRenaming = false; public appClientToken: AccessTokenDto; - @Ng2.Input('appName') - public appName: string; + @Ng2.Output() + public renamed = new Ng2.EventEmitter(); - @Ng2.Input('client') + @Ng2.Input() public client: AppClientDto; - @Ng2.ViewChild('inputId') - public inputId: Ng2.ElementRef; - - @Ng2.ViewChild('inputSecret') - public inputSecret: Ng2.ElementRef; - public modalDialog = new ModalView(); - constructor( - private readonly appClientsService: AppClientsService, - private readonly notifications: NotificationService - ) { + public get clientName() { + return this.client.name || this.client.id; } - public rename() { - this.appClientsService.renameClient(this.appName, this.client.id, this.client.name) - .subscribe(() => { - this.stopRename(); - }, error => { - this.notifications.notify(Notification.error(error.displayMessage)); - this.cancelRename(); - }); + public get clientId() { + return this.client.id + ':' + this.client.secret; } - public cancelRename() { - this.client.name = this.oldName; - - this.isRenaming = false; - } - - public stopRename() { - this.client.name = this.client.name || this.client.id; - - this.isRenaming = false; + public get clientSecret() { + return this.client.secret; } - public startRename() { - this.oldName = this.client.name; + public renameForm = + this.formBuilder.group({ + name: [''] + }); - this.isRenaming = true; + constructor( + private readonly formBuilder: Ng2Forms.FormBuilder + ) { } - public createToken(client: AppClientDto) { - this.appClientsService.createToken(this.appName, client) - .subscribe(token => { - this.appClientToken = token; - this.modalDialog.show(); - }, error => { - this.notifications.notify(Notification.error('Failed to retrieve access token. Please retry.')); - }); + public ngOnChanges() { + this.renameForm.controls['name'].setValue(this.clientName); } - public copyId() { - this.copyToClipbord(this.inputId.nativeElement); + public cancelRename() { + this.isRenaming = false; } - public copySecret() { - this.copyToClipbord(this.inputSecret.nativeElement); + public startRename() { + this.isRenaming = true; } - private copyToClipbord(element: HTMLInputElement | HTMLTextAreaElement) { - const currentFocus: any = document.activeElement; - - const prevSelectionStart = element.selectionStart; - const prevSelectionEnd = element.selectionEnd; - - element.focus(); - element.setSelectionRange(0, element.value.length); - + public rename() { try { - document.execCommand('copy'); - } catch (e) { - console.log('Copy failed'); - } - - if (currentFocus && typeof currentFocus.focus === 'function') { - currentFocus.focus(); + this.renamed.emit(this.renameForm.controls['name'].value); + } finally { + this.isRenaming = false; } - - element.setSelectionRange(prevSelectionStart, prevSelectionEnd); } } diff --git a/src/Squidex/app/components/internal/app/settings/clients-page.component.html b/src/Squidex/app/components/internal/app/settings/clients-page.component.html index 381ae0573..10f46f2d2 100644 --- a/src/Squidex/app/components/internal/app/settings/clients-page.component.html +++ b/src/Squidex/app/components/internal/app/settings/clients-page.component.html @@ -1,3 +1,5 @@ + +
@@ -14,7 +16,7 @@
- +