Browse Source

Merge branch 'master' into feature/reference-enrichment

pull/388/head
Sebastian Stehle 7 years ago
parent
commit
260dab0485
  1. 2
      Squidex.sln
  2. 4
      src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs
  3. 2
      src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs
  4. 2
      src/Squidex.Infrastructure/Orleans/IBackgroundGrain.cs
  5. 7
      src/Squidex.Web/ApiExceptionFilterAttribute.cs
  6. 5
      src/Squidex/Config/Orleans/OrleansServices.cs
  7. 9
      src/Squidex/app/features/content/shared/due-time-selector.component.html
  8. 2
      src/Squidex/app/framework/angular/forms/autocomplete.component.html
  9. 67
      src/Squidex/app/framework/angular/forms/date-time-editor.component.ts
  10. 4
      src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html
  11. 2
      src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts
  12. 6
      src/Squidex/app/shared/components/markdown-editor.component.ts
  13. 14
      src/Squidex/app/shared/components/pipes.ts
  14. 8
      src/Squidex/app/shared/components/rich-editor.component.ts
  15. 4
      src/Squidex/app/shared/services/assets.service.ts
  16. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs

2
Squidex.sln

@ -361,7 +361,7 @@ Global
{6A811927-3C37-430A-90F4-503E37123956} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{D7166C56-178A-4457-B56A-C615C7450DEE} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{945871B1-77B8-43FB-B53C-27CF385AB756} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A}
{945871B1-77B8-43FB-B53C-27CF385AB756} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{B51126A8-0D75-4A79-867D-10724EC6AC84} = {94207AA6-4923-4183-A558-E0F8196B8CA3}
{5E75AB7D-6F01-4313-AFF1-7F7128FFD71F} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{F7771E22-47BD-45C4-A133-FD7F1DE27CA0} = {7EDE8CF1-B1E4-4005-B154-834B944E0D7A}

4
src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs

@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private static async Task<NamedId<Guid>> CreateBasicsSchemaAsync(Func<ICommand, Task> publish)
{
var command =
SchemaBuilder.Create("basics")
SchemaBuilder.Create("Basics")
.AddString("First Name", f => f
.Required()
.ShowInList()
@ -120,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
private static async Task<NamedId<Guid>> CreateProjectsSchemaAsync(Func<ICommand, Task> publish)
{
var schema =
SchemaBuilder.Create("projects")
SchemaBuilder.Create("Projects")
.AddString("Name", f => f
.Required()
.ShowInList()

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

@ -239,7 +239,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (command.Publish)
{
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published }));
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published }));
}
}

2
src/Squidex.Infrastructure/Orleans/IBackgroundGrain.cs

@ -7,11 +7,13 @@
using System.Threading.Tasks;
using Orleans;
using Orleans.Concurrency;
namespace Squidex.Infrastructure.Orleans
{
public interface IBackgroundGrain : IGrainWithStringKey
{
[OneWay]
Task ActivateAsync();
}
}

7
src/Squidex.Web/ApiExceptionFilterAttribute.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Squidex.Infrastructure;
@ -27,6 +28,7 @@ namespace Squidex.Web
static ApiExceptionFilterAttribute()
{
AddHandler<ValidationException>(OnValidationException);
AddHandler<DecoderFallbackException>(OnDecoderException);
AddHandler<DomainObjectNotFoundException>(OnDomainObjectNotFoundException);
AddHandler<DomainObjectVersionException>(OnDomainObjectVersionException);
AddHandler<DomainForbiddenException>(OnDomainForbiddenException);
@ -34,6 +36,11 @@ namespace Squidex.Web
AddHandler<SecurityException>(OnSecurityException);
}
private static IActionResult OnDecoderException(DecoderFallbackException ex)
{
return ErrorResult(400, new ErrorDto { Message = ex.Message });
}
private static IActionResult OnDomainObjectNotFoundException(DomainObjectNotFoundException ex)
{
return new NotFoundResult();

5
src/Squidex/Config/Orleans/OrleansServices.cs

@ -28,11 +28,6 @@ namespace Squidex.Config.Orleans
{
services.AddOrleans(config, environment, builder =>
{
builder.ConfigureLogging(logging =>
{
logging.AddFilters();
});
builder.ConfigureServices(siloServices =>
{
siloServices.AddSingleton<IIncomingGrainCallFilter, LocalCacheFilter>();

9
src/Squidex/app/features/content/shared/due-time-selector.component.html

@ -1,21 +1,21 @@
<ng-container *sqxModal="dueTimeDialog">
<ng-container *sqxModal="dueTimeDialog;closeAuto:false">
<sqx-modal-dialog (close)="cancelStatusChange()">
<ng-container title>
{{dueTimeAction}} content item(s)
Change content item(s) to {{dueTimeAction}}
</ng-container>
<ng-container content>
<div class="form-check">
<input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Immediately" id="immediately" name="dueTimeMode">
<label class="form-check-label" for="immediately">
{{dueTimeAction}} content item(s) immediately.
Set to {{dueTimeAction}} immediately.
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" [(ngModel)]="dueTimeMode" value="Scheduled" id="scheduled" name="dueTimeMode">
<label class="form-check-label" for="scheduled">
{{dueTimeAction}} content item(s) at a later point date and time.
Set to {{dueTimeAction}} at a later point date and time.
</label>
</div>
@ -27,5 +27,4 @@
<button type="button" class="float-right btn btn-primary" [disabled]="dueTimeMode === 'Scheduled' && !dueTime" (click)="confirmStatusChange()" sqxFocusOnInit>Confirm</button>
</ng-container>
</sqx-modal-dialog>
</ng-container>

2
src/Squidex/app/framework/angular/forms/autocomplete.component.html

@ -6,7 +6,7 @@
autocapitalize="off">
<ng-container *sqxModal="snapshot.suggestedItems.length > 0" position="bottom-left">
<div class="control-dropdown" [sqxAnchoredTo]="input" positition="bottom-left" #container @fade>
<div class="control-dropdown" [sqxAnchoredTo]="input" position="bottom-left" #container @fade>
<div *ngFor="let item of snapshot.suggestedItems; let i = index" class="control-dropdown-item control-dropdown-item-selectable"
[class.active]="i === snapshot.suggestedIndex"
[container]="container"

67
src/Squidex/app/framework/angular/forms/date-time-editor.component.ts

@ -28,8 +28,8 @@ export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
})
export class DateTimeEditorComponent extends StatefulControlComponent<{}, string | null> implements OnInit, AfterViewInit {
private picker: any;
private timeValue: any | null = null;
private dateValue: any | null = null;
private timeValue: moment.Moment | null = null;
private dateValue: moment.Moment | null = null;
private suppressEvents = false;
@Input()
@ -65,10 +65,10 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
if (!value || value.length === 0) {
this.timeValue = null;
} else {
this.timeValue = moment(value, 'HH:mm:ss');
this.timeValue = moment.utc(value, 'HH:mm:ss');
}
this.updateValue();
this.callChangeFormatted();
}));
this.own(
@ -76,10 +76,10 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
if (!value || value.length === 0) {
this.dateValue = null;
} else {
this.dateValue = moment(value, 'YYYY-MM-DD');
this.dateValue = moment.utc(value, 'YYYY-MM-DD');
}
this.updateValue();
this.callChangeFormatted();
}));
}
@ -87,10 +87,10 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
if (Types.isString(obj) && obj.length > 0) {
const parsed = moment.parseZone(obj);
this.dateValue = moment(parsed);
this.dateValue = parsed;
if (this.showTime) {
this.timeValue = moment(parsed);
this.timeValue = parsed;
}
} else {
this.timeValue = null;
@ -128,7 +128,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
}
this.dateValue = this.picker.getMoment();
this.updateValue();
this.callChangeFormatted();
this.callTouched();
}
});
@ -140,7 +140,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
this.writeValue(new Date().toUTCString());
this.updateControls();
this.updateValue();
this.callChangeFormatted();
this.callTouched();
return false;
@ -158,26 +158,30 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
return false;
}
private updateValue() {
let result: string | null;
private callChangeFormatted() {
this.callChange(this.getValue());
}
if ((this.dateValue && !this.dateValue.isValid()) || (this.timeValue && !this.timeValue.isValid())) {
result = null;
} else if (!this.dateValue && !this.timeValue) {
result = null;
} else {
result = this.dateValue.format('YYYY-MM-DD');
if (this.showTime && this.timeValue) {
result += 'T';
result += this.timeValue.format('HH:mm:ss');
result += 'Z';
} else if (this.enforceTime) {
result += 'T00:00:00Z';
}
private getValue(): string | null {
if (!this.dateValue || !this.dateValue.isValid()) {
return null;
}
if (this.timeValue && !this.timeValue.isValid()) {
return null;
}
this.callChange(result);
let result = this.dateValue.format('YYYY-MM-DD');
if (this.showTime && this.timeValue) {
result += 'T';
result += this.timeValue.format('HH:mm:ss');
result += 'Z';
} else if (this.enforceTime) {
result += 'T00:00:00Z';
}
return result;
}
private updateControls() {
@ -190,13 +194,16 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string
}
if (this.dateValue && this.dateValue.isValid() && this.picker) {
this.dateControl.setValue(this.dateValue.format('YYYY-MM-DD'), { emitEvent: false });
const dateString = this.dateValue.format('YYYY-MM-DD');
const dateLocal = moment(dateString);
this.dateControl.setValue(dateString, { emitEvent: false });
this.picker.setMoment(this.dateValue);
this.picker.setMoment(dateLocal);
} else {
this.dateControl.setValue(null, { emitEvent: false });
}
this.suppressEvents = false;
}
}
}

4
src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html

@ -1,6 +1,6 @@
<ng-container *sqxModal="tooltipModal;closeAuto:false">
<div class="onboarding-rect" [sqxAnchoredTo]="for" [offset]="4" position="full"></div>
<div class="onboarding-help" [sqxAnchoredTo]="for" [offset]="4" @fade>
<div class="onboarding-rect" [sqxAnchoredTo]="for" [offset]="4" [position]="'full'"></div>
<div class="onboarding-help" [sqxAnchoredTo]="for" [offset]="4" [position]="position" @fade>
<div class="onboarding-text">
<ng-content></ng-content>
</div>

2
src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts

@ -58,7 +58,7 @@ export class OnboardingTooltipComponent extends StatefulComponent implements OnD
if (this.for && this.helpId && Types.isFunction(this.for.addEventListener)) {
this.own(
timer(this.after).subscribe(() => {
if (true || this.onboardingService.shouldShow(this.helpId)) {
if (this.onboardingService.shouldShow(this.helpId)) {
const forRect = this.for.getBoundingClientRect();
const x = forRect.left + 0.5 * forRect.width;

6
src/Squidex/app/shared/components/markdown-editor.component.ts

@ -9,6 +9,7 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, E
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ApiUrlConfig,
AssetDto,
AssetUploaderState,
DialogModel,
@ -52,6 +53,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
public assetsDialog = new DialogModel();
constructor(changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig,
private readonly assetUploader: AssetUploaderState,
private readonly renderer: Renderer2,
private readonly resourceLoader: ResourceLoaderService
@ -205,7 +207,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
let content = '';
for (let asset of assets) {
content += `![${asset.fileName}](${asset.contentUrl} '${asset.fileName}')`;
content += `![${asset.fileName}](${asset.fullUrl(this.apiUrl)} '${asset.fileName}')`;
}
if (content.length > 0) {
@ -252,7 +254,7 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
this.assetUploader.uploadFile(file)
.subscribe(asset => {
if (Types.is(asset, AssetDto)) {
replaceText(`![${asset.fileName}](${asset.contentUrl} '${asset.fileName}')`);
replaceText(`![${asset.fileName}](${asset.fullUrl(this.apiUrl)} '${asset.fileName}')`);
}
}, error => {
if (!Types.is(error, UploadCanceled)) {

14
src/Squidex/app/shared/components/pipes.ts

@ -216,8 +216,13 @@ export class UserPictureRefPipe extends UserAsyncPipe implements PipeTransform {
pure: true
})
export class AssetUrlPipe implements PipeTransform {
constructor(
private readonly apiUrl: ApiUrlConfig
) {
}
public transform(asset: AssetDto): string {
return `${asset.contentUrl}&sq=${MathHelper.guid()}`;
return `${asset.fullUrl(this.apiUrl)}&sq=${MathHelper.guid()}`;
}
}
@ -226,8 +231,13 @@ export class AssetUrlPipe implements PipeTransform {
pure: true
})
export class AssetPreviewUrlPipe implements PipeTransform {
constructor(
private readonly apiUrl: ApiUrlConfig
) {
}
public transform(asset: AssetDto): string {
return asset.contentUrl;
return asset.fullUrl(this.apiUrl);
}
}

8
src/Squidex/app/shared/components/rich-editor.component.ts

@ -11,6 +11,7 @@ import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, E
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ApiUrlConfig,
AssetDto,
AssetUploaderState,
DialogModel,
@ -55,6 +56,7 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
public assetsDialog = new DialogModel();
constructor(changeDetector: ChangeDetectorRef,
private readonly apiUrl: ApiUrlConfig,
private readonly assetUploader: AssetUploaderState,
private readonly resourceLoader: ResourceLoaderService
) {
@ -102,7 +104,7 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
this.assetUploader.uploadFile(file)
.subscribe(asset => {
if (Types.is(asset, AssetDto)) {
success(asset.contentUrl);
success(asset.fullUrl(this.apiUrl));
}
}, error => {
if (!Types.is(error, UploadCanceled)) {
@ -190,7 +192,7 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
let content = '';
for (let asset of assets) {
content += `<img src="${asset.contentUrl}" alt="${asset.fileName}" />`;
content += `<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`;
}
if (content.length > 0) {
@ -220,7 +222,7 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
this.assetUploader.uploadFile(file)
.subscribe(asset => {
if (Types.is(asset, AssetDto)) {
replaceText(`<img src="${asset.contentUrl}" alt="${asset.fileName}" />`);
replaceText(`<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`);
}
}, error => {
if (!Types.is(error, UploadCanceled)) {

4
src/Squidex/app/shared/services/assets.service.ts

@ -80,6 +80,10 @@ export class AssetDto {
this._meta = meta;
}
public fullUrl(apiUrl: ApiUrlConfig) {
return apiUrl.buildUrl(this.contentUrl);
}
}
export interface AnnotateAssetDto {

2
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs

@ -153,7 +153,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
LastEvents
.ShouldHaveSameEvents(
CreateContentEvent(new ContentCreated { Data = data, Status = Status.Draft }),
CreateContentEvent(new ContentStatusChanged { Status = Status.Published })
CreateContentEvent(new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published })
);
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>"))

Loading…
Cancel
Save