mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
251 changed files with 2689 additions and 1806 deletions
@ -0,0 +1,92 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using GraphQL.Resolvers; |
||||
|
using GraphQL.Types; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
||||
|
{ |
||||
|
public sealed class ContentInterfaceGraphType : InterfaceGraphType<IContentEntity> |
||||
|
{ |
||||
|
public ContentInterfaceGraphType() |
||||
|
{ |
||||
|
Name = $"Content"; |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "id", |
||||
|
ResolvedType = AllTypes.NonNullGuid, |
||||
|
Resolver = Resolve(x => x.Id), |
||||
|
Description = $"The id of the content." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "version", |
||||
|
ResolvedType = AllTypes.NonNullInt, |
||||
|
Resolver = Resolve(x => x.Version), |
||||
|
Description = $"The version of the content." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "created", |
||||
|
ResolvedType = AllTypes.NonNullDate, |
||||
|
Resolver = Resolve(x => x.Created), |
||||
|
Description = $"The date and time when the content has been created." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "createdBy", |
||||
|
ResolvedType = AllTypes.NonNullString, |
||||
|
Resolver = Resolve(x => x.CreatedBy.ToString()), |
||||
|
Description = $"The user that has created the content." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "lastModified", |
||||
|
ResolvedType = AllTypes.NonNullDate, |
||||
|
Resolver = Resolve(x => x.LastModified), |
||||
|
Description = $"The date and time when the content has been modified last." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "lastModifiedBy", |
||||
|
ResolvedType = AllTypes.NonNullString, |
||||
|
Resolver = Resolve(x => x.LastModifiedBy.ToString()), |
||||
|
Description = $"The user that has updated the content last." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "status", |
||||
|
ResolvedType = AllTypes.NonNullString, |
||||
|
Resolver = Resolve(x => x.Status.Name.ToUpperInvariant()), |
||||
|
Description = $"The the status of the content." |
||||
|
}); |
||||
|
|
||||
|
AddField(new FieldType |
||||
|
{ |
||||
|
Name = "statusColor", |
||||
|
ResolvedType = AllTypes.NonNullString, |
||||
|
Resolver = Resolve(x => x.StatusColor), |
||||
|
Description = $"The color status of the content." |
||||
|
}); |
||||
|
|
||||
|
Description = $"The structure of all content types."; |
||||
|
} |
||||
|
|
||||
|
private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object> action) |
||||
|
{ |
||||
|
return new FuncFieldResolver<IEnrichedContentEntity, object>(c => action(c.Source)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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 GraphQL.Types; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types |
||||
|
{ |
||||
|
public sealed class ContentUnionGraphType : UnionGraphType |
||||
|
{ |
||||
|
private readonly Dictionary<Guid, IObjectGraphType> types = new Dictionary<Guid, IObjectGraphType>(); |
||||
|
|
||||
|
public ContentUnionGraphType(string fieldName, Dictionary<Guid, ContentGraphType> schemaTypes, IEnumerable<Guid> schemaIds) |
||||
|
{ |
||||
|
Name = $"{fieldName}ReferenceUnionDto"; |
||||
|
|
||||
|
if (schemaIds?.Any() == true) |
||||
|
{ |
||||
|
foreach (var schemaId in schemaIds) |
||||
|
{ |
||||
|
var schemaType = schemaTypes.GetOrDefault(schemaId); |
||||
|
|
||||
|
if (schemaType != null) |
||||
|
{ |
||||
|
types[schemaId] = schemaType; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
foreach (var schemaType in schemaTypes) |
||||
|
{ |
||||
|
types[schemaType.Key] = schemaType.Value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
foreach (var type in types) |
||||
|
{ |
||||
|
AddPossibleType(type.Value); |
||||
|
} |
||||
|
|
||||
|
ResolveType = value => |
||||
|
{ |
||||
|
if (value is IContentEntity content) |
||||
|
{ |
||||
|
return types.GetOrDefault(content.SchemaId.Id); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; |
||||
|
|
||||
|
import { |
||||
|
AppLanguageDto, |
||||
|
ContentDto, |
||||
|
getContentValue, |
||||
|
RootFieldDto |
||||
|
} from '@app/shared'; |
||||
|
|
||||
|
/* tslint:disable:component-selector */ |
||||
|
|
||||
|
@Component({ |
||||
|
selector: '[sqxContentSelectorItem]', |
||||
|
template: ` |
||||
|
<tr (click)="toggle()"> |
||||
|
<td class="cell-select" sqxStopClick> |
||||
|
<input type="checkbox" class="form-check" |
||||
|
[disabled]="!selectable" |
||||
|
[ngModel]="selected || !selectable" |
||||
|
(ngModelChange)="emitSelectedChange($event)" /> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-user"> |
||||
|
<img class="user-picture" title="{{content.lastModifiedBy | sqxUserNameRef}}" [attr.src]="content.lastModifiedBy | sqxUserPictureRef" /> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-auto cell-content" *ngFor="let value of values"> |
||||
|
<sqx-content-value [value]="value"></sqx-content-value> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-time"> |
||||
|
<sqx-content-status |
||||
|
[status]="content.status" |
||||
|
[statusColor]="content.statusColor" |
||||
|
[scheduledTo]="content.scheduleJob?.status" |
||||
|
[scheduledAt]="content.scheduleJob?.dueTime" |
||||
|
[isPending]="content.isPending"> |
||||
|
</sqx-content-status> |
||||
|
|
||||
|
<small class="item-modified">{{content.lastModified | sqxFromNow}}</small> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr class="spacer"></tr> |
||||
|
`,
|
||||
|
changeDetection: ChangeDetectionStrategy.OnPush |
||||
|
}) |
||||
|
export class ContentSelectorItemComponent implements OnChanges { |
||||
|
@Output() |
||||
|
public selectedChange = new EventEmitter<boolean>(); |
||||
|
|
||||
|
@Input() |
||||
|
public selected = false; |
||||
|
|
||||
|
@Input() |
||||
|
public selectable = true; |
||||
|
|
||||
|
@Input() |
||||
|
public language: AppLanguageDto; |
||||
|
|
||||
|
@Input() |
||||
|
public fields: ReadonlyArray<RootFieldDto>; |
||||
|
|
||||
|
@Input('sqxContentSelectorItem') |
||||
|
public content: ContentDto; |
||||
|
|
||||
|
public values: ReadonlyArray<any> = []; |
||||
|
|
||||
|
public ngOnChanges(changes: SimpleChanges) { |
||||
|
if (changes['content'] || changes['language']) { |
||||
|
this.updateValues(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public toggle() { |
||||
|
if (this.selectable) { |
||||
|
this.emitSelectedChange(!this.selected); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public emitSelectedChange(isSelected: boolean) { |
||||
|
this.selectedChange.emit(isSelected); |
||||
|
} |
||||
|
|
||||
|
private updateValues() { |
||||
|
const values = []; |
||||
|
|
||||
|
for (const field of this.fields) { |
||||
|
const { formatted } = getContentValue(this.content, this.language, field); |
||||
|
|
||||
|
values.push(formatted); |
||||
|
} |
||||
|
|
||||
|
this.values = values; |
||||
|
} |
||||
|
} |
||||
@ -1,24 +1,2 @@ |
|||||
@import '_vars'; |
@import '_vars'; |
||||
@import '_mixins'; |
@import '_mixins'; |
||||
|
|
||||
.reference-edit { |
|
||||
& { |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
&:hover { |
|
||||
.reference-menu { |
|
||||
display: block; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.reference-menu { |
|
||||
@include absolute(0, -.25rem, auto, auto); |
|
||||
display: none; |
|
||||
padding-left: 2rem; |
|
||||
min-height: 2.4rem; |
|
||||
max-height: 2.4rem; |
|
||||
white-space: nowrap; |
|
||||
background: $color-table-background; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,28 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
.reference-edit { |
||||
|
& { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
.reference-menu { |
||||
|
display: block; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.reference-menu { |
||||
|
@include absolute(0, -.25rem, auto, auto); |
||||
|
display: none; |
||||
|
min-height: 2.4rem; |
||||
|
max-height: 2.4rem; |
||||
|
white-space: nowrap; |
||||
|
background: $color-table-background; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.badge { |
||||
|
@include truncate; |
||||
|
display: inline-block !important; |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; |
||||
|
|
||||
|
import { |
||||
|
AppLanguageDto, |
||||
|
ContentDto, |
||||
|
getContentValue |
||||
|
} from '@app/shared'; |
||||
|
|
||||
|
/* tslint:disable:component-selector */ |
||||
|
|
||||
|
@Component({ |
||||
|
selector: '[sqxReferenceItem]', |
||||
|
styleUrls: ['./reference-item.component.scss'], |
||||
|
template: ` |
||||
|
<tr> |
||||
|
<td class="cell-select"> |
||||
|
<i class="icon-drag2 drag-handle"></i> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-user" *ngIf="!isCompact"> |
||||
|
<img class="user-picture" title="{{content.lastModifiedBy | sqxUserNameRef}}" [attr.src]="content.lastModifiedBy | sqxUserPictureRef" /> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-auto cell-content" *ngFor="let value of values"> |
||||
|
<sqx-content-value [value]="value"></sqx-content-value> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-label" *ngIf="!isCompact"> |
||||
|
<span class="badge badge-pill truncate-inline badge-primary">{{content.schemaDisplayName}}</span> |
||||
|
</td> |
||||
|
|
||||
|
<td class="cell-actions"> |
||||
|
<div class="reference-edit"> |
||||
|
<button type="button" class="btn btn-text-secondary"> |
||||
|
<i class="icon-dots"></i> |
||||
|
</button> |
||||
|
|
||||
|
<div class="reference-menu"> |
||||
|
<a class="btn btn-text-secondary" [routerLink]="['../..', content.schemaName, content.id]"> |
||||
|
<i class="icon-pencil"></i> |
||||
|
</a> |
||||
|
|
||||
|
<button type="button" class="btn btn-text-secondary" (click)="emitDelete()"> |
||||
|
<i class="icon-close"></i> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr class="spacer"></tr> |
||||
|
`,
|
||||
|
changeDetection: ChangeDetectionStrategy.OnPush |
||||
|
}) |
||||
|
export class ReferenceItemComponent implements OnChanges { |
||||
|
@Output() |
||||
|
public delete = new EventEmitter(); |
||||
|
|
||||
|
@Input() |
||||
|
public language: AppLanguageDto; |
||||
|
|
||||
|
@Input() |
||||
|
public isCompact = false; |
||||
|
|
||||
|
@Input() |
||||
|
public columnCount = 0; |
||||
|
|
||||
|
@Input('sqxReferenceItem') |
||||
|
public content: ContentDto; |
||||
|
|
||||
|
public values: ReadonlyArray<any> = []; |
||||
|
|
||||
|
public ngOnChanges(changes: SimpleChanges) { |
||||
|
this.updateValues(); |
||||
|
} |
||||
|
|
||||
|
public emitDelete() { |
||||
|
this.delete.emit(); |
||||
|
} |
||||
|
|
||||
|
private updateValues() { |
||||
|
const values = []; |
||||
|
|
||||
|
for (let i = 0; i < this.columnCount; i++) { |
||||
|
const field = this.content.referenceFields[i]; |
||||
|
|
||||
|
if (field) { |
||||
|
const { formatted } = getContentValue(this.content, this.language, field); |
||||
|
|
||||
|
values.push(formatted); |
||||
|
} else { |
||||
|
values.push(''); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.values = values; |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue