mirror of https://github.com/Squidex/squidex.git
40 changed files with 965 additions and 259 deletions
@ -0,0 +1,67 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps |
||||
|
{ |
||||
|
public sealed class AppUISettings : IAppUISettings |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
|
||||
|
public AppUISettings(IGrainFactory grainFactory) |
||||
|
{ |
||||
|
Guard.NotNull(grainFactory, nameof(grainFactory)); |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public async Task<JsonObject> GetAsync(Guid appId, string userId) |
||||
|
{ |
||||
|
var result = await GetGrain(appId, userId).GetAsync(); |
||||
|
|
||||
|
return result.Value; |
||||
|
} |
||||
|
|
||||
|
public Task RemoveAsync(Guid appId, string userId, string path) |
||||
|
{ |
||||
|
return GetGrain(appId, userId).RemoveAsync(path); |
||||
|
} |
||||
|
|
||||
|
public Task SetAsync(Guid appId, string userId, string path, IJsonValue value) |
||||
|
{ |
||||
|
return GetGrain(appId, userId).SetAsync(path, value.AsJ()); |
||||
|
} |
||||
|
|
||||
|
public Task SetAsync(Guid appId, string userId, JsonObject settings) |
||||
|
{ |
||||
|
return GetGrain(appId, userId).SetAsync(settings.AsJ()); |
||||
|
} |
||||
|
|
||||
|
private IAppUISettingsGrain GetGrain(Guid appId, string userId) |
||||
|
{ |
||||
|
return grainFactory.GetGrain<IAppUISettingsGrain>(Key(appId, userId)); |
||||
|
} |
||||
|
|
||||
|
private string Key(Guid appId, string userId) |
||||
|
{ |
||||
|
if (!string.IsNullOrWhiteSpace(userId)) |
||||
|
{ |
||||
|
return $"{appId}_{userId}"; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return $"{appId}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps |
||||
|
{ |
||||
|
public interface IAppUISettings |
||||
|
{ |
||||
|
Task<JsonObject> GetAsync(Guid appId, string userId); |
||||
|
|
||||
|
Task SetAsync(Guid appId, string userId, string path, IJsonValue value); |
||||
|
|
||||
|
Task SetAsync(Guid appId, string userId, JsonObject settings); |
||||
|
|
||||
|
Task RemoveAsync(Guid appId, string userId, string path); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core'; |
||||
|
|
||||
|
import { Queries } from '@app/shared/internal'; |
||||
|
import { SavedQuery } from '../state/queries'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-shared-queries', |
||||
|
template: ` |
||||
|
<div class="sidebar-section"> |
||||
|
<h3>Shared queries</h3> |
||||
|
|
||||
|
<ng-container *ngIf="queries.queriesShared | async; let queryList"> |
||||
|
<ng-container *ngIf="queryList.length > 0; else noQuery"> |
||||
|
<a class="sidebar-item" *ngFor="let saved of queryList; trackBy: trackByQuery" (click)="search.emit(saved)" |
||||
|
[class.active]="isSelectedQuery(saved)"> |
||||
|
|
||||
|
{{saved.name}} |
||||
|
|
||||
|
<a class="sidebar-item-remove float-right" (click)="queries.removeShared(saved)" sqxStopClick> |
||||
|
<i class="icon-close"></i> |
||||
|
</a> |
||||
|
</a> |
||||
|
</ng-container> |
||||
|
|
||||
|
<ng-template #noQuery> |
||||
|
<div class="sidebar-item text-muted"> |
||||
|
Search for {{types}} and use <i class="icon-star-empty"></i> icon in search form to save query for all contributors. |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</ng-container> |
||||
|
</div> |
||||
|
|
||||
|
<hr /> |
||||
|
|
||||
|
<div class="sidebar-section"> |
||||
|
<h3>My queries</h3> |
||||
|
|
||||
|
<ng-container *ngIf="queries.queriesUser | async; let queryList"> |
||||
|
<ng-container *ngIf="queryList.length > 0; else noQuery"> |
||||
|
<a class="sidebar-item" *ngFor="let saved of queryList; trackBy: trackByQuery" (click)="search.emit(saved)" |
||||
|
[class.active]="isSelectedQuery(saved)"> |
||||
|
|
||||
|
{{saved.name}} |
||||
|
|
||||
|
<a class="sidebar-item-remove float-right" (click)="queries.removeUser(saved)" sqxStopClick> |
||||
|
<i class="icon-close"></i> |
||||
|
</a> |
||||
|
</a> |
||||
|
</ng-container> |
||||
|
|
||||
|
<ng-template #noQuery> |
||||
|
<div class="sidebar-item text-muted"> |
||||
|
Search for {{types}} and use <i class="icon-star-empty"></i> icon in search form to save query for yourself. |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
</ng-container> |
||||
|
</div> |
||||
|
` |
||||
|
}) |
||||
|
export class SavedQueriesComponent { |
||||
|
@Input() |
||||
|
public queryUsed: (saved: SavedQuery) => boolean; |
||||
|
|
||||
|
@Input() |
||||
|
public queries: Queries; |
||||
|
|
||||
|
@Input() |
||||
|
public types: string; |
||||
|
|
||||
|
@Output() |
||||
|
public search = new EventEmitter<SavedQuery>(); |
||||
|
|
||||
|
public isSelectedQuery(saved: SavedQuery) { |
||||
|
return this.queryUsed && this.queryUsed(saved); |
||||
|
} |
||||
|
|
||||
|
public trackByQuery(index: number, query: { name: string }) { |
||||
|
return query.name; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Orleans; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Apps |
||||
|
{ |
||||
|
public class AppUISettingsTests |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>(); |
||||
|
private readonly IAppUISettingsGrain grain = A.Fake<IAppUISettingsGrain>(); |
||||
|
private readonly AppUISettings sut; |
||||
|
|
||||
|
public AppUISettingsTests() |
||||
|
{ |
||||
|
A.CallTo(() => grainFactory.GetGrain<IAppUISettingsGrain>(A<string>.Ignored, null)) |
||||
|
.Returns(grain); |
||||
|
|
||||
|
sut = new AppUISettings(grainFactory); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_call_grain_when_retrieving_settings() |
||||
|
{ |
||||
|
var settings = JsonValue.Object(); |
||||
|
|
||||
|
A.CallTo(() => grain.GetAsync()) |
||||
|
.Returns(settings.AsJ()); |
||||
|
|
||||
|
var result = await sut.GetAsync(Guid.NewGuid(), "user"); |
||||
|
|
||||
|
Assert.Same(settings, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_call_grain_when_setting_value() |
||||
|
{ |
||||
|
var value = JsonValue.Object(); |
||||
|
|
||||
|
await sut.SetAsync(Guid.NewGuid(), "user", "the.path", value); |
||||
|
|
||||
|
A.CallTo(() => grain.SetAsync("the.path", A<J<IJsonValue>>.That.Matches(x => x.Value == value))) |
||||
|
.MustHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_call_grain_when_replacing_settings() |
||||
|
{ |
||||
|
var value = JsonValue.Object(); |
||||
|
|
||||
|
await sut.SetAsync(Guid.NewGuid(), "user", value); |
||||
|
|
||||
|
A.CallTo(() => grain.SetAsync(A<J<JsonObject>>.That.Matches(x => x.Value == value))) |
||||
|
.MustHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_call_grain_when_removing_value() |
||||
|
{ |
||||
|
await sut.RemoveAsync(Guid.NewGuid(), "user", "the.path"); |
||||
|
|
||||
|
A.CallTo(() => grain.RemoveAsync("the.path")) |
||||
|
.MustHaveHappened(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue