Browse Source

Admin feature.

pull/311/head
Sebastian 7 years ago
parent
commit
4a712a8a04
  1. 6
      src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs
  2. 7
      src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs
  3. 213
      src/Squidex.Domain.Apps.Entities/Backup/AppCleanerGrain.cs
  4. 5
      src/Squidex.Domain.Apps.Entities/Backup/BackupHandler.cs
  5. 16
      src/Squidex.Domain.Apps.Entities/Backup/CleanerStatus.cs
  6. 44
      src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs
  7. 20
      src/Squidex.Domain.Apps.Entities/Backup/IAppCleanerGrain.cs
  8. 24
      src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs
  9. 5
      src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs
  10. 34
      src/Squidex.Domain.Apps.Entities/History/BackupHistory.cs
  11. 14
      src/Squidex.Domain.Apps.Entities/Rules/BackupRules.cs
  12. 14
      src/Squidex.Domain.Apps.Entities/Schemas/BackupSchemas.cs
  13. 18
      src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs
  14. 6
      src/Squidex/Config/Domain/EntitiesServices.cs
  15. 2
      src/Squidex/Config/Orleans/SiloWrapper.cs
  16. 5
      src/Squidex/app/features/administration/administration-area.component.html
  17. 1
      src/Squidex/app/features/administration/declarations.ts
  18. 6
      src/Squidex/app/features/administration/module.ts
  19. 65
      src/Squidex/app/features/administration/pages/restore/restore-page.component.html
  20. 1
      src/Squidex/app/features/administration/pages/restore/restore-page.component.scss
  21. 0
      src/Squidex/app/features/administration/pages/restore/restore-page.component.ts
  22. 3
      src/Squidex/app/features/apps/declarations.ts
  23. 9
      src/Squidex/app/features/apps/module.ts
  24. 4
      src/Squidex/app/features/apps/pages/apps-page.component.html
  25. 4
      src/Squidex/app/features/apps/pages/apps-page.component.scss
  26. 59
      src/Squidex/app/features/apps/pages/restore-page.component.html
  27. 18
      src/Squidex/app/theme/icomoon/demo.html
  28. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  29. 1
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  30. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  31. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  32. 219
      src/Squidex/app/theme/icomoon/selection.json
  33. 13
      src/Squidex/app/theme/icomoon/style.css
  34. 48
      tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs

6
src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs

@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Entities.Apps.Indexes;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
@ -37,11 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
this.grainFactory = grainFactory;
}
public override Task RemoveAsync(Guid appId)
{
return RemoveSnapshotAsync<AppState>(appId);
}
public async override Task RestoreEventAsync(Envelope<IEvent> @event, Guid appId, BackupReader reader)
{
switch (@event.Payload)

7
src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs

@ -52,13 +52,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
this.tagService = tagService;
}
public override async Task RemoveAsync(Guid appId)
{
await tagService.ClearAsync(appId, TagGroups.Assets);
await assetRepository.RemoveAsync(appId);
}
public override Task BackupEventAsync(EventData @event, Guid appId, BackupWriter writer)
{
if (@event.Type == "AssetCreatedEvent" ||

213
src/Squidex.Domain.Apps.Entities/Backup/AppCleanerGrain.cs

@ -1,213 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Orleans;
using Orleans.Concurrency;
using Orleans.Runtime;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.Backup
{
[Reentrant]
public sealed class AppCleanerGrain : GrainOfString, IRemindable, IAppCleanerGrain
{
private readonly IGrainFactory grainFactory;
private readonly IStore<Guid> store;
private readonly IEventStore eventStore;
private readonly IEnumerable<BackupHandler> handlers;
private readonly ISemanticLog log;
private IPersistence<State> persistence;
private bool isCleaning;
private State state = new State();
[CollectionName("AppCleaner")]
public sealed class State
{
public HashSet<Guid> Apps { get; set; } = new HashSet<Guid>();
public HashSet<Guid> FailedApps { get; set; } = new HashSet<Guid>();
}
public AppCleanerGrain(IGrainFactory grainFactory, IEventStore eventStore, IStore<Guid> store, IEnumerable<BackupHandler> handlers, ISemanticLog log)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
Guard.NotNull(store, nameof(store));
Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(log, nameof(log));
this.grainFactory = grainFactory;
this.store = store;
this.handlers = handlers;
this.log = log;
this.eventStore = eventStore;
}
public async override Task OnActivateAsync(string key)
{
await RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(1));
persistence = store.WithSnapshots<AppCleanerGrain, State, Guid>(Guid.Empty, s =>
{
state = s;
});
await ReadAsync();
Clean();
}
public Task EnqueueAppAsync(Guid appId)
{
if (appId != Guid.Empty)
{
state.Apps.Add(appId);
Clean();
}
return WriteAsync();
}
public Task ActivateAsync()
{
Clean();
return TaskHelper.Done;
}
public Task ReceiveReminder(string reminderName, TickStatus status)
{
Clean();
return TaskHelper.Done;
}
public Task<CleanerStatus> GetStatusAsync(Guid appId)
{
if (state.Apps.Contains(appId))
{
return Task.FromResult(CleanerStatus.Cleaning);
}
if (state.FailedApps.Contains(appId))
{
return Task.FromResult(CleanerStatus.Failed);
}
return Task.FromResult(CleanerStatus.Cleaned);
}
private void Clean()
{
CleanAsync().Forget();
}
private async Task CleanAsync()
{
if (isCleaning)
{
return;
}
isCleaning = true;
try
{
foreach (var appId in state.Apps.ToList())
{
await CleanupAppAsync(appId);
}
}
finally
{
isCleaning = false;
}
}
private async Task CleanupAppAsync(Guid appId)
{
using (Profiler.StartSession())
{
try
{
log.LogInformation(w => w
.WriteProperty("action", "cleanApp")
.WriteProperty("status", "started")
.WriteProperty("appId", appId.ToString()));
await CleanupAppCoreAsync(appId);
log.LogInformation(w =>
{
w.WriteProperty("action", "cleanApp");
w.WriteProperty("status", "completed");
w.WriteProperty("appId", appId.ToString());
Profiler.Session?.Write(w);
});
}
catch (Exception ex)
{
state.FailedApps.Add(appId);
log.LogError(ex, w =>
{
w.WriteProperty("action", "cleanApp");
w.WriteProperty("status", "failed");
w.WriteProperty("appId", appId.ToString());
Profiler.Session?.Write(w);
});
}
finally
{
state.Apps.Remove(appId);
await WriteAsync();
}
}
}
private async Task CleanupAppCoreAsync(Guid appId)
{
using (Profiler.Trace("DeleteEvents"))
{
await eventStore.DeleteManyAsync("AppId", appId.ToString());
}
foreach (var handler in handlers)
{
using (Profiler.TraceMethod(handler.GetType(), nameof(BackupHandler.RemoveAsync)))
{
await handler.RemoveAsync(appId);
}
}
}
private async Task ReadAsync()
{
await persistence.ReadAsync();
}
private async Task WriteAsync()
{
await persistence.WriteSnapshotAsync(state);
}
}
}

5
src/Squidex.Domain.Apps.Entities/Backup/BackupHandler.cs

@ -50,10 +50,5 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
return TaskHelper.Done;
}
public virtual Task RemoveAsync(Guid appId)
{
return TaskHelper.Done;
}
}
}

16
src/Squidex.Domain.Apps.Entities/Backup/CleanerStatus.cs

@ -1,16 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Entities.Backup
{
public enum CleanerStatus
{
Cleaned,
Cleaning,
Failed
}
}

44
src/Squidex.Domain.Apps.Entities/Backup/EnqueueAppToCleanerMiddleware.cs

@ -1,44 +0,0 @@
// ==========================================================================
// 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.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Backup
{
public sealed class EnqueueAppToCleanerMiddleware : ICommandMiddleware
{
private readonly IAppCleanerGrain cleaner;
public EnqueueAppToCleanerMiddleware(IGrainFactory grainFactory)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
cleaner = grainFactory.GetGrain<IAppCleanerGrain>(SingleGrain.Id);
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
if (context.IsCompleted)
{
switch (context.Command)
{
case ArchiveApp archiveApp:
await cleaner.EnqueueAppAsync(archiveApp.AppId);
break;
}
}
await next();
}
}
}

20
src/Squidex.Domain.Apps.Entities/Backup/IAppCleanerGrain.cs

@ -1,20 +0,0 @@
// ==========================================================================
// 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.Orleans;
namespace Squidex.Domain.Apps.Entities.Backup
{
public interface IAppCleanerGrain : IBackgroundGrain
{
Task EnqueueAppAsync(Guid appId);
Task<CleanerStatus> GetStatusAsync(Guid appId);
}
}

24
src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs

@ -30,7 +30,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IAssetStore assetStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly IGrainFactory grainFactory;
private readonly IAppCleanerGrain appCleaner;
private readonly ISemanticLog log;
private readonly IEventStore eventStore;
private readonly IBackupArchiveLocation backupArchiveLocation;
@ -75,8 +74,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.handlers = handlers;
this.store = store;
this.log = log;
appCleaner = grainFactory.GetGrain<IAppCleanerGrain>(SingleGrain.Id);
}
public override async Task OnActivateAsync(string key)
@ -244,8 +241,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
await Safe.CleanupRestoreAsync(handler, CurrentJob.AppId, CurrentJob.Id, log);
}
await appCleaner.EnqueueAppAsync(CurrentJob.AppId);
}
}
@ -274,8 +269,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
if (@event.Payload is AppCreated appCreated)
{
CurrentJob.AppId = appCreated.AppId.Id;
await CheckCleanupStatus();
}
else if (@event.Payload is AppContributorAssigned appContributorAssigned)
{
@ -302,23 +295,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
Log("Reading events completed.");
}
private async Task CheckCleanupStatus()
{
var cleaner = grainFactory.GetGrain<IAppCleanerGrain>(SingleGrain.Id);
var status = await cleaner.GetStatusAsync(CurrentJob.AppId);
if (status == CleanerStatus.Cleaning)
{
throw new BackupRestoreException("The app is removed in the background.");
}
if (status == CleanerStatus.Cleaning)
{
throw new BackupRestoreException("The app could not be cleaned.");
}
}
private void Log(string message, bool replace = false)
{
if (replace && CurrentJob.Log.Count > 0)

5
src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs

@ -34,11 +34,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
this.contentRepository = contentRepository;
}
public override Task RemoveAsync(Guid appId)
{
return contentRepository.RemoveAsync(appId);
}
public override Task RestoreEventAsync(Envelope<IEvent> @event, Guid appId, BackupReader reader)
{
switch (@event.Payload)

34
src/Squidex.Domain.Apps.Entities/History/BackupHistory.cs

@ -1,34 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Entities.History.Repositories;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.History
{
public sealed class BackupHistory : BackupHandler
{
private readonly IHistoryEventRepository historyEventRepository;
public override string Name { get; } = "History";
public BackupHistory(IHistoryEventRepository historyEventRepository)
{
Guard.NotNull(historyEventRepository, nameof(historyEventRepository));
this.historyEventRepository = historyEventRepository;
}
public override Task RemoveAsync(Guid appId)
{
return historyEventRepository.RemoveAsync(appId);
}
}
}

14
src/Squidex.Domain.Apps.Entities/Rules/BackupRules.cs

@ -40,20 +40,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
this.ruleEventRepository = ruleEventRepository;
}
public override async Task RemoveAsync(Guid appId)
{
await grainFactory.GetGrain<IRulesByAppIndex>(appId).ClearAsync();
var idsToRemove = await grainFactory.GetGrain<IRulesByAppIndex>(appId).GetRuleIdsAsync();
foreach (var ruleId in idsToRemove)
{
await RemoveSnapshotAsync<RuleState>(ruleId);
}
await ruleEventRepository.RemoveAsync(appId);
}
public override Task RestoreEventAsync(Envelope<IEvent> @event, Guid appId, BackupReader reader)
{
switch (@event.Payload)

14
src/Squidex.Domain.Apps.Entities/Schemas/BackupSchemas.cs

@ -42,20 +42,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
this.grainFactory = grainFactory;
}
public override async Task RemoveAsync(Guid appId)
{
var index = grainFactory.GetGrain<ISchemasByAppIndex>(appId);
var idsToRemove = await index.GetSchemaIdsAsync();
foreach (var schemaId in idsToRemove)
{
await RemoveSnapshotAsync<SchemaState>(schemaId);
}
await index.ClearAsync();
}
public override Task RestoreEventAsync(Envelope<IEvent> @event, Guid appId, BackupReader reader)
{
switch (@event.Payload)

18
src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs

@ -23,7 +23,8 @@ namespace Squidex.Areas.Api.Controllers.Backups
[ApiAuthorize]
[ApiExceptionFilter]
[ApiModelValidation(true)]
[SwaggerTag(nameof(Backups))]
[MustBeAdministrator]
[SwaggerIgnore]
public class RestoreController : ApiController
{
private readonly IGrainFactory grainFactory;
@ -34,16 +35,8 @@ namespace Squidex.Areas.Api.Controllers.Backups
this.grainFactory = grainFactory;
}
/// <summary>
/// Get the restore jobs.
/// </summary>
/// <returns>
/// 200 => Restore job returned.
/// 404 => App not found.
/// </returns>
[HttpGet]
[Route("apps/restore/")]
[ProducesResponseType(typeof(RestoreJobDto), 200)]
[ApiCosts(0)]
public async Task<IActionResult> GetJob()
{
@ -63,13 +56,6 @@ namespace Squidex.Areas.Api.Controllers.Backups
return Ok(response);
}
/// <summary>
/// Start a new restore job.
/// </summary>
/// <param name="request">The request object.</param>
/// <returns>
/// 204 => Backup started.
/// </returns>
[HttpPost]
[Route("apps/restore/")]
[ApiCosts(0)]

6
src/Squidex/Config/Domain/EntitiesServices.cs

@ -178,9 +178,6 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<CreateProfileCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<EnqueueAppToCleanerMiddleware>()
.As<ICommandMiddleware>();
}
private static void AddBackupHandlers(this IServiceCollection services)
@ -194,9 +191,6 @@ namespace Squidex.Config.Domain
services.AddTransientAs<BackupContents>()
.As<BackupHandler>();
services.AddTransientAs<BackupHistory>()
.As<BackupHandler>();
services.AddTransientAs<BackupRules>()
.As<BackupHandler>();

2
src/Squidex/Config/Orleans/SiloWrapper.cs

@ -15,7 +15,6 @@ using Orleans;
using Orleans.Configuration;
using Orleans.Hosting;
using Squidex.Config.Domain;
using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Infrastructure;
@ -63,7 +62,6 @@ namespace Squidex.Config.Orleans
.EnableDirectClient()
.AddIncomingGrainCallFilter<LocalCacheFilter>()
.AddStartupTask<Bootstrap<IContentSchedulerGrain>>()
.AddStartupTask<Bootstrap<IAppCleanerGrain>>()
.AddStartupTask<Bootstrap<IEventConsumerManagerGrain>>()
.AddStartupTask<Bootstrap<IRuleDequeuerGrain>>()
.AddStartupTask((services, ct) =>

5
src/Squidex/app/features/administration/administration-area.component.html

@ -12,6 +12,11 @@
<i class="nav-icon icon-user-o"></i> <div class="nav-text">Users</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="restore" routerLinkActive="active">
<i class="nav-icon icon-backup"></i> <div class="nav-text">Restore</div>
</a>
</li>
</ul>
</div>

1
src/Squidex/app/features/administration/declarations.ts

@ -11,6 +11,7 @@ export * from './guards/user-must-exist.guard';
export * from './guards/unset-user.guard';
export * from './pages/event-consumers/event-consumers-page.component';
export * from './pages/restore/restore-page.component';
export * from './pages/users/user-page.component';
export * from './pages/users/users-page.component';

6
src/Squidex/app/features/administration/module.ts

@ -18,6 +18,7 @@ import {
EventConsumersPageComponent,
EventConsumersService,
EventConsumersState,
RestorePageComponent,
UnsetUserGuard,
UserMustExistGuard,
UserPageComponent,
@ -38,6 +39,10 @@ const routes: Routes = [
path: 'event-consumers',
component: EventConsumersPageComponent
},
{
path: 'restore',
component: RestorePageComponent
},
{
path: 'users',
component: UsersPageComponent,
@ -69,6 +74,7 @@ const routes: Routes = [
declarations: [
AdministrationAreaComponent,
EventConsumersPageComponent,
RestorePageComponent,
UserPageComponent,
UsersPageComponent
],

65
src/Squidex/app/features/administration/pages/restore/restore-page.component.html

@ -0,0 +1,65 @@
<sqx-title message="Restore Backup"></sqx-title>
<sqx-panel theme="light" desiredWidth="70rem">
<ng-container title>
Restore Backup
</ng-container>
<ng-container content>
<ng-container *ngIf="restoreJob; let job">
<div class="card section">
<div class="card-header">
<div class="row no-gutters">
<div class="col col-auto pr-2">
<div *ngIf="job.status === 'Started'" class="restore-status restore-status-pending spin">
<i class="icon-hour-glass"></i>
</div>
<div *ngIf="job.status === 'Failed'" class="restore-status restore-status-failed">
<i class="icon-exclamation"></i>
</div>
<div *ngIf="job.status === 'Completed'" class="restore-status restore-status-success">
<i class="icon-checkmark"></i>
</div>
</div>
<div class="col">
<h3>Last Restore Operation</h3>
</div>
<div class="col text-right restore-url">
{{job.url}}
</div>
</div>
</div>
<div class="card-body">
<div *ngFor="let row of job.log">
{{row}}
</div>
</div>
<div class="card-footer text-muted">
<div class="row">
<div class="col">
Started: {{job.started | sqxISODate}}
</div>
<div class="col text-right" *ngIf="job.stopped">
Stopped: {{job.stopped | sqxISODate}}
</div>
</div>
</div>
</div>
</ng-container>
<div class="table-items-row">
<form [formGroup]="restoreForm.form" (submit)="restore()">
<div class="row no-gutters">
<div class="col">
<input class="form-control" name="url" formControlName="url" placeholder="Url to backup" />
</div>
<div class="col col-auto pl-1">
<button type="submit" class="btn btn-success" [disabled]="restoreForm.hasNoUrl | async">Restore Backup</button>
</div>
</div>
</form>
</div>
</ng-container>
</sqx-panel>

1
src/Squidex/app/features/apps/pages/restore-page.component.scss → src/Squidex/app/features/administration/pages/restore/restore-page.component.scss

@ -62,6 +62,7 @@ h3 {
}
&-url {
@include truncate;
line-height: 30px;
}
}

0
src/Squidex/app/features/apps/pages/restore-page.component.ts → src/Squidex/app/features/administration/pages/restore/restore-page.component.ts

3
src/Squidex/app/features/apps/declarations.ts

@ -6,5 +6,4 @@
*/
export * from './pages/apps-page.component';
export * from './pages/onboarding-dialog.component';
export * from './pages/restore-page.component';
export * from './pages/onboarding-dialog.component';

9
src/Squidex/app/features/apps/module.ts

@ -12,17 +12,13 @@ import { SqxFrameworkModule, SqxSharedModule } from '@app/shared';
import {
AppsPageComponent,
OnboardingDialogComponent,
RestorePageComponent
OnboardingDialogComponent
} from './declarations';
const routes: Routes = [
{
path: '',
component: AppsPageComponent
}, {
path: 'restore',
component: RestorePageComponent
}
];
@ -34,8 +30,7 @@ const routes: Routes = [
],
declarations: [
AppsPageComponent,
OnboardingDialogComponent,
RestorePageComponent
OnboardingDialogComponent
]
})
export class SqxFeatureAppsModule { }

4
src/Squidex/app/features/apps/pages/apps-page.component.html

@ -76,10 +76,6 @@
</div>
</div>
<div class="apps-section">
<a class="restore-link" [routerLink]="['/app/restore']">Restore app from backup</a>
</div>
<ng-container *sqxModalView="addAppDialog;onRoot:true">
<sqx-app-form [template]="addAppTemplate"
(completed)="addAppDialog.hide()">

4
src/Squidex/app/features/apps/pages/apps-page.component.scss

@ -17,10 +17,6 @@
}
}
.restore-link {
font-size: 0.8rem;
}
.card {
& {
margin-right: 1rem;

59
src/Squidex/app/features/apps/pages/restore-page.component.html

@ -1,59 +0,0 @@
<sqx-title message="Restore Backup"></sqx-title>
<div class="container">
<ng-container *ngIf="restoreJob; let job">
<div class="card section">
<div class="card-header">
<div class="row no-gutters">
<div class="col col-auto pr-2">
<div *ngIf="job.status === 'Started'" class="restore-status restore-status-pending spin">
<i class="icon-hour-glass"></i>
</div>
<div *ngIf="job.status === 'Failed'" class="restore-status restore-status-failed">
<i class="icon-exclamation"></i>
</div>
<div *ngIf="job.status === 'Completed'" class="restore-status restore-status-success">
<i class="icon-checkmark"></i>
</div>
</div>
<div class="col">
<h3>Last Restore Operation</h3>
</div>
<div class="col text-right restore-url">
{{job.url}}
</div>
</div>
</div>
<div class="card-body">
<div *ngFor="let row of job.log">
{{row}}
</div>
</div>
<div class="card-footer text-muted">
<div class="row">
<div class="col">
Started: {{job.started | sqxISODate}}
</div>
<div class="col text-right" *ngIf="job.stopped">
Stopped: {{job.stopped | sqxISODate}}
</div>
</div>
</div>
</div>
</ng-container>
<div class="table-items-row">
<form [formGroup]="restoreForm.form" (submit)="restore()">
<div class="row no-gutters">
<div class="col">
<input class="form-control" name="url" formControlName="url" placeholder="Url to backup" />
</div>
<div class="col col-auto pl-1">
<button type="submit" class="btn btn-success" [disabled]="restoreForm.hasNoUrl | async">Restore Backup</button>
</div>
</div>
</form>
</div>
</div>

18
src/Squidex/app/theme/icomoon/demo.html

@ -9,10 +9,26 @@
<link rel="stylesheet" href="style.css"></head>
<body>
<div class="bgc1 clearfix">
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;94)</small></h1>
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;95)</small></h1>
</div>
<div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 24</h1>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-backup">
</span>
<span class="mls"> icon-backup</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95b" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95b;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-support">

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

1
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -98,6 +98,7 @@
<glyph unicode="&#xe958;" glyph-name="single-content" horiz-adv-x="1029" d="M251.429 850.277c-87.896 0-160-72.104-160-160v-525.714c0-87.896 72.104-160 160-160h525.714c87.896 0 160 72.104 160 160v525.714c0 87.896-72.104 160-160 160zM251.429 804.562h525.714c62.961 0 114.286-51.325 114.286-114.286v-525.714c0-62.961-51.325-114.286-114.286-114.286h-525.714c-62.961 0-114.286 51.325-114.286 114.286v525.714c0 62.961 51.325 114.286 114.286 114.286zM251.429 644.562c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h525.697c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017zM251.429 507.419c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h297.126c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017zM251.429 370.277c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h297.126c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017z" />
<glyph unicode="&#xe959;" glyph-name="action-Medium" d="M121.429 688.64c1.28 12.587-3.541 25.003-12.928 33.451l-95.573 115.2v17.195h296.832l229.461-503.253 201.728 503.253h283.051v-17.195l-81.792-78.379c-7.040-5.376-10.539-14.208-9.088-22.955v-576c-1.451-8.704 2.048-17.493 9.088-22.912l79.787-78.379v-17.195h-401.493v17.195l82.645 80.299c8.107 8.107 8.107 10.496 8.107 22.955v465.536l-229.973-584.021h-31.019l-267.733 584.021v-391.424c-2.219-16.469 3.243-33.024 14.805-44.928l107.52-130.56v-17.152h-304.853v17.195l107.52 130.56c11.52 11.861 16.64 28.587 13.909 44.885v452.608z" />
<glyph unicode="&#xe95a;" glyph-name="support" d="M726 426.667c0-24-20-42-44-42h-426l-170-172v598c0 24 18 42 42 42h554c24 0 44-18 44-42v-384zM896 682.667c24 0 42-18 42-42v-640l-170 170h-470c-24 0-42 18-42 42v86h554v384h86z" />
<glyph unicode="&#xe95b;" glyph-name="backup" d="M512 810.667c212 0 384-172 384-384s-172-384-384-384c-88 0-170 30-234 80l60 60c50-34 110-54 174-54 166 0 298 132 298 298s-132 298-298 298-298-132-298-298h128l-172-170-170 170h128c0 212 172 384 384 384zM598 426.667c0-46-40-86-86-86s-86 40-86 86 40 86 86 86 86-40 86-86z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
<glyph unicode="&#xf0c9;" glyph-name="list" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

219
src/Squidex/app/theme/icomoon/selection.json

@ -1,6 +1,35 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M512 128c212 0 384 172 384 384s-172 384-384 384c-88 0-170-30-234-80l60-60c50 34 110 54 174 54 166 0 298-132 298-298s-132-298-298-298-298 132-298 298h128l-172 170-170-170h128c0-212 172-384 384-384zM598 512c0 46-40 86-86 86s-86-40-86-86 40-86 86-86 86 40 86 86z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"settings_backup_restore"
],
"grid": 24
},
"attrs": [
{}
],
"properties": {
"order": 1,
"id": 2,
"prevSize": 24,
"code": 59739,
"name": "backup"
},
"setIdx": 1,
"setId": 3,
"iconIdx": 0
},
{
"icon": {
"paths": [
@ -26,9 +55,9 @@
"code": 59738,
"name": "support"
},
"setIdx": 0,
"setIdx": 1,
"setId": 3,
"iconIdx": 0
"iconIdx": 2
},
{
"icon": {
@ -55,7 +84,7 @@
"code": 59705,
"name": "control-RichText"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 90
},
@ -84,7 +113,7 @@
"code": 59710,
"name": "download"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 91
},
@ -113,7 +142,7 @@
"code": 59737,
"name": "action-Medium"
},
"setIdx": 1,
"setIdx": 2,
"setId": 2,
"iconIdx": 0
},
@ -142,7 +171,7 @@
"prevSize": 32,
"code": 59729
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 44
},
@ -172,7 +201,7 @@
"prevSize": 32,
"code": 59726
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 45
},
@ -204,7 +233,7 @@
"prevSize": 32,
"code": 59727
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 46
},
@ -233,7 +262,7 @@
"prevSize": 32,
"code": 59724
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 47
},
@ -271,7 +300,7 @@
"prevSize": 32,
"code": 59722
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 48
},
@ -300,7 +329,7 @@
"prevSize": 32,
"code": 59652
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 49
},
@ -329,7 +358,7 @@
"prevSize": 32,
"code": 59653
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 50
},
@ -358,7 +387,7 @@
"prevSize": 32,
"code": 59654
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 51
},
@ -387,7 +416,7 @@
"prevSize": 32,
"code": 59655
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 52
},
@ -416,7 +445,7 @@
"prevSize": 32,
"code": 59656
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 53
},
@ -445,7 +474,7 @@
"prevSize": 32,
"code": 59657
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 54
},
@ -474,7 +503,7 @@
"prevSize": 32,
"code": 59658
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 55
},
@ -503,7 +532,7 @@
"prevSize": 32,
"code": 59659
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 56
},
@ -532,7 +561,7 @@
"prevSize": 32,
"code": 59660
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 57
},
@ -561,7 +590,7 @@
"prevSize": 32,
"code": 59661
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 58
},
@ -590,7 +619,7 @@
"prevSize": 32,
"code": 59662
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 59
},
@ -619,7 +648,7 @@
"prevSize": 32,
"code": 59663
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 60
},
@ -648,7 +677,7 @@
"prevSize": 32,
"code": 59664
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 61
},
@ -683,7 +712,7 @@
"prevSize": 32,
"code": 59665
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 62
},
@ -712,7 +741,7 @@
"prevSize": 32,
"code": 59666
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 63
},
@ -741,7 +770,7 @@
"prevSize": 32,
"code": 59667
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 64
},
@ -770,7 +799,7 @@
"prevSize": 32,
"code": 59668
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 65
},
@ -799,7 +828,7 @@
"prevSize": 32,
"code": 59669
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 66
},
@ -828,7 +857,7 @@
"prevSize": 32,
"code": 59670
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 67
},
@ -857,7 +886,7 @@
"prevSize": 32,
"code": 59671
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 68
},
@ -886,7 +915,7 @@
"prevSize": 32,
"code": 59672
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 69
},
@ -916,7 +945,7 @@
"prevSize": 32,
"code": 59713
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 70
},
@ -945,7 +974,7 @@
"prevSize": 32,
"code": 59673
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 71
},
@ -974,7 +1003,7 @@
"prevSize": 32,
"code": 59675
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 72
},
@ -1003,7 +1032,7 @@
"prevSize": 32,
"code": 59676
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 73
},
@ -1032,7 +1061,7 @@
"prevSize": 32,
"code": 59677
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 74
},
@ -1061,7 +1090,7 @@
"prevSize": 32,
"code": 59678
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 75
},
@ -1090,7 +1119,7 @@
"prevSize": 32,
"code": 59679
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 76
},
@ -1119,7 +1148,7 @@
"prevSize": 32,
"code": 59680
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 77
},
@ -1157,7 +1186,7 @@
"prevSize": 32,
"code": 59681
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 78
},
@ -1186,7 +1215,7 @@
"prevSize": 32,
"code": 59682
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 79
},
@ -1215,7 +1244,7 @@
"prevSize": 32,
"code": 59683
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 80
},
@ -1244,7 +1273,7 @@
"prevSize": 32,
"code": 59684
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 81
},
@ -1273,7 +1302,7 @@
"prevSize": 32,
"code": 59685
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 82
},
@ -1302,7 +1331,7 @@
"prevSize": 32,
"code": 59674
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 83
},
@ -1331,7 +1360,7 @@
"prevSize": 32,
"code": 59686
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 84
},
@ -1360,7 +1389,7 @@
"prevSize": 32,
"code": 59687
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 85
},
@ -1389,7 +1418,7 @@
"prevSize": 32,
"code": 59688
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 86
},
@ -1428,7 +1457,7 @@
"prevSize": 28,
"code": 59736
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 0
},
@ -1470,7 +1499,7 @@
"prevSize": 28,
"code": 59735
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 1
},
@ -1508,7 +1537,7 @@
"prevSize": 28,
"code": 59734
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 3
},
@ -1538,7 +1567,7 @@
"code": 59733,
"name": "exclamation"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 4
},
@ -1568,7 +1597,7 @@
"prevSize": 28,
"code": 59730
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 5
},
@ -1598,7 +1627,7 @@
"code": 59725,
"name": "action-Slack"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 6
},
@ -1627,7 +1656,7 @@
"prevSize": 28,
"code": 59723
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 7
},
@ -1659,7 +1688,7 @@
"prevSize": 28,
"code": 59721
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 8
},
@ -1691,7 +1720,7 @@
"prevSize": 28,
"code": 59711
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 9
},
@ -1721,7 +1750,7 @@
"code": 59648,
"name": "angle-down"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 10
},
@ -1751,7 +1780,7 @@
"code": 59649,
"name": "angle-left"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 11
},
@ -1781,7 +1810,7 @@
"code": 59697,
"name": "angle-right"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 12
},
@ -1811,7 +1840,7 @@
"code": 59651,
"name": "angle-up"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 13
},
@ -1852,7 +1881,7 @@
"prevSize": 28,
"code": 59717
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 14
},
@ -1887,7 +1916,7 @@
"prevSize": 28,
"code": 59720
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 15
},
@ -1917,7 +1946,7 @@
"code": 59709,
"name": "bug"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 16
},
@ -1947,7 +1976,7 @@
"code": 59692,
"name": "caret-down"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 17
},
@ -1977,7 +2006,7 @@
"code": 59690,
"name": "caret-left"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 18
},
@ -2007,7 +2036,7 @@
"code": 59689,
"name": "caret-right"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 19
},
@ -2037,7 +2066,7 @@
"code": 59691,
"name": "caret-up"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 20
},
@ -2075,7 +2104,7 @@
"prevSize": 28,
"code": 59718
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 21
},
@ -2104,7 +2133,7 @@
"prevSize": 28,
"code": 59702
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 22
},
@ -2133,7 +2162,7 @@
"prevSize": 28,
"code": 59703
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 23
},
@ -2162,7 +2191,7 @@
"prevSize": 28,
"code": 59704
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 24
},
@ -2189,7 +2218,7 @@
"prevSize": 28,
"code": 61450
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 25
},
@ -2218,7 +2247,7 @@
"prevSize": 28,
"code": 61641
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 26
},
@ -2247,7 +2276,7 @@
"prevSize": 28,
"code": 59698
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 27
},
@ -2297,7 +2326,7 @@
"prevSize": 28,
"code": 59719
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 28
},
@ -2329,7 +2358,7 @@
"code": 59732,
"name": "hour-glass"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 29
},
@ -2362,7 +2391,7 @@
"code": 59731,
"name": "spinner"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 30
},
@ -2393,7 +2422,7 @@
"code": 59728,
"name": "clock"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 31
},
@ -2430,7 +2459,7 @@
"prevSize": 32,
"code": 59650
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 32
},
@ -2463,7 +2492,7 @@
"prevSize": 32,
"code": 59850
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 33
},
@ -2496,7 +2525,7 @@
"code": 59715,
"name": "elapsed"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 34
},
@ -2526,7 +2555,7 @@
"code": 59707,
"name": "google"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 35
},
@ -2558,7 +2587,7 @@
"code": 59700,
"name": "lock"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 36
},
@ -2589,7 +2618,7 @@
"code": 59712,
"name": "microsoft, action-AzureQueue"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 37
},
@ -2619,7 +2648,7 @@
"code": 59695,
"name": "pause"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 38
},
@ -2649,7 +2678,7 @@
"code": 59696,
"name": "play"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 39
},
@ -2688,7 +2717,7 @@
"code": 59694,
"name": "reset"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 40
},
@ -2723,7 +2752,7 @@
"code": 59693,
"name": "settings2"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 41
},
@ -2757,7 +2786,7 @@
"code": 59716,
"name": "timeout"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 42
},
@ -2787,7 +2816,7 @@
"code": 59699,
"name": "unlocked"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 43
},
@ -2820,7 +2849,7 @@
"code": 59701,
"name": "browser"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 87
},
@ -2852,7 +2881,7 @@
"code": 59714,
"name": "checkmark"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 88
},
@ -2882,7 +2911,7 @@
"code": 59706,
"name": "control-Stars"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 89
},
@ -2911,7 +2940,7 @@
"code": 59708,
"name": "info"
},
"setIdx": 2,
"setIdx": 3,
"setId": 1,
"iconIdx": 92
}

13
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?lx1t7v');
src: url('fonts/icomoon.eot?lx1t7v#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?lx1t7v') format('truetype'),
url('fonts/icomoon.woff?lx1t7v') format('woff'),
url('fonts/icomoon.svg?lx1t7v#icomoon') format('svg');
src: url('fonts/icomoon.eot?za0y3d');
src: url('fonts/icomoon.eot?za0y3d#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?za0y3d') format('truetype'),
url('fonts/icomoon.woff?za0y3d') format('woff'),
url('fonts/icomoon.svg?za0y3d#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,6 +24,9 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-backup:before {
content: "\e95b";
}
.icon-support:before {
content: "\e95a";
}

48
tests/Squidex.Domain.Apps.Entities.Tests/Backup/EnqueueAppToCleanerMiddlewareTests.cs

@ -1,48 +0,0 @@
// ==========================================================================
// 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.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Backup
{
public class EnqueueAppToCleanerMiddlewareTests
{
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly IAppCleanerGrain index = A.Fake<IAppCleanerGrain>();
private readonly Guid appId = Guid.NewGuid();
private readonly EnqueueAppToCleanerMiddleware sut;
public EnqueueAppToCleanerMiddlewareTests()
{
A.CallTo(() => grainFactory.GetGrain<IAppCleanerGrain>(SingleGrain.Id, null))
.Returns(index);
sut = new EnqueueAppToCleanerMiddleware(grainFactory);
}
[Fact]
public async Task Should_enqueue_for_cleanup_on_archive()
{
var context =
new CommandContext(new ArchiveApp { AppId = appId }, commandBus)
.Complete();
await sut.HandleAsync(context);
A.CallTo(() => index.EnqueueAppAsync(appId))
.MustHaveHappened();
}
}
}
Loading…
Cancel
Save