Browse Source

Temp

pull/1/head
Sebastian 9 years ago
parent
commit
7cd8c6a747
  1. 27
      src/Squidex.Core/Schemas/DateTimeField.cs
  2. 4
      src/Squidex.Core/Schemas/DateTimeFieldEditor.cs
  3. 16
      src/Squidex.Core/Schemas/DateTimeFieldProperties.cs
  4. 5
      src/Squidex/app/features/settings/pages/clients/clients-page.component.html
  5. 15
      src/Squidex/app/framework/angular/date-time-editor.component.html
  6. 36
      src/Squidex/app/framework/angular/date-time-editor.component.scss
  7. 215
      src/Squidex/app/framework/angular/date-time-editor.component.ts
  8. 1
      src/Squidex/app/framework/declarations.ts
  9. 3
      src/Squidex/app/framework/module.ts
  10. 3
      src/Squidex/app/theme/vendor.scss
  11. 1
      src/Squidex/package.json
  12. 5
      tests/Squidex.Core.Tests/Schemas/DateTimeFieldPropertiesTests.cs
  13. 9
      tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs
  14. 8
      tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs

27
src/Squidex.Core/Schemas/DateTimeField.cs

@ -12,9 +12,13 @@ using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Library; using Microsoft.OData.Edm.Library;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NJsonSchema; using NJsonSchema;
using NodaTime;
using NodaTime.Text;
using Squidex.Core.Schemas.Validators; using Squidex.Core.Schemas.Validators;
using Squidex.Infrastructure; using Squidex.Infrastructure;
// ReSharper disable ConvertIfStatementToSwitchStatement
namespace Squidex.Core.Schemas namespace Squidex.Core.Schemas
{ {
[TypeName("DateTimeField")] [TypeName("DateTimeField")]
@ -34,13 +38,30 @@ namespace Squidex.Core.Schemas
if (Properties.MinValue.HasValue || Properties.MaxValue.HasValue) if (Properties.MinValue.HasValue || Properties.MaxValue.HasValue)
{ {
yield return new RangeValidator<DateTimeOffset>(Properties.MinValue, Properties.MaxValue); yield return new RangeValidator<Instant>(Properties.MinValue, Properties.MaxValue);
} }
} }
protected override object ConvertValue(JToken value) protected override object ConvertValue(JToken value)
{ {
return (DateTimeOffset?)value; if (value.Type == JTokenType.String)
{
var parseResult = InstantPattern.General.Parse(value.ToString());
if (!parseResult.Success)
{
throw parseResult.Exception;
}
return parseResult.Value;
}
if (value.Type == JTokenType.Null)
{
return null;
}
throw new InvalidCastException("Invalid json type, expected string.");
} }
protected override void PrepareJsonSchema(JsonProperty jsonProperty) protected override void PrepareJsonSchema(JsonProperty jsonProperty)
@ -51,7 +72,7 @@ namespace Squidex.Core.Schemas
protected override IEdmTypeReference CreateEdmType() protected override IEdmTypeReference CreateEdmType()
{ {
return EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.DateTimeOffset, !Properties.IsRequired); return EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Date, !Properties.IsRequired);
} }
} }
} }

4
src/Squidex.Core/Schemas/DateTimeFieldEditor.cs

@ -11,8 +11,6 @@ namespace Squidex.Core.Schemas
public enum DateTimeFieldEditor public enum DateTimeFieldEditor
{ {
Date, Date,
DateWithTimezone, DateTime
DateTime,
DateTimeWithTimezone
} }
} }

16
src/Squidex.Core/Schemas/DateTimeFieldProperties.cs

@ -6,9 +6,9 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NodaTime;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Core.Schemas namespace Squidex.Core.Schemas
@ -17,11 +17,11 @@ namespace Squidex.Core.Schemas
public sealed class DateTimeFieldProperties : FieldProperties public sealed class DateTimeFieldProperties : FieldProperties
{ {
private DateTimeFieldEditor editor; private DateTimeFieldEditor editor;
private DateTimeOffset? maxValue; private Instant? maxValue;
private DateTimeOffset? minValue; private Instant? minValue;
private DateTimeOffset? defaultValue; private Instant? defaultValue;
public DateTimeOffset? MaxValue public Instant? MaxValue
{ {
get { return maxValue; } get { return maxValue; }
set set
@ -32,7 +32,7 @@ namespace Squidex.Core.Schemas
} }
} }
public DateTimeOffset? MinValue public Instant? MinValue
{ {
get { return minValue; } get { return minValue; }
set set
@ -43,7 +43,7 @@ namespace Squidex.Core.Schemas
} }
} }
public DateTimeOffset? DefaultValue public Instant? DefaultValue
{ {
get { return defaultValue; } get { return defaultValue; }
set set
@ -67,7 +67,7 @@ namespace Squidex.Core.Schemas
public override JToken GetDefaultValue() public override JToken GetDefaultValue()
{ {
return DefaultValue; return DefaultValue != null ? DefaultValue.ToString() : null;
} }
protected override IEnumerable<ValidationError> ValidateCore() protected override IEnumerable<ValidationError> ValidateCore()

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

@ -13,6 +13,11 @@
<div class="panel-main"> <div class="panel-main">
<div class="panel-content panel-content-scroll"> <div class="panel-content panel-content-scroll">
<div><sqx-date-time-editor mode="Date" ngModel="2013-01-01T00:00:00-13:00"></sqx-date-time-editor></div>
<div><sqx-date-time-editor mode="DateWithTimezone" ngModel="2013-01-01T00:00:00-13:00"></sqx-date-time-editor></div>
<div><sqx-date-time-editor mode="DateTime" ngModel="2013-01-01T00:00:00-13:00"></sqx-date-time-editor></div>
<div><sqx-date-time-editor mode="DateTimeWithTimezone" ngModel="2013-01-01T00:00:00-13:00"></sqx-date-time-editor></div>
<div class="table-items-row" *ngIf="appClients && appClients.length === 0"> <div class="table-items-row" *ngIf="appClients && appClients.length === 0">
No client created yet. No client created yet.
</div> </div>

15
src/Squidex/app/framework/angular/date-time-editor.component.html

@ -0,0 +1,15 @@
<div>
<div class="form-inline">
<div class="form-group date-group">
<input type="text" class="form-control" #dateInput [disabled]="isDisabled" readonly />
</div>
<div class="form-group time-group" *ngIf="showTime">
<input type="text" class="form-control" [formControl]="timeControl" (blur)="touched()" />
</div>
<div class="form-group timezone-group" *ngIf="showTimezone">
<select class="form-control" [formControl]="timeZoneControl">
<option *ngFor="let timezone of timezones" [ngValue]="timezone.value">{{timezone.label}}</option>
</select>
</div>
</div>
</div>

36
src/Squidex/app/framework/angular/date-time-editor.component.scss

@ -0,0 +1,36 @@
@import '_mixins';
@import '_vars';
$form-color: #fff;
.form-control {
&[readonly] {
background: $form-color;
}
}
.date-group {
& {
padding-right: .5rem;
}
.form-control {
width: 8rem;
}
}
.time-group {
& {
padding-right: .5rem;
}
.form-control {
width: 5rem;
}
}
.timezone-group {
.form-control {
width: 8.5rem;
}
}

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

@ -0,0 +1,215 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { AfterViewInit, Component, forwardRef, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import * as moment from 'moment';
let Pikaday = require('pikaday/pikaday');
/* tslint:disable:no-empty */
const NOOP = () => { };
export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateTimeEditorComponent),
multi: true
};
const TIMEZONES: any[] = [
{ label: 'UTC-13:00', value: -780 },
{ label: 'UTC-12:00', value: -720 },
{ label: 'UTC-11:00', value: -660 },
{ label: 'UTC-10:00', value: -600 },
{ label: 'UTC-09:30', value: -570 },
{ label: 'UTC-09:00', value: -540 },
{ label: 'UTC-08:00', value: -480 },
{ label: 'UTC-07:00', value: -420 },
{ label: 'UTC-06:00', value: -360 },
{ label: 'UTC-05:00', value: -300 },
{ label: 'UTC-04:30', value: -270 },
{ label: 'UTC-04:00', value: -240 },
{ label: 'UTC-03:30', value: -210 },
{ label: 'UTC-03:00', value: -180 },
{ label: 'UTC-02:00', value: -120 },
{ label: 'UTC-01:00', value: -60 },
{ label: 'UTC+00:00', value: 0 },
{ label: 'UTC+01:00', value: 60 },
{ label: 'UTC+02:00', value: 120 },
{ label: 'UTC+03:00', value: 180 },
{ label: 'UTC+03:30', value: 210 },
{ label: 'UTC+04:00', value: 240 },
{ label: 'UTC+04:30', value: 270 },
{ label: 'UTC+05:00', value: 300 },
{ label: 'UTC+05:30', value: 330 },
{ label: 'UTC+05:45', value: 345 },
{ label: 'UTC+06:00', value: 360 },
{ label: 'UTC+06:30', value: 390 },
{ label: 'UTC+07:00', value: 420 },
{ label: 'UTC+08:00', value: 480 },
{ label: 'UTC+08:45', value: 425 },
{ label: 'UTC+09:00', value: 540 },
{ label: 'UTC+09:30', value: 570 },
{ label: 'UTC+10:00', value: 600 },
{ label: 'UTC+10:30', value: 630 },
{ label: 'UTC+11:00', value: 660 },
{ label: 'UTC+11:30', value: 690 },
{ label: 'UTC+12:00', value: 720 },
{ label: 'UTC+12:45', value: 765 },
{ label: 'UTC+13:00', value: 780 },
{ label: 'UTC+14:00', value: 840 }
];
@Component({
selector: 'sqx-date-time-editor',
styleUrls: ['./date-time-editor.component.scss'],
templateUrl: './date-time-editor.component.html',
providers: [SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR]
})
export class DateTimeEditorComponent implements ControlValueAccessor, OnInit, AfterViewInit {
private picker: any;
private time: any;
private date: any;
private offset: number;
private suppressEvents = false;
private changeCallback: (value: any) => void = NOOP;
private touchedCallback: () => void = NOOP;
public get showTime() {
return this.mode === 'DateTime' || this.mode === 'DateTimeWithTimezone';
}
public get showTimezone() {
return this.mode === 'DateWithTimezone' || this.mode === 'DateTimeWithTimezone';
}
public timezones = TIMEZONES;
public timeControl =
new FormControl();
public timeZoneControl =
new FormControl();
public isDisabled = false;
@Input()
public mode: string;
@ViewChild('dateInput')
public dateInput: ElementRef;
public ngOnInit() {
this.timeControl.valueChanges.subscribe(value => {
const time = moment(value, 'HH:mm:ss');
this.time = moment();
this.time.hours(time.hours()).minutes(time.minutes()).seconds(time.seconds());
this.updateValue();
});
this.timeZoneControl.valueChanges.subscribe(value => {
this.offset = value;
this.updateValue();
this.touched();
});
}
public writeValue(value: any) {
const parsed = (moment.parseZone(value) || moment());
this.time = moment(parsed);
this.date = moment(parsed);
this.offset = parsed.utcOffset();
this.updateControls();
}
public setDisabledState(isDisabled: boolean): void {
this.isDisabled = isDisabled;
if (isDisabled) {
this.timeControl.disable();
this.timeZoneControl.disable();
} else {
this.timeControl.enable();
this.timeZoneControl.enable();
}
}
public registerOnChange(fn: any) {
this.changeCallback = fn;
}
public registerOnTouched(fn: any) {
this.touchedCallback = fn;
}
public ngAfterViewInit() {
this.picker = new Pikaday({
field: this.dateInput.nativeElement,
format: 'YYYY-MM-DD',
onSelect: () => {
if (this.suppressEvents) {
return;
}
const date = this.picker.getMoment();
this.date.years(date.years()).months(date.months()).dates(date.dates());
this.updateValue();
this.touched();
}
});
this.updateControls();
}
public touched() {
this.touchedCallback();
}
private updateValue() {
let result = this.date.format('YYYY-MM-DD');
if (this.showTime) {
result += 'T';
result += this.time.format('HH:mm:ss');
}
if (this.showTimezone) {
result += moment().utcOffset(this.offset).format('Z');
} else if (this.showTime) {
result += 'Z';
}
this.changeCallback(result);
}
private updateControls() {
if (!this.date) {
return;
}
this.suppressEvents = true;
this.timeControl.setValue(this.time.format('HH:mm'), { emitEvent: false });
this.timeZoneControl.setValue(this.offset, { emitEvent: false });
if (this.picker) {
this.picker.setMoment(this.date);
}
this.suppressEvents = false;
}
}

1
src/Squidex/app/framework/declarations.ts

@ -11,6 +11,7 @@ export * from './angular/validators';
export * from './angular/cloak.directive'; export * from './angular/cloak.directive';
export * from './angular/control-errors.component'; export * from './angular/control-errors.component';
export * from './angular/copy.directive'; export * from './angular/copy.directive';
export * from './angular/date-time-editor.component';
export * from './angular/date-time.pipes'; export * from './angular/date-time.pipes';
export * from './angular/focus-on-change.directive'; export * from './angular/focus-on-change.directive';
export * from './angular/focus-on-init.directive'; export * from './angular/focus-on-init.directive';

3
src/Squidex/app/framework/module.ts

@ -17,6 +17,7 @@ import {
CloakDirective, CloakDirective,
ControlErrorsComponent, ControlErrorsComponent,
CopyDirective, CopyDirective,
DateTimeEditorComponent,
DayOfWeekPipe, DayOfWeekPipe,
DayPipe, DayPipe,
DisplayNamePipe, DisplayNamePipe,
@ -58,6 +59,7 @@ import {
CloakDirective, CloakDirective,
ControlErrorsComponent, ControlErrorsComponent,
CopyDirective, CopyDirective,
DateTimeEditorComponent,
DayOfWeekPipe, DayOfWeekPipe,
DayPipe, DayPipe,
DisplayNamePipe, DisplayNamePipe,
@ -84,6 +86,7 @@ import {
CloakDirective, CloakDirective,
ControlErrorsComponent, ControlErrorsComponent,
CopyDirective, CopyDirective,
DateTimeEditorComponent,
DayOfWeekPipe, DayOfWeekPipe,
DayPipe, DayPipe,
DisplayNamePipe, DisplayNamePipe,

3
src/Squidex/app/theme/vendor.scss

@ -3,6 +3,9 @@
// Bootstrap // Bootstrap
@import './../../node_modules/bootstrap/scss/bootstrap.scss'; @import './../../node_modules/bootstrap/scss/bootstrap.scss';
// Pikaday
@import './../../node_modules/pikaday/css/pikaday.css';
// Bootstrap Overrides // Bootstrap Overrides
@import '_bootstrap.scss'; @import '_bootstrap.scss';

1
src/Squidex/package.json

@ -28,6 +28,7 @@
"moment": "^2.17.1", "moment": "^2.17.1",
"mousetrap": "^1.6.0", "mousetrap": "^1.6.0",
"oidc-client": "^1.2.2", "oidc-client": "^1.2.2",
"pikaday": "^1.5.1",
"rxjs": "5.0.3", "rxjs": "5.0.3",
"zone.js": "^0.7.6" "zone.js": "^0.7.6"
}, },

5
tests/Squidex.Core.Tests/Schemas/DateTimeFieldPropertiesTests.cs

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using FluentAssertions; using FluentAssertions;
using NodaTime;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Xunit; using Xunit;
@ -133,9 +134,9 @@ namespace Squidex.Core.Schemas
} }
} }
private static DateTimeOffset FutureDays(int days) private static Instant FutureDays(int days)
{ {
return DateTimeOffset.UtcNow.AddDays(days); return SystemClock.Instance.GetCurrentInstant().Plus(Duration.FromDays(days));
} }
} }
} }

9
tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NodaTime;
using Xunit; using Xunit;
namespace Squidex.Core.Schemas namespace Squidex.Core.Schemas
@ -64,7 +65,7 @@ namespace Squidex.Core.Schemas
await sut.ValidateAsync(CreateValue(FutureDays(0)), errors); await sut.ValidateAsync(CreateValue(FutureDays(0)), errors);
errors.ShouldBeEquivalentTo( errors.ShouldBeEquivalentTo(
new[] { $"My-DateTime must be greater than '{DateTimeOffset.UtcNow.AddDays(10)}'" }); new[] { $"My-DateTime must be greater than '{FutureDays(10)}'" });
} }
[Fact] [Fact]
@ -89,14 +90,14 @@ namespace Squidex.Core.Schemas
new[] { "My-DateTime is not a valid value" }); new[] { "My-DateTime is not a valid value" });
} }
private static DateTimeOffset FutureDays(int days) private static Instant FutureDays(int days)
{ {
return DateTimeOffset.UtcNow.AddDays(days); return SystemClock.Instance.GetCurrentInstant().Plus(Duration.FromDays(days));
} }
private static JValue CreateValue(object v) private static JValue CreateValue(object v)
{ {
return new JValue(v); return v is Instant ? new JValue(v.ToString()) : new JValue(v);
} }
} }
} }

8
tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs

@ -10,6 +10,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using NodaTime;
using NodaTime.Text;
using Squidex.Core.Contents; using Squidex.Core.Contents;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Xunit; using Xunit;
@ -288,6 +290,8 @@ namespace Squidex.Core.Schemas
[Fact] [Fact]
private void Should_enrich_with_default_values() private void Should_enrich_with_default_values()
{ {
var now = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds());
var schema = var schema =
Schema.Create("my-schema", new SchemaProperties()) Schema.Create("my-schema", new SchemaProperties())
.AddOrUpdateField( .AddOrUpdateField(
@ -297,7 +301,7 @@ namespace Squidex.Core.Schemas
.AddOrUpdateField( .AddOrUpdateField(
new NumberField(3, "my-number", new NumberFieldProperties { DefaultValue = 123 })) new NumberField(3, "my-number", new NumberFieldProperties { DefaultValue = 123 }))
.AddOrUpdateField( .AddOrUpdateField(
new DateTimeField(4, "my-datetime", new DateTimeFieldProperties { DefaultValue = DateTime.Today })); new DateTimeField(4, "my-datetime", new DateTimeFieldProperties { DefaultValue = now }));
var data = var data =
new ContentData() new ContentData()
@ -315,7 +319,7 @@ namespace Squidex.Core.Schemas
Assert.Equal("DE-String", (string)data["my-string"]["de"]); Assert.Equal("DE-String", (string)data["my-string"]["de"]);
Assert.Equal("EN-String", (string)data["my-string"]["en"]); Assert.Equal("EN-String", (string)data["my-string"]["en"]);
Assert.Equal(DateTime.Today, (DateTime)data["my-datetime"]["iv"]); Assert.Equal(now, InstantPattern.General.Parse((string)data["my-datetime"]["iv"]).Value);
Assert.Equal(true, (bool)data["my-boolean"]["iv"]); Assert.Equal(true, (bool)data["my-boolean"]["iv"]);
} }

Loading…
Cancel
Save