Browse Source

Status queries.

pull/377/head
Sebastian Stehle 7 years ago
parent
commit
643ba11802
  1. 3
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  2. 2
      src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs
  3. 15
      src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs
  4. 13
      src/Squidex.Domain.Apps.Entities/History/ParsedHistoryEvent.cs
  5. 11
      src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs
  6. 3
      src/Squidex/Areas/Api/Controllers/History/HistoryController.cs
  7. 2
      src/Squidex/app-config/webpack.config.js
  8. 5
      src/Squidex/app/features/content/pages/content/content-page.component.html
  9. 4
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  10. 5
      src/Squidex/app/features/content/shared/content-item.component.html
  11. 4
      src/Squidex/app/features/content/shared/content-status.component.html
  12. 12
      src/Squidex/app/features/content/shared/content-status.component.scss
  13. 3
      src/Squidex/app/features/content/shared/content-status.component.ts
  14. 2
      src/Squidex/app/shared/components/schema-category.component.ts
  15. 2
      src/Squidex/app/shared/services/contents.service.spec.ts
  16. 6
      src/Squidex/app/shared/services/contents.service.ts
  17. 4
      src/Squidex/app/theme/_bootstrap.scss
  18. 2
      tools/Migrate_01/OldEvents/ContentCreated.cs
  19. 2
      tools/Migrate_01/OldEvents/ContentStatusChanged.cs
  20. 39
      tools/Migrate_01/OldEvents/ContentStatusScheduled.cs

3
src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -73,6 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
var result = new AssetCreatedResult(existing, true); var result = new AssetCreatedResult(existing, true);
context.Complete(result); context.Complete(result);
await next();
return; return;
} }
} }
@ -128,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
private async Task HandleCoreAsync(CommandContext context, Func<Task> next) private async Task HandleCoreAsync(CommandContext context, Func<Task> next)
{ {
await HandleAsync(context, next); await base.HandleAsync(context, next);
if (context.PlainResult is IAssetEntity asset && !(context.PlainResult is IAssetEntityEnriched)) if (context.PlainResult is IAssetEntity asset && !(context.PlainResult is IAssetEntityEnriched))
{ {

2
src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public override async Task HandleAsync(CommandContext context, Func<Task> next) public override async Task HandleAsync(CommandContext context, Func<Task> next)
{ {
await HandleAsync(context, next); await base.HandleAsync(context, next);
if (context.PlainResult is IContentEntity content) if (context.PlainResult is IContentEntity content)
{ {

15
src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs

@ -72,19 +72,24 @@ namespace Squidex.Domain.Apps.Entities.Contents
private async Task ResolveColorAsync(IContentEntity content, ContentEntity result, Dictionary<(Guid, Status), StatusInfo> cache) private async Task ResolveColorAsync(IContentEntity content, ContentEntity result, Dictionary<(Guid, Status), StatusInfo> cache)
{ {
if (!cache.TryGetValue((content.SchemaId.Id, content.Status), out var info)) result.StatusColor = await GetColorAsync(content.SchemaId, content.Status, cache);
}
private async Task<string> GetColorAsync(NamedId<Guid> schemaId, Status status, Dictionary<(Guid, Status), StatusInfo> cache)
{
if (!cache.TryGetValue((schemaId.Id, status), out var info))
{ {
info = await contentWorkflow.GetInfoAsync(content.Status); info = await contentWorkflow.GetInfoAsync(status);
if (info == null) if (info == null)
{ {
info = new StatusInfo(content.Status, DefaultColor); info = new StatusInfo(status, DefaultColor);
} }
cache[(content.SchemaId.Id, content.Status)] = info; cache[(schemaId.Id, status)] = info;
} }
result.StatusColor = info.Color; return info.Color;
} }
} }
} }

13
src/Squidex.Domain.Apps.Entities/History/ParsedHistoryEvent.cs

@ -53,14 +53,17 @@ namespace Squidex.Domain.Apps.Entities.History
message = new Lazy<string>(() => message = new Lazy<string>(() =>
{ {
var result = texts[item.Message]; if (texts.TryGetValue(item.Message, out var result))
foreach (var kvp in item.Parameters)
{ {
result = result.Replace("[" + kvp.Key + "]", kvp.Value); foreach (var kvp in item.Parameters)
{
result = result.Replace("[" + kvp.Key + "]", kvp.Value);
}
return result;
} }
return result; return null;
}); });
} }
} }

11
src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Diagnostics;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
namespace Squidex.Infrastructure.EventSourcing namespace Squidex.Infrastructure.EventSourcing
@ -33,6 +34,11 @@ namespace Squidex.Infrastructure.EventSourcing
if (payloadObj is IMigrated<IEvent> migratedEvent) if (payloadObj is IMigrated<IEvent> migratedEvent)
{ {
payloadObj = migratedEvent.Migrate(); payloadObj = migratedEvent.Migrate();
if (ReferenceEquals(migratedEvent, payloadObj))
{
Debug.WriteLine("Migration should return new event.");
}
} }
var envelope = new Envelope<IEvent>(payloadObj, eventData.Headers); var envelope = new Envelope<IEvent>(payloadObj, eventData.Headers);
@ -47,6 +53,11 @@ namespace Squidex.Infrastructure.EventSourcing
if (migrate && eventPayload is IMigrated<IEvent> migratedEvent) if (migrate && eventPayload is IMigrated<IEvent> migratedEvent)
{ {
eventPayload = migratedEvent.Migrate(); eventPayload = migratedEvent.Migrate();
if (ReferenceEquals(migratedEvent, eventPayload))
{
Debug.WriteLine("Migration should return new event.");
}
} }
var payloadType = typeNameRegistry.GetName(eventPayload.GetType()); var payloadType = typeNameRegistry.GetName(eventPayload.GetType());

3
src/Squidex/Areas/Api/Controllers/History/HistoryController.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Squidex.Areas.Api.Controllers.History.Models; using Squidex.Areas.Api.Controllers.History.Models;
@ -48,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.History
{ {
var events = await historyService.QueryByChannelAsync(AppId, channel, 100); var events = await historyService.QueryByChannelAsync(AppId, channel, 100);
var response = events.ToArray(HistoryEventDto.FromHistoryEvent); var response = events.Select(HistoryEventDto.FromHistoryEvent).Where(x => x.Message != null).ToArray();
return Ok(response); return Ok(response);
} }

2
src/Squidex/app-config/webpack.config.js

@ -251,7 +251,9 @@ module.exports = function (env) {
waitForLinting: isProduction waitForLinting: isProduction
}) })
); );
}
if (!isCoverage) {
config.plugins.push( config.plugins.push(
new plugins.NgToolsWebpack.AngularCompilerPlugin({ new plugins.NgToolsWebpack.AngularCompilerPlugin({
directTemplateLoading: true, directTemplateLoading: true,

5
src/Squidex/app/features/content/pages/content/content-page.component.html

@ -36,6 +36,7 @@
[class.active]="dropdown.isOpen | async" #optionsButton> [class.active]="dropdown.isOpen | async" #optionsButton>
<sqx-content-status <sqx-content-status
[status]="content.status" [status]="content.status"
[statusColor]="content.statusColor"
[scheduledTo]="content.scheduleJob?.status" [scheduledTo]="content.scheduleJob?.status"
[scheduledAt]="content.scheduleJob?.dueTime" [scheduledAt]="content.scheduleJob?.dueTime"
[isPending]="content.isPending" [isPending]="content.isPending"
@ -56,8 +57,8 @@
</a> </a>
<ng-container *ngIf="!schema.isSingleton"> <ng-container *ngIf="!schema.isSingleton">
<a class="dropdown-item" *ngFor="let status of content.statusUpdates" (click)="changeStatus(status)"> <a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="changeStatus(info.status)">
Status to {{status}} Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>

4
src/Squidex/app/features/content/pages/contents/contents-page.component.ts

@ -207,8 +207,8 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit {
const allActions = {}; const allActions = {};
for (let content of this.contentsState.snapshot.contents.values) { for (let content of this.contentsState.snapshot.contents.values) {
for (let status of content.statusUpdates) { for (let info of content.statusUpdates) {
allActions[status] = true; allActions[info.status] = info.color;
} }
} }

5
src/Squidex/app/features/content/shared/content-item.component.html

@ -24,6 +24,7 @@
<td class="cell-time" *ngIf="!isCompact" [sqxStopClick]="isDirty"> <td class="cell-time" *ngIf="!isCompact" [sqxStopClick]="isDirty">
<sqx-content-status <sqx-content-status
[status]="content.status" [status]="content.status"
[statusColor]="content.statusColor"
[scheduledTo]="content.scheduleJob?.status" [scheduledTo]="content.scheduleJob?.status"
[scheduledAt]="content.scheduleJob?.dueTime" [scheduledAt]="content.scheduleJob?.dueTime"
[isPending]="content.isPending"> [isPending]="content.isPending">
@ -55,8 +56,8 @@
<i class="icon-dots"></i> <i class="icon-dots"></i>
</button> </button>
<div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="optionsButton" @fade> <div class="dropdown-menu" *sqxModalView="dropdown;closeAlways:true" [sqxModalTarget]="optionsButton" @fade>
<a class="dropdown-item" *ngFor="let status of content.statusUpdates" (click)="emitChangeStatus(status)"> <a class="dropdown-item" *ngFor="let info of content.statusUpdates" (click)="emitChangeStatus(info.status)">
Status to {{status}} Change to <i class="icon-circle icon-sm" [style.color]="info.color"></i> {{info.status}}
</a> </a>
<a class="dropdown-item" (click)="emitClone(); dropdown.hide()" *ngIf="canClone"> <a class="dropdown-item" (click)="emitClone(); dropdown.hide()" *ngIf="canClone">
Clone Clone

4
src/Squidex/app/features/content/shared/content-status.component.html

@ -1,11 +1,11 @@
<ng-container *ngIf="scheduledTo; else noSchedule"> <ng-container *ngIf="scheduledTo; else noSchedule">
<span class="content-status content-status-{{scheduledTo | lowercase}} mr-1" title="{{tooltipText}}" titlePosition="top"> <span class="content-status pending mr-1"title="{{tooltipText}}" titlePosition="top">
<i class="icon-clock"></i> <i class="icon-clock"></i>
</span> </span>
</ng-container> </ng-container>
<ng-template #noSchedule> <ng-template #noSchedule>
<span class="content-status content-status-{{displayStatus | lowercase}} mr-1" title="{{tooltipText}}" titlePosition="top"> <span class="content-status default mr-1" [style.color]="statusColor" title="{{tooltipText}}" titlePosition="top">
<i class="icon-circle"></i> <i class="icon-circle"></i>
</span> </span>
</ng-template> </ng-template>

12
src/Squidex/app/features/content/shared/content-status.component.scss

@ -6,19 +6,11 @@
vertical-align: middle; vertical-align: middle;
} }
&-published { &.default {
color: $color-theme-green;
}
&-draft {
color: $color-text-decent; color: $color-text-decent;
} }
&-archived { &.pending {
color: $color-theme-error;
}
&-pending {
color: $color-dark-black; color: $color-dark-black;
} }

3
src/Squidex/app/features/content/shared/content-status.component.ts

@ -19,6 +19,9 @@ export class ContentStatusComponent {
@Input() @Input()
public status: string; public status: string;
@Input()
public statusColor: string;
@Input() @Input()
public scheduledTo?: string; public scheduledTo?: string;

2
src/Squidex/app/shared/components/schema-category.component.ts

@ -74,7 +74,7 @@ export class SchemaCategoryComponent extends StatefulComponent<State> implements
let filtered = this.schemaCategory.schemas; let filtered = this.schemaCategory.schemas;
if (this.forContent) { if (this.forContent) {
filtered = filtered.filter(x => x.canReadContents); filtered = filtered.filter(x => x.canReadContents && x.isPublished);
} }
let isOpen = false; let isOpen = false;

2
src/Squidex/app/shared/services/contents.service.spec.ts

@ -353,6 +353,7 @@ describe('ContentsService', () => {
return { return {
id: `id${id}`, id: `id${id}`,
status: `Status${id}`, status: `Status${id}`,
statusColor: 'black',
created: `${id % 1000 + 2000}-12-12T10:10:00`, created: `${id % 1000 + 2000}-12-12T10:10:00`,
createdBy: `creator-${id}`, createdBy: `creator-${id}`,
lastModified: `${id % 1000 + 2000}-11-11T10:10:00`, lastModified: `${id % 1000 + 2000}-11-11T10:10:00`,
@ -381,6 +382,7 @@ export function createContent(id: number, suffix = '') {
return new ContentDto(links, return new ContentDto(links,
`id${id}`, `id${id}`,
`Status${id}${suffix}`, `Status${id}${suffix}`,
'black',
DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), `creator-${id}`, DateTime.parseISO_UTC(`${id % 1000 + 2000}-12-12T10:10:00`), `creator-${id}`,
DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), `modifier-${id}`, DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`), `modifier-${id}`,
new ScheduleDto('Draft', `Scheduler${id}`, DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`)), new ScheduleDto('Draft', `Scheduler${id}`, DateTime.parseISO_UTC(`${id % 1000 + 2000}-11-11T10:10:00`)),

6
src/Squidex/app/shared/services/contents.service.ts

@ -58,7 +58,7 @@ export class ContentsDto extends ResultSet<ContentDto> {
export class ContentDto { export class ContentDto {
public readonly _links: ResourceLinks; public readonly _links: ResourceLinks;
public readonly statusUpdates: string[]; public readonly statusUpdates: StatusInfo[];
public readonly canDelete: boolean; public readonly canDelete: boolean;
public readonly canDraftDiscard: boolean; public readonly canDraftDiscard: boolean;
@ -69,6 +69,7 @@ export class ContentDto {
constructor(links: ResourceLinks, constructor(links: ResourceLinks,
public readonly id: string, public readonly id: string,
public readonly status: string, public readonly status: string,
public readonly statusColor: string,
public readonly created: DateTime, public readonly created: DateTime,
public readonly createdBy: string, public readonly createdBy: string,
public readonly lastModified: DateTime, public readonly lastModified: DateTime,
@ -87,7 +88,7 @@ export class ContentDto {
this.canDraftPublish = hasAnyLink(links, 'draft/publish'); this.canDraftPublish = hasAnyLink(links, 'draft/publish');
this.canUpdate = hasAnyLink(links, 'update'); this.canUpdate = hasAnyLink(links, 'update');
this.statusUpdates = Object.keys(links).filter(x => x.startsWith('status/')).map(x => x.substr(7)); this.statusUpdates = Object.keys(links).filter(x => x.startsWith('status/')).map(x => ({ status: x.substr(7), color: links[x].metadata! }));
} }
} }
@ -284,6 +285,7 @@ function parseContent(response: any) {
return new ContentDto(response._links, return new ContentDto(response._links,
response.id, response.id,
response.status, response.status,
response.statusColor,
DateTime.parseISO_UTC(response.created), response.createdBy, DateTime.parseISO_UTC(response.created), response.createdBy,
DateTime.parseISO_UTC(response.lastModified), response.lastModifiedBy, DateTime.parseISO_UTC(response.lastModified), response.lastModifiedBy,
response.scheduleJob response.scheduleJob

4
src/Squidex/app/theme/_bootstrap.scss

@ -304,6 +304,10 @@ a {
} }
} }
.icon-sm {
font-size: 70%;
}
// //
// Button improvements // Button improvements
// //

2
tools/Migrate_01/OldEvents/ContentCreated.cs

@ -32,7 +32,7 @@ namespace Migrate_01.OldEvents
migrated.Status = Status.Draft; migrated.Status = Status.Draft;
} }
return this; return migrated;
} }
} }
} }

2
tools/Migrate_01/OldEvents/ContentStatusChanged.cs

@ -41,7 +41,7 @@ namespace Migrate_01.OldEvents
migrated.Change = StatusChange.Change; migrated.Change = StatusChange.Change;
} }
return this; return migrated;
} }
} }
} }

39
tools/Migrate_01/OldEvents/ContentStatusScheduled.cs

@ -1,39 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using ContentStatusScheduledV2 = Squidex.Domain.Apps.Events.Contents.ContentStatusScheduled;
namespace Migrate_01.OldEvents
{
[EventType(nameof(ContentStatusScheduled))]
[Obsolete]
public sealed class ContentStatusScheduled : ContentEvent, IMigrated<IEvent>
{
public Status Status { get; set; }
public Instant DueTime { get; set; }
public IEvent Migrate()
{
var migrated = SimpleMapper.Map(this, new ContentStatusScheduledV2());
if (migrated.Status == default)
{
migrated.Status = Status.Draft;
}
return this;
}
}
}
Loading…
Cancel
Save