From 3978642a6a9b5dfcda871f797c7bcf02a92d55ab Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 21 Feb 2017 23:32:00 +0100 Subject: [PATCH] 1) DateTime => Instant 2) Bugfix: Language selector not visible 3) Bugfix: Instant conversion --- .../Schemas/BooleanFieldEditor.cs | 3 +- src/Squidex.Core/Schemas/DateTimeField.cs | 11 +- .../EventStore/MongoEventCommit.cs | 3 +- .../EventStore/MongoEventStore.cs | 9 +- .../InstantSerializer.cs | 54 ++++++ .../MongoEntity.cs | 5 +- .../MongoRepositoryBase.cs | 1 + .../Commands/EnrichWithTimestampHandler.cs | 16 +- .../CQRS/Commands/ITimestampCommand.cs | 4 +- .../Json/InstantConverter.cs | 55 ++++++ src/Squidex.Infrastructure/Languages.cs | 2 +- .../Reflection/SimpleMapper.cs | 3 +- .../Apps/MongoAppClientEntity.cs | 5 - .../History/ParsedHistoryEvent.cs | 5 +- .../Users/MongoUserEntity.cs | 1 + .../Utils/EntityMapper.cs | 4 +- src/Squidex.Read/Apps/IAppClientEntity.cs | 4 - src/Squidex.Read/IEntity.cs | 5 +- .../Apps/Commands/AttachClient.cs | 6 +- src/Squidex/Config/Domain/EventStoreModule.cs | 3 +- .../Config/Domain/InfrastructureModule.cs | 5 + src/Squidex/Config/Domain/Serializers.cs | 9 +- .../Config/Identity/LazyClientStore.cs | 2 +- src/Squidex/Config/Swagger/SwaggerServices.cs | 6 + .../Controllers/Api/Apps/Models/AppDto.cs | 5 +- .../Api/History/Models/HistoryEventDto.cs | 3 +- .../Models/DateTimeFieldPropertiesDto.cs | 8 +- .../Api/Schemas/Models/SchemaDetailsDto.cs | 5 +- .../Api/Schemas/Models/SchemaDto.cs | 5 +- .../ContentApi/Models/ContentDto.cs | 5 +- src/Squidex/Squidex.csproj | 1 + .../content/content-field.component.html | 4 +- .../app/features/schemas/declarations.ts | 2 + src/Squidex/app/features/schemas/module.ts | 4 + .../schemas/pages/schema/field.component.html | 6 + .../schemas/pages/schema/field.component.scss | 2 +- .../pages/schema/schema-page.component.ts | 5 +- .../schema/types/boolean-ui.component.html | 9 - .../types/boolean-validation.component.scss | 10 -- .../schema/types/date-time-ui.component.html | 33 ++++ .../schema/types/date-time-ui.component.scss | 2 + .../schema/types/date-time-ui.component.ts | 40 +++++ .../types/date-time-validation.component.html | 33 ++++ .../types/date-time-validation.component.scss | 6 + .../types/date-time-validation.component.ts | 49 +++++ .../schema/types/number-ui.component.html | 2 - .../types/number-validation.component.scss | 2 +- .../schema/types/string-ui.component.html | 2 - .../types/string-validation.component.scss | 2 +- .../pages/clients/clients-page.component.html | 3 +- .../angular/control-errors.component.ts | 1 + .../angular/date-time-editor.component.html | 7 +- .../angular/date-time-editor.component.scss | 31 ++-- .../angular/date-time-editor.component.ts | 156 ++++++---------- .../app/framework/angular/validators.ts | 18 ++ src/Squidex/app/framework/utils/date-time.ts | 1 - .../components/language-selector.component.ts | 2 +- .../app/shared/services/schemas.service.ts | 22 +++ .../app/theme/icomoon/demo-files/demo.css | 5 +- src/Squidex/app/theme/icomoon/demo.html | 169 ++++++++++++------ .../app/theme/icomoon/fonts/icomoon.eot | Bin 13040 -> 13476 bytes .../app/theme/icomoon/fonts/icomoon.svg | 3 +- .../app/theme/icomoon/fonts/icomoon.ttf | Bin 12876 -> 13312 bytes .../app/theme/icomoon/fonts/icomoon.woff | Bin 12952 -> 13388 bytes src/Squidex/app/theme/icomoon/selection.json | 141 +++++++++------ src/Squidex/app/theme/icomoon/style.css | 19 +- .../Schemas/DateTimeFieldTests.cs | 1 - .../Squidex.Core.Tests/Schemas/SchemaTests.cs | 24 +-- .../Schemas/SchemaValidationTests.cs | 5 +- .../EnrichWithTimestampHandlerTests.cs | 32 ++-- .../Reflection/SimpleMapperTests.cs | 9 +- .../Apps/AppCommandHandlerTests.cs | 4 +- .../Apps/AppDomainObjectTests.cs | 4 +- tools/GenerateLanguages/Program.cs | 2 +- 74 files changed, 747 insertions(+), 378 deletions(-) create mode 100644 src/Squidex.Infrastructure.MongoDb/InstantSerializer.cs create mode 100644 src/Squidex.Infrastructure/Json/InstantConverter.cs create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.scss create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.html create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.scss create mode 100644 src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts diff --git a/src/Squidex.Core/Schemas/BooleanFieldEditor.cs b/src/Squidex.Core/Schemas/BooleanFieldEditor.cs index fd6d7fe5f..15105e5c7 100644 --- a/src/Squidex.Core/Schemas/BooleanFieldEditor.cs +++ b/src/Squidex.Core/Schemas/BooleanFieldEditor.cs @@ -10,7 +10,6 @@ namespace Squidex.Core.Schemas { public enum BooleanFieldEditor { - Checkbox, - Toggle + Checkbox } } diff --git a/src/Squidex.Core/Schemas/DateTimeField.cs b/src/Squidex.Core/Schemas/DateTimeField.cs index 1ba98ff46..aaeb51cd5 100644 --- a/src/Squidex.Core/Schemas/DateTimeField.cs +++ b/src/Squidex.Core/Schemas/DateTimeField.cs @@ -17,6 +17,7 @@ using NodaTime.Text; using Squidex.Core.Schemas.Validators; using Squidex.Infrastructure; +// ReSharper disable ConvertIfStatementToConditionalTernaryExpression // ReSharper disable ConvertIfStatementToSwitchStatement namespace Squidex.Core.Schemas @@ -67,7 +68,15 @@ namespace Squidex.Core.Schemas protected override void PrepareJsonSchema(JsonProperty jsonProperty) { jsonProperty.Type = JsonObjectType.String; - jsonProperty.Format = "date-time"; + + if (Properties.Editor == DateTimeFieldEditor.Date) + { + jsonProperty.Format = JsonFormatStrings.Date; + } + else + { + jsonProperty.Format = JsonFormatStrings.DateTime; + } } protected override IEdmTypeReference CreateEdmType() diff --git a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventCommit.cs b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventCommit.cs index c61935136..7c4cabf31 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventCommit.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventCommit.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using NodaTime; namespace Squidex.Infrastructure.MongoDb.EventStore { @@ -22,7 +23,7 @@ namespace Squidex.Infrastructure.MongoDb.EventStore [BsonRequired] [BsonElement] - public DateTime Timestamp { get; set; } + public Instant Timestamp { get; set; } [BsonElement] [BsonRequired] diff --git a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs index c03a63d0c..d51cfc9c7 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs @@ -13,6 +13,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver; +using NodaTime; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Reflection; @@ -26,13 +27,17 @@ namespace Squidex.Infrastructure.MongoDb.EventStore { private const int Retries = 500; private readonly IEventNotifier notifier; + private readonly IClock clock; private string eventsOffsetIndex; - public MongoEventStore(IMongoDatabase database, IEventNotifier notifier) + public MongoEventStore(IMongoDatabase database, IEventNotifier notifier, IClock clock) : base(database) { + Guard.NotNull(clock, nameof(clock)); Guard.NotNull(notifier, nameof(notifier)); + this.clock = clock; + this.notifier = notifier; } @@ -115,7 +120,7 @@ namespace Squidex.Infrastructure.MongoDb.EventStore throw new WrongEventVersionException(currentVersion, expectedVersion); } - var now = DateTime.UtcNow; + var now = clock.GetCurrentInstant(); var commitEvents = events.Select(x => SimpleMapper.Map(x, new MongoEvent())).ToList(); diff --git a/src/Squidex.Infrastructure.MongoDb/InstantSerializer.cs b/src/Squidex.Infrastructure.MongoDb/InstantSerializer.cs new file mode 100644 index 000000000..15360bc6c --- /dev/null +++ b/src/Squidex.Infrastructure.MongoDb/InstantSerializer.cs @@ -0,0 +1,54 @@ +// ========================================================================== +// InstantSerializer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using NodaTime; +using NodaTime.Text; + +// ReSharper disable InvertIf + +namespace Squidex.Infrastructure.MongoDb +{ + public sealed class InstantSerializer : SerializerBase + { + private static bool isRegistered; + private static readonly object LockObject = new object(); + + public static bool Register() + { + if (!isRegistered) + { + lock (LockObject) + { + if (!isRegistered) + { + BsonSerializer.RegisterSerializer(new InstantSerializer()); + + isRegistered = true; + return true; + } + } + } + + return false; + } + + public override Instant Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var value = context.Reader.ReadDateTime(); + + return Instant.FromUnixTimeMilliseconds(value); + } + + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Instant value) + { + context.Writer.WriteDateTime(value.ToUnixTimeMilliseconds()); + } + } +} diff --git a/src/Squidex.Infrastructure.MongoDb/MongoEntity.cs b/src/Squidex.Infrastructure.MongoDb/MongoEntity.cs index 52274d458..a480b310b 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoEntity.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoEntity.cs @@ -9,6 +9,7 @@ using System; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; +using NodaTime; namespace Squidex.Infrastructure.MongoDb { @@ -21,10 +22,10 @@ namespace Squidex.Infrastructure.MongoDb [BsonRequired] [BsonElement] - public DateTime Created { get; set; } + public Instant Created { get; set; } [BsonRequired] [BsonElement] - public DateTime LastModified { get; set; } + public Instant LastModified { get; set; } } } diff --git a/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs b/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs index 1419c3bd1..481e55b2f 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs @@ -88,6 +88,7 @@ namespace Squidex.Infrastructure.MongoDb static MongoRepositoryBase() { RefTokenSerializer.Register(); + InstantSerializer.Register(); } protected MongoRepositoryBase(IMongoDatabase database) diff --git a/src/Squidex.Infrastructure/CQRS/Commands/EnrichWithTimestampHandler.cs b/src/Squidex.Infrastructure/CQRS/Commands/EnrichWithTimestampHandler.cs index 58a2131cf..0c65d53f6 100644 --- a/src/Squidex.Infrastructure/CQRS/Commands/EnrichWithTimestampHandler.cs +++ b/src/Squidex.Infrastructure/CQRS/Commands/EnrichWithTimestampHandler.cs @@ -8,23 +8,19 @@ using System; using System.Threading.Tasks; +using NodaTime; namespace Squidex.Infrastructure.CQRS.Commands { public sealed class EnrichWithTimestampHandler : ICommandHandler { - private readonly Func timestamp; + private readonly IClock clock; - public EnrichWithTimestampHandler() - : this(() => DateTime.UtcNow) + public EnrichWithTimestampHandler(IClock clock) { - } - - public EnrichWithTimestampHandler(Func timestamp) - { - Guard.NotNull(timestamp, nameof(timestamp)); + Guard.NotNull(clock, nameof(clock)); - this.timestamp = timestamp; + this.clock = clock; } public Task HandleAsync(CommandContext context) @@ -33,7 +29,7 @@ namespace Squidex.Infrastructure.CQRS.Commands if (timestampCommand != null) { - timestampCommand.Timestamp = timestamp(); + timestampCommand.Timestamp = clock.GetCurrentInstant(); } return Task.FromResult(false); diff --git a/src/Squidex.Infrastructure/CQRS/Commands/ITimestampCommand.cs b/src/Squidex.Infrastructure/CQRS/Commands/ITimestampCommand.cs index f241c5878..9dfb0a3cf 100644 --- a/src/Squidex.Infrastructure/CQRS/Commands/ITimestampCommand.cs +++ b/src/Squidex.Infrastructure/CQRS/Commands/ITimestampCommand.cs @@ -6,12 +6,12 @@ // All rights reserved. // ========================================================================== -using System; +using NodaTime; namespace Squidex.Infrastructure.CQRS.Commands { public interface ITimestampCommand : ICommand { - DateTime Timestamp { get; set; } + Instant Timestamp { get; set; } } } diff --git a/src/Squidex.Infrastructure/Json/InstantConverter.cs b/src/Squidex.Infrastructure/Json/InstantConverter.cs new file mode 100644 index 000000000..7e81a81fc --- /dev/null +++ b/src/Squidex.Infrastructure/Json/InstantConverter.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// InstantConverter.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Newtonsoft.Json; +using NodaTime; +using NodaTime.Text; + +// ReSharper disable ConvertIfStatementToSwitchStatement + +namespace Squidex.Infrastructure.Json +{ + public sealed class InstantConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value != null) + { + writer.WriteValue(value.ToString()); + } + else + { + writer.WriteNull(); + } + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.String) + { + return InstantPattern.General.Parse(reader.Value.ToString()).Value; + } + if (reader.TokenType == JsonToken.Date) + { + return Instant.FromDateTimeUtc((DateTime)reader.Value); + } + if (reader.TokenType == JsonToken.Null && objectType == typeof(Instant?)) + { + return null; + } + + throw new JsonException($"Not a valid date time, expected String or Date, but got {reader.TokenType}."); + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Instant) || objectType == typeof(Instant?); + } + } +} diff --git a/src/Squidex.Infrastructure/Languages.cs b/src/Squidex.Infrastructure/Languages.cs index 5033d33d7..85dbbfdf2 100644 --- a/src/Squidex.Infrastructure/Languages.cs +++ b/src/Squidex.Infrastructure/Languages.cs @@ -1,5 +1,5 @@ // ========================================================================== -// Langauges.cs +// Languages.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group diff --git a/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs b/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs index 2ba1fe2cc..6dd378c10 100644 --- a/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs +++ b/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs @@ -101,8 +101,7 @@ namespace Squidex.Infrastructure.Reflection typeof(short), typeof(int), typeof(long), - typeof(string), - typeof(DateTime) + typeof(string) }; static ClassMapper() diff --git a/src/Squidex.Read.MongoDb/Apps/MongoAppClientEntity.cs b/src/Squidex.Read.MongoDb/Apps/MongoAppClientEntity.cs index fb0ce7500..be38a14f5 100644 --- a/src/Squidex.Read.MongoDb/Apps/MongoAppClientEntity.cs +++ b/src/Squidex.Read.MongoDb/Apps/MongoAppClientEntity.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using System; using MongoDB.Bson.Serialization.Attributes; using Squidex.Read.Apps; @@ -22,10 +21,6 @@ namespace Squidex.Read.MongoDb.Apps [BsonElement] public string Secret { get; set; } - [BsonRequired] - [BsonElement] - public DateTime ExpiresUtc { get; set; } - [BsonRequired] [BsonElement] public string Name { get; set; } diff --git a/src/Squidex.Read.MongoDb/History/ParsedHistoryEvent.cs b/src/Squidex.Read.MongoDb/History/ParsedHistoryEvent.cs index df6483fab..40d616eb2 100644 --- a/src/Squidex.Read.MongoDb/History/ParsedHistoryEvent.cs +++ b/src/Squidex.Read.MongoDb/History/ParsedHistoryEvent.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using NodaTime; using Squidex.Infrastructure; using Squidex.Read.History; @@ -35,12 +36,12 @@ namespace Squidex.Read.MongoDb.History get { return inner.Actor; } } - public DateTime Created + public Instant Created { get { return inner.Created; } } - public DateTime LastModified + public Instant LastModified { get { return inner.LastModified; } } diff --git a/src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs b/src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs index 7a2d3c9b1..f5f7336fb 100644 --- a/src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs +++ b/src/Squidex.Read.MongoDb/Users/MongoUserEntity.cs @@ -31,6 +31,7 @@ namespace Squidex.Read.MongoDb.Users { get { return inner.Claims.Find(x => x.Type == SquidexClaimTypes.SquidexDisplayName)?.Value; } } + public string PictureUrl { get { return inner.Claims.Find(x => x.Type == SquidexClaimTypes.SquidexPictureUrl)?.Value; } diff --git a/src/Squidex.Read.MongoDb/Utils/EntityMapper.cs b/src/Squidex.Read.MongoDb/Utils/EntityMapper.cs index ff3044909..0e628b70e 100644 --- a/src/Squidex.Read.MongoDb/Utils/EntityMapper.cs +++ b/src/Squidex.Read.MongoDb/Utils/EntityMapper.cs @@ -46,12 +46,12 @@ namespace Squidex.Read.MongoDb.Utils private static void SetCreated(EnvelopeHeaders headers, MongoEntity entity) { - entity.Created = headers.Timestamp().ToDateTimeUtc(); + entity.Created = headers.Timestamp(); } private static void SetLastModified(EnvelopeHeaders headers, MongoEntity entity) { - entity.LastModified = headers.Timestamp().ToDateTimeUtc(); + entity.LastModified = headers.Timestamp(); } private static void SetCreatedBy(SquidexEvent @event, MongoEntity entity) diff --git a/src/Squidex.Read/Apps/IAppClientEntity.cs b/src/Squidex.Read/Apps/IAppClientEntity.cs index b009b11fb..e996dc85e 100644 --- a/src/Squidex.Read/Apps/IAppClientEntity.cs +++ b/src/Squidex.Read/Apps/IAppClientEntity.cs @@ -6,8 +6,6 @@ // All rights reserved. // ========================================================================== -using System; - namespace Squidex.Read.Apps { public interface IAppClientEntity @@ -17,7 +15,5 @@ namespace Squidex.Read.Apps string Name { get; } string Secret { get; } - - DateTime ExpiresUtc { get; } } } diff --git a/src/Squidex.Read/IEntity.cs b/src/Squidex.Read/IEntity.cs index 17d62ee2f..707d53dc6 100644 --- a/src/Squidex.Read/IEntity.cs +++ b/src/Squidex.Read/IEntity.cs @@ -7,6 +7,7 @@ // ========================================================================== using System; +using NodaTime; namespace Squidex.Read { @@ -14,8 +15,8 @@ namespace Squidex.Read { Guid Id { get; } - DateTime Created { get; } + Instant Created { get; } - DateTime LastModified { get; } + Instant LastModified { get; } } } \ No newline at end of file diff --git a/src/Squidex.Write/Apps/Commands/AttachClient.cs b/src/Squidex.Write/Apps/Commands/AttachClient.cs index dd1ed8b36..a84bba7c6 100644 --- a/src/Squidex.Write/Apps/Commands/AttachClient.cs +++ b/src/Squidex.Write/Apps/Commands/AttachClient.cs @@ -6,19 +6,15 @@ // All rights reserved. // ========================================================================== -using System; using System.Collections.Generic; using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Commands; namespace Squidex.Write.Apps.Commands { - public sealed class AttachClient : AppAggregateCommand, ITimestampCommand, IValidatable + public sealed class AttachClient : AppAggregateCommand, IValidatable { public string Id { get; set; } - public DateTime Timestamp { get; set; } - public void Validate(IList errors) { if (!Id.IsSlug()) diff --git a/src/Squidex/Config/Domain/EventStoreModule.cs b/src/Squidex/Config/Domain/EventStoreModule.cs index 2214a3667..9e6f0f4fd 100644 --- a/src/Squidex/Config/Domain/EventStoreModule.cs +++ b/src/Squidex/Config/Domain/EventStoreModule.cs @@ -10,6 +10,7 @@ using System; using Autofac; using Microsoft.Extensions.Configuration; using MongoDB.Driver; +using NodaTime; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb.EventStore; @@ -55,7 +56,7 @@ namespace Squidex.Config.Domain var mongoDbClient = new MongoClient(connectionString); var mongoDatabase = mongoDbClient.GetDatabase(databaseName); - var eventStore = new MongoEventStore(mongoDatabase, c.Resolve()); + var eventStore = new MongoEventStore(mongoDatabase, c.Resolve(), c.Resolve()); return eventStore; }) diff --git a/src/Squidex/Config/Domain/InfrastructureModule.cs b/src/Squidex/Config/Domain/InfrastructureModule.cs index 5a38f13bf..b430e5f15 100644 --- a/src/Squidex/Config/Domain/InfrastructureModule.cs +++ b/src/Squidex/Config/Domain/InfrastructureModule.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; +using NodaTime; using Squidex.Core.Schemas; using Squidex.Core.Schemas.Json; using Squidex.Infrastructure; @@ -32,6 +33,10 @@ namespace Squidex.Config.Domain protected override void Load(ContainerBuilder builder) { + builder.Register(c => SystemClock.Instance) + .As() + .SingleInstance(); + builder.RegisterType() .As() .SingleInstance(); diff --git a/src/Squidex/Config/Domain/Serializers.cs b/src/Squidex/Config/Domain/Serializers.cs index 08fcbc99b..d7c27ed62 100644 --- a/src/Squidex/Config/Domain/Serializers.cs +++ b/src/Squidex/Config/Domain/Serializers.cs @@ -10,6 +10,8 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using NodaTime; +using NodaTime.Serialization.JsonNet; using Squidex.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.Json; @@ -26,20 +28,23 @@ namespace Squidex.Config.Domain settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + settings.Converters.Add(new InstantConverter()); + settings.Converters.Add(new LanguageConverter()); settings.Converters.Add(new NamedGuidIdConverter()); settings.Converters.Add(new NamedLongIdConverter()); settings.Converters.Add(new NamedStringIdConverter()); - settings.Converters.Add(new LanguageConverter()); settings.Converters.Add(new PropertiesBagConverter()); settings.Converters.Add(new RefTokenConverter()); settings.NullValueHandling = NullValueHandling.Ignore; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; - settings.DateParseHandling = DateParseHandling.DateTime; + settings.DateParseHandling = DateParseHandling.None; settings.TypeNameHandling = typeNameHandling; + settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); + return settings; } diff --git a/src/Squidex/Config/Identity/LazyClientStore.cs b/src/Squidex/Config/Identity/LazyClientStore.cs index 50a3de656..8425c4a9a 100644 --- a/src/Squidex/Config/Identity/LazyClientStore.cs +++ b/src/Squidex/Config/Identity/LazyClientStore.cs @@ -72,7 +72,7 @@ namespace Squidex.Config.Identity { ClientId = id, ClientName = id, - ClientSecrets = new List { new Secret(appClient.Secret.Sha256(), appClient.ExpiresUtc) }, + ClientSecrets = new List { new Secret(appClient.Secret.Sha256()) }, AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = new List diff --git a/src/Squidex/Config/Swagger/SwaggerServices.cs b/src/Squidex/Config/Swagger/SwaggerServices.cs index accb8e24c..5a0c5ee28 100644 --- a/src/Squidex/Config/Swagger/SwaggerServices.cs +++ b/src/Squidex/Config/Swagger/SwaggerServices.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using NJsonSchema; using NJsonSchema.Generation.TypeMappers; +using NodaTime; using NSwag.AspNetCore; using NSwag.SwaggerGeneration.WebApi.Processors.Security; using Squidex.Controllers.ContentApi.Generator; @@ -70,6 +71,11 @@ namespace Squidex.Config.Swagger settings.TypeMappers = new List { + new PrimitiveTypeMapper(typeof(Instant), schema => + { + schema.Type = JsonObjectType.String; + schema.Format = JsonFormatStrings.DateTime; + }), new PrimitiveTypeMapper(typeof(Language), s => s.Type = JsonObjectType.String), new PrimitiveTypeMapper(typeof(RefToken), s => s.Type = JsonObjectType.String) }; diff --git a/src/Squidex/Controllers/Api/Apps/Models/AppDto.cs b/src/Squidex/Controllers/Api/Apps/Models/AppDto.cs index ff3b1fd60..2321855ea 100644 --- a/src/Squidex/Controllers/Api/Apps/Models/AppDto.cs +++ b/src/Squidex/Controllers/Api/Apps/Models/AppDto.cs @@ -10,6 +10,7 @@ using System; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using NodaTime; using Squidex.Core.Apps; namespace Squidex.Controllers.Api.Apps.Models @@ -31,12 +32,12 @@ namespace Squidex.Controllers.Api.Apps.Models /// /// The date and time when the app has been created. /// - public DateTime Created { get; set; } + public Instant Created { get; set; } /// /// The date and time when the app has been modified last. /// - public DateTime LastModified { get; set; } + public Instant LastModified { get; set; } /// /// The permission level of the user. diff --git a/src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs b/src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs index 839950555..7fb20900e 100644 --- a/src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs +++ b/src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs @@ -8,6 +8,7 @@ using System; using System.ComponentModel.DataAnnotations; +using NodaTime; namespace Squidex.Controllers.Api.History.Models { @@ -33,6 +34,6 @@ namespace Squidex.Controllers.Api.History.Models /// /// The time when the event happened. /// - public DateTime Created { get; set; } + public Instant Created { get; set; } } } diff --git a/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs index d25a79fdf..88f62677c 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/DateTimeFieldPropertiesDto.cs @@ -6,10 +6,10 @@ // All rights reserved. // ========================================================================== -using System.Collections.Immutable; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; +using NodaTime; using Squidex.Core.Schemas; using Squidex.Infrastructure.Reflection; @@ -21,17 +21,17 @@ namespace Squidex.Controllers.Api.Schemas.Models /// /// The default value for the field value. /// - public string DefaultValue { get; set; } + public Instant? DefaultValue { get; set; } /// /// The maximum allowed value for the field value. /// - public string MaxValue { get; set; } + public Instant? MaxValue { get; set; } /// /// The minimum allowed value for the field value. /// - public string MinValue { get; set; } + public Instant? MinValue { get; set; } /// /// The editor that is used to manage this field. diff --git a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs index f226a806d..6636348e9 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDetailsDto.cs @@ -10,6 +10,7 @@ using Squidex.Infrastructure; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using NodaTime; namespace Squidex.Controllers.Api.Schemas.Models { @@ -65,11 +66,11 @@ namespace Squidex.Controllers.Api.Schemas.Models /// /// The date and time when the schema has been created. /// - public DateTime Created { get; set; } + public Instant Created { get; set; } /// /// The date and time when the schema has been modified last. /// - public DateTime LastModified { get; set; } + public Instant LastModified { get; set; } } } diff --git a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs index 646769d08..aa1e50b75 100644 --- a/src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs +++ b/src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs @@ -9,6 +9,7 @@ using Squidex.Infrastructure; using System; using System.ComponentModel.DataAnnotations; +using NodaTime; namespace Squidex.Controllers.Api.Schemas.Models { @@ -52,11 +53,11 @@ namespace Squidex.Controllers.Api.Schemas.Models /// /// The date and time when the schema has been created. /// - public DateTime Created { get; set; } + public Instant Created { get; set; } /// /// The date and time when the schema has been modified last. /// - public DateTime LastModified { get; set; } + public Instant LastModified { get; set; } } } diff --git a/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs b/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs index 095699663..f1e2693f6 100644 --- a/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs +++ b/src/Squidex/Controllers/ContentApi/Models/ContentDto.cs @@ -8,6 +8,7 @@ using System; using System.ComponentModel.DataAnnotations; +using NodaTime; using Squidex.Infrastructure; namespace Squidex.Controllers.ContentApi.Models @@ -40,12 +41,12 @@ namespace Squidex.Controllers.ContentApi.Models /// /// The date and time when the content element has been created. /// - public DateTime Created { get; set; } + public Instant Created { get; set; } /// /// The date and time when the content element has been modified last. /// - public DateTime LastModified { get; set; } + public Instant LastModified { get; set; } /// /// Indicates if the content element is publihed. diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 1e9aa1fd5..2b1a06aec 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -58,6 +58,7 @@ + diff --git a/src/Squidex/app/features/content/pages/content/content-field.component.html b/src/Squidex/app/features/content/pages/content/content-field.component.html index 4885797d5..ead778762 100644 --- a/src/Squidex/app/features/content/pages/content/content-field.component.html +++ b/src/Squidex/app/features/content/pages/content/content-field.component.html @@ -55,6 +55,8 @@ +
+
@@ -65,8 +67,6 @@
-
-
diff --git a/src/Squidex/app/features/schemas/declarations.ts b/src/Squidex/app/features/schemas/declarations.ts index c434277a8..4c7419da6 100644 --- a/src/Squidex/app/features/schemas/declarations.ts +++ b/src/Squidex/app/features/schemas/declarations.ts @@ -7,6 +7,8 @@ export * from './pages/schema/types/boolean-ui.component'; export * from './pages/schema/types/boolean-validation.component'; +export * from './pages/schema/types/date-time-ui.component'; +export * from './pages/schema/types/date-time-validation.component'; export * from './pages/schema/types/number-ui.component'; export * from './pages/schema/types/number-validation.component'; export * from './pages/schema/types/string-ui.component'; diff --git a/src/Squidex/app/features/schemas/module.ts b/src/Squidex/app/features/schemas/module.ts index ff6cdadbd..32cfdc54e 100644 --- a/src/Squidex/app/features/schemas/module.ts +++ b/src/Squidex/app/features/schemas/module.ts @@ -20,6 +20,8 @@ import { FieldComponent, BooleanUIComponent, BooleanValidationComponent, + DateTimeUIComponent, + DateTimeValidationComponent, NumberUIComponent, NumberValidationComponent, SchemaEditFormComponent, @@ -74,6 +76,8 @@ const routes: Routes = [ FieldComponent, BooleanUIComponent, BooleanValidationComponent, + DateTimeUIComponent, + DateTimeValidationComponent, NumberUIComponent, NumberValidationComponent, SchemaEditFormComponent, diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.html b/src/Squidex/app/features/schemas/pages/schema/field.component.html index ebed4912c..e7f967022 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.html @@ -138,6 +138,9 @@
+
+ +
@@ -152,6 +155,9 @@
+
+ +
diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.scss b/src/Squidex/app/features/schemas/pages/schema/field.component.scss index aed2d5998..48f8f2991 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.scss +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.scss @@ -35,7 +35,7 @@ $field-header: #e7ebef; &.active { background: $color-theme-blue; - border: 0; + border-color: $color-theme-blue; color: $color-accent-dark; } } diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts index 3e96d415a..62cf88081 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts @@ -43,7 +43,8 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { public fieldTypes: string[] = [ 'string', 'number', - 'boolean' + 'boolean', + 'dateTime' ]; public schemaName: string; @@ -65,7 +66,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { [ Validators.required, Validators.maxLength(40), - ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name must be a valid javascript name in camel case.') + ValidatorsEx.pattern('[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*', 'Name must be a valid javascript name in camel case.') ]] }); diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html index 6ab2beddb..67b60fbd8 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html @@ -3,8 +3,6 @@
- - @@ -23,13 +21,6 @@ Checkbox -
\ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.scss index c6403a8b4..5969e6344 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.scss +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.scss @@ -1,16 +1,6 @@ @import '_vars'; @import '_mixins'; -.minlength { - &-col { - position: relative; - } - - &-label { - @include absolute(0, -.25rem, auto, auto); - } -} - .form-check-input { margin: 0; } \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html new file mode 100644 index 000000000..8f66a87ba --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.html @@ -0,0 +1,33 @@ +
+
+ + +
+ + + + Define the placeholder for the input control. + +
+
+
+ + +
+ + +
+
+
\ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.scss new file mode 100644 index 000000000..fbb752506 --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.scss @@ -0,0 +1,2 @@ +@import '_vars'; +@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts new file mode 100644 index 000000000..26d7e34fe --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts @@ -0,0 +1,40 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { Observable } from 'rxjs'; + +import { FloatConverter, NumberFieldPropertiesDto } from 'shared'; + +@Component({ + selector: 'sqx-date-time-ui', + styleUrls: ['date-time-ui.component.scss'], + templateUrl: 'date-time-ui.component.html' +}) +export class DateTimeUIComponent implements OnInit { + @Input() + public editForm: FormGroup; + + @Input() + public properties: NumberFieldPropertiesDto; + + public converter = new FloatConverter(); + + public hideAllowedValues: Observable; + + public ngOnInit() { + this.editForm.addControl('editor', + new FormControl(this.properties.editor, [ + Validators.required + ])); + this.editForm.addControl('placeholder', + new FormControl(this.properties.placeholder, [ + Validators.maxLength(100) + ])); + } +} \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.html b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.html new file mode 100644 index 000000000..4d669c216 --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.html @@ -0,0 +1,33 @@ +
+
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+
\ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.scss new file mode 100644 index 000000000..5969e6344 --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.scss @@ -0,0 +1,6 @@ +@import '_vars'; +@import '_mixins'; + +.form-check-input { + margin: 0; +} \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts new file mode 100644 index 000000000..0509f0179 --- /dev/null +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts @@ -0,0 +1,49 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { Observable } from 'rxjs'; + +import { NumberFieldPropertiesDto, ValidatorsEx } from 'shared'; + +@Component({ + selector: 'sqx-date-time-validation', + styleUrls: ['date-time-validation.component.scss'], + templateUrl: 'date-time-validation.component.html' +}) +export class DateTimeValidationComponent implements OnInit { + @Input() + public editForm: FormGroup; + + @Input() + public properties: NumberFieldPropertiesDto; + + public hideDefaultValue: Observable; + + public ngOnInit() { + this.editForm.addControl('maxValue', + new FormControl(this.properties.maxValue, [ + ValidatorsEx.validDateTime() + ])); + + this.editForm.addControl('minValue', + new FormControl(this.properties.minValue, [ + ValidatorsEx.validDateTime() + ])); + + this.editForm.addControl('defaultValue', + new FormControl(this.properties.defaultValue, [ + ValidatorsEx.validDateTime() + ])); + + this.hideDefaultValue = + Observable.of(this.properties.isRequired) + .merge(this.editForm.get('isRequired').valueChanges) + .map(x => !!x); + } +} \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html index 739632e74..adbda39ea 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html @@ -3,8 +3,6 @@
- - diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss index 96c628285..b9d07d654 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.scss @@ -7,7 +7,7 @@ } &-label { - @include absolute(0, -25rem, auto, auto); + @include absolute(0, -2rem, auto, auto); } } diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html index 8a28a8f2c..9233af64a 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html @@ -3,8 +3,6 @@
- - diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss index 96c628285..b9d07d654 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss @@ -7,7 +7,7 @@ } &-label { - @include absolute(0, -25rem, auto, auto); + @include absolute(0, -2rem, auto, auto); } } diff --git a/src/Squidex/app/features/settings/pages/clients/clients-page.component.html b/src/Squidex/app/features/settings/pages/clients/clients-page.component.html index 5fed3b2a4..df59e5a9e 100644 --- a/src/Squidex/app/features/settings/pages/clients/clients-page.component.html +++ b/src/Squidex/app/features/settings/pages/clients/clients-page.component.html @@ -13,10 +13,9 @@
+
-
-
No client created yet. diff --git a/src/Squidex/app/framework/angular/control-errors.component.ts b/src/Squidex/app/framework/angular/control-errors.component.ts index 7132be85a..108f35010 100644 --- a/src/Squidex/app/framework/angular/control-errors.component.ts +++ b/src/Squidex/app/framework/angular/control-errors.component.ts @@ -18,6 +18,7 @@ const DEFAULT_ERRORS: { [key: string]: string } = { maxvalue: '{field} must be smaller than {maxValue}.', minlength: '{field} must have more than {requiredLength} characters.', maxlength: '{field} cannot have more than {requiredLength} characters.', + validdatetime: '{field} is not a valid date time', validnumber: '{field} is not a valid number.', validvalues: '{field} is not a valid value.' }; diff --git a/src/Squidex/app/framework/angular/date-time-editor.component.html b/src/Squidex/app/framework/angular/date-time-editor.component.html index 67a9a55c1..607c25cac 100644 --- a/src/Squidex/app/framework/angular/date-time-editor.component.html +++ b/src/Squidex/app/framework/angular/date-time-editor.component.html @@ -1,15 +1,10 @@
- +
-
- -
diff --git a/src/Squidex/app/framework/angular/date-time-editor.component.scss b/src/Squidex/app/framework/angular/date-time-editor.component.scss index d379ab3e5..15ae9dda6 100644 --- a/src/Squidex/app/framework/angular/date-time-editor.component.scss +++ b/src/Squidex/app/framework/angular/date-time-editor.component.scss @@ -3,6 +3,21 @@ $form-color: #fff; +:host(.ng-invalid) { + &.ng-dirty { + .form-control { + & { + border-color: $color-theme-error; + } + + &:hover, + &:focus { + border-color: $color-theme-error-dark; + } + } + } +} + .form-control { &[readonly] { background: $form-color; @@ -11,26 +26,16 @@ $form-color: #fff; .date-group { & { - padding-right: .5rem; + padding-right: .25rem; } .form-control { - width: 8rem; + width: 7.5rem; } } .time-group { - & { - padding-right: .5rem; - } - - .form-control { - width: 5rem; - } -} - -.timezone-group { .form-control { - width: 8.5rem; + width: 7.5rem; } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/date-time-editor.component.ts b/src/Squidex/app/framework/angular/date-time-editor.component.ts index bef37912b..9466eec83 100644 --- a/src/Squidex/app/framework/angular/date-time-editor.component.ts +++ b/src/Squidex/app/framework/angular/date-time-editor.component.ts @@ -11,7 +11,6 @@ import * as moment from 'moment'; let Pikaday = require('pikaday/pikaday'); - /* tslint:disable:no-empty */ const NOOP = () => { }; @@ -22,50 +21,6 @@ export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { 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'], @@ -74,75 +29,75 @@ const TIMEZONES: any[] = [ }) export class DateTimeEditorComponent implements ControlValueAccessor, OnInit, AfterViewInit { private picker: any; - private time: any; - private date: any; - private offset: number; + private timeValue: any | null = null; + private dateValue: any | null = null; 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'; + return this.mode === 'DateTime'; } - public timezones = TIMEZONES; - - public timeControl = - new FormControl(); - - public timeZoneControl = - new FormControl(); + public timeControl = new FormControl(); - public isDisabled = false; + public dateControl = new FormControl(); @Input() public mode: string; + @Input() + public enforceTime: boolean; + @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()); + if (!value || value.length === 0) { + this.timeValue = null; + } else { + this.timeValue = moment(value, 'HH:mm:ss'); + } this.updateValue(); }); - this.timeZoneControl.valueChanges.subscribe(value => { - this.offset = value; + this.dateControl.valueChanges.subscribe(value => { + if (!value || value.length === 0) { + this.dateValue = null; + } else { + this.dateValue = moment(value, 'YYYY-MM-DD'); + } this.updateValue(); - this.touched(); }); } public writeValue(value: any) { - const parsed = (moment.parseZone(value) || moment()); + if (!value || value.length === 0) { + this.timeValue = null; + this.dateValue = null; + } else { + const parsed = moment.parseZone(value); - this.time = moment(parsed); - this.date = moment(parsed); + this.dateValue = moment(parsed); - this.offset = parsed.utcOffset(); + if (this.showTime) { + this.timeValue = moment(parsed); + } + } this.updateControls(); } public setDisabledState(isDisabled: boolean): void { - this.isDisabled = isDisabled; - if (isDisabled) { + this.dateControl.disable(); this.timeControl.disable(); - this.timeZoneControl.disable(); } else { + this.dateControl.enable(); this.timeControl.enable(); - this.timeZoneControl.enable(); } } @@ -155,17 +110,9 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnInit, Af } public ngAfterViewInit() { - this.picker = new Pikaday({ - field: this.dateInput.nativeElement, - format: 'YYYY-MM-DD', + 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.dateValue = this.picker.getMoment(); this.updateValue(); this.touched(); @@ -180,34 +127,43 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnInit, Af } private updateValue() { - let result = this.date.format('YYYY-MM-DD'); + let result: string = null; - if (this.showTime) { - result += 'T'; - result += this.time.format('HH:mm:ss'); + if ((this.dateValue && !this.dateValue.isValid()) || (this.timeValue && !this.timeValue.isValid())) { + result = 'Invalid DateTime'; + } 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'; + } } - if (this.showTimezone) { - result += moment().utcOffset(this.offset).format('Z'); - } else if (this.showTime) { - result += 'Z'; - } + console.error(result); this.changeCallback(result); } private updateControls() { - if (!this.date) { + if (!this.dateValue) { return; } this.suppressEvents = true; - this.timeControl.setValue(this.time.format('HH:mm'), { emitEvent: false }); - this.timeZoneControl.setValue(this.offset, { emitEvent: false }); + if (this.timeValue && this.timeValue.isValid()) { + this.timeControl.setValue(this.timeValue.format('HH:mm:ss'), { emitEvent: false }); + } + if (this.dateValue && this.dateValue.isValid()) { + this.dateControl.setValue(this.dateValue.format('YYYY-MM-DD'), { emitEvent: false }); - if (this.picker) { - this.picker.setMoment(this.date); + this.picker.setMoment(this.dateValue); } this.suppressEvents = false; diff --git a/src/Squidex/app/framework/angular/validators.ts b/src/Squidex/app/framework/angular/validators.ts index b86ff7a6b..ef70f0539 100644 --- a/src/Squidex/app/framework/angular/validators.ts +++ b/src/Squidex/app/framework/angular/validators.ts @@ -11,6 +11,8 @@ import { Validators } from '@angular/forms'; +import { DateTime } from './../utils/date-time'; + export module ValidatorsEx { export function pattern(pattern: string | RegExp, message?: string): ValidatorFn { if (!pattern) { @@ -47,6 +49,22 @@ export module ValidatorsEx { }; } + export function validDateTime() { + return (control: AbstractControl): { [key: string]: any } => { + const v: string = control.value; + + if (v) { + try { + DateTime.parseISO_UTC(v); + } catch (e) { + return { validdateTime: false }; + } + } + + return {}; + }; + } + export function between(minValue?: number, maxValue?: number) { if (!minValue || !maxValue) { return Validators.nullValidator; diff --git a/src/Squidex/app/framework/utils/date-time.ts b/src/Squidex/app/framework/utils/date-time.ts index 8338bf11a..6198ea448 100644 --- a/src/Squidex/app/framework/utils/date-time.ts +++ b/src/Squidex/app/framework/utils/date-time.ts @@ -111,7 +111,6 @@ export class DateTime { return new DateTime(parsedMoment.toDate()); } else { throw `${value} is not a valid date time string`; - } } diff --git a/src/Squidex/app/shared/components/language-selector.component.ts b/src/Squidex/app/shared/components/language-selector.component.ts index 6d79cb605..af60a5a82 100644 --- a/src/Squidex/app/shared/components/language-selector.component.ts +++ b/src/Squidex/app/shared/components/language-selector.component.ts @@ -35,7 +35,7 @@ export class LanguageSelectorComponent implements OnInit { public selectedLanguageChanged = new EventEmitter(); public get isSmallMode(): boolean { - return this.languages && this.languages.length > 0 && this.languages.length <= 0; + return this.languages && this.languages.length > 0 && this.languages.length <= 3; } public get isLargeMode(): boolean { diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 2d3fdacc9..a1fbd5f3a 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -40,6 +40,12 @@ export function createProperties(fieldType: string, values: Object | null = null undefined, undefined, undefined, false, false, false, 'Checkbox', undefined); break; + case 'dateTime': + properties = + new DateTimeFieldPropertiesDto( + undefined, undefined, undefined, false, false, false, 'DateTime', + undefined, undefined, undefined); + break; default: throw 'Invalid properties type'; } @@ -140,6 +146,22 @@ export class StringFieldPropertiesDto extends FieldPropertiesDto { } } +export class DateTimeFieldPropertiesDto extends FieldPropertiesDto { + constructor(label: string | undefined, hints: string | undefined, placeholder: string | undefined, + isRequired: boolean, + isListField: boolean, + isLocalizable: boolean, + public readonly editor: string, + public readonly defaultValue?: string, + public readonly maxValue?: string, + public readonly minValue?: string + ) { + super(label, hints, placeholder, isRequired, isListField, isLocalizable); + + this['fieldType'] = 'dateTime'; + } +} + export class BooleanFieldPropertiesDto extends FieldPropertiesDto { constructor(label: string | undefined, hints: string | undefined, placeholder: string | undefined, isRequired: boolean, diff --git a/src/Squidex/app/theme/icomoon/demo-files/demo.css b/src/Squidex/app/theme/icomoon/demo-files/demo.css index 8fd83c792..bea3cc13c 100644 --- a/src/Squidex/app/theme/icomoon/demo-files/demo.css +++ b/src/Squidex/app/theme/icomoon/demo-files/demo.css @@ -150,9 +150,12 @@ p { font-size: 32px; } .fs2 { - font-size: 28px; + font-size: 32px; } .fs3 { + font-size: 28px; +} +.fs4 { font-size: 32px; } diff --git a/src/Squidex/app/theme/icomoon/demo.html b/src/Squidex/app/theme/icomoon/demo.html index c4be3f5ce..eeae76328 100644 --- a/src/Squidex/app/theme/icomoon/demo.html +++ b/src/Squidex/app/theme/icomoon/demo.html @@ -9,11 +9,30 @@
-

Font Name: icomoon (Glyphs: 53)

+

Font Name: icomoon (Glyphs: 54)

-

Grid Size: 16

+

Grid Size: 32

+
+ + + + icon-browser +
+
+ + +
+
+ liga: + +
+
+
+
+

Grid Size: 16

+
@@ -29,7 +48,7 @@
-
+
@@ -45,7 +64,7 @@
-
+
@@ -61,7 +80,7 @@
-
+
@@ -77,7 +96,7 @@
-
+
@@ -93,7 +112,7 @@
-
+
@@ -109,7 +128,7 @@
-
+
@@ -128,7 +147,7 @@

Grid Size: 14

-
+
@@ -144,7 +163,7 @@
-
+
@@ -160,7 +179,7 @@
-
+
@@ -176,7 +195,7 @@
-
+
@@ -192,7 +211,7 @@
-
+
@@ -208,7 +227,7 @@
-
+
@@ -224,7 +243,7 @@
-
+
@@ -240,7 +259,7 @@
-
+
@@ -256,7 +275,7 @@
-
+
@@ -275,7 +294,7 @@

Grid Size: Unknown

-
+
@@ -291,7 +310,7 @@
-
+
@@ -307,7 +326,7 @@
-
+
@@ -323,7 +342,7 @@
-
+
@@ -339,7 +358,7 @@
-
+
@@ -355,7 +374,7 @@
-
+
@@ -371,7 +390,7 @@
-
+
@@ -387,7 +406,7 @@
-
+
@@ -403,7 +422,7 @@
-
+
@@ -419,7 +438,7 @@
-
+
@@ -435,7 +454,7 @@
-
+
@@ -451,7 +470,7 @@
-
+
@@ -467,7 +486,7 @@
-
+
@@ -483,7 +502,7 @@
-
+
@@ -499,7 +518,7 @@
-
+
@@ -515,7 +534,7 @@
-
+
@@ -531,7 +550,7 @@
-
+
@@ -547,7 +566,7 @@
-
+
@@ -563,7 +582,7 @@
-
+
@@ -579,7 +598,7 @@
-
+
@@ -595,7 +614,7 @@
-
+
@@ -611,7 +630,7 @@
-
+
@@ -627,7 +646,7 @@
-
+
@@ -643,7 +662,7 @@
-
+
@@ -659,7 +678,7 @@
-
+
@@ -675,7 +694,7 @@
-
+
@@ -691,7 +710,7 @@
-
+
@@ -707,7 +726,7 @@
-
+
@@ -723,7 +742,7 @@
-
+
-
+
@@ -755,7 +774,7 @@
-
+
@@ -771,7 +790,7 @@
-
+
@@ -787,7 +806,7 @@
-
+
@@ -803,7 +822,7 @@
-
+
@@ -819,7 +838,7 @@
-
+
@@ -835,7 +854,7 @@
-
+
@@ -851,7 +870,7 @@
-
+
@@ -867,7 +886,7 @@
-
+
@@ -883,7 +902,7 @@
-
+
@@ -899,7 +918,39 @@
-
+
+
+ + + + icon-type-date-time +
+
+ + +
+
+ liga: + +
+
+
+
+ + + + icon-type-dateTime +
+
+ + +
+
+ liga: + +
+
+
@@ -915,7 +966,7 @@
-
+
@@ -931,7 +982,7 @@
-
+
diff --git a/src/Squidex/app/theme/icomoon/fonts/icomoon.eot b/src/Squidex/app/theme/icomoon/fonts/icomoon.eot index 0f52f8885991fa17186b039a5699680c7f77988f..fe8a0f34d0594dc4d95c82ad4edeb2c4a1d8cd89 100644 GIT binary patch delta 721 zcmZ{hKWI}?6voee|1Q^(+K}5sdv(8n1VGm&3Ltd#m^%`7ZHvlHiASdrQ)$-b9(LiNdC9!fHT~q6U;98f9{t#vS z8LC6f^<5>)&=$PmV@~i84(R@iSR@jo?H&+{D|p|fVI`=2SDL0I%}S>9sNqSoyFp@a z15-fC$EtvzWH?Pc&Bc1C{t?Q0_~`F9|D5<6^q~O7&g#z1qi27>fEMtV?TfESgeQGy o>t*KHalJ$))?Z^p!J2G+&U{la^)|b5B=HD6N=$B^8vnq40@6~AH2?qr delta 316 zcmZ3I`5~3La7U|`q-#0kl{i3Q?emv&D)AyY4PvA&yu zfw6*tK{p~JH8F)}Z&*A7gWd(8I - + @@ -60,4 +60,5 @@ + \ No newline at end of file diff --git a/src/Squidex/app/theme/icomoon/fonts/icomoon.ttf b/src/Squidex/app/theme/icomoon/fonts/icomoon.ttf index acced8348ab3e7c8437efd61931fb6049f2cd254..ab2ac04080bbbf99e6fd58233fae8e1a8f90fcd1 100644 GIT binary patch delta 764 zcmZ{i%WD%+6o=29*Yz5bCgwKTOd*p>+_YrOOKBA&h=LWw2cmB5(AH$p*kEgkpv)*J zC@N?d(v@Af6fD%8E(X^kw7PJag$w@yS81l6Gt>H@c;TM&`_AhQocT2EHC z`g;KT4(4l339pY-y9!V` zjGVjSv}Tr1iyrc2eB@z!Zhiqj6i>NJc~I52&;4Vp`hrcc>e?ChVAPEPZ25X!^NzW=6$%Bl{n%+gu3V%aP^6lv-Ds;y^Elmg^Wb zwH^qrb-Cy#sV0~tn`B<#DK^8f;BJ6*f=94R_g|*t@ibj;mry*x`yMqbq3wIpG$m

FPrUz*)j5}T`71*8JB3IsWZ-NY^zE0Ow3C@ayUJ8%B>xDDDS!1(&JFV_Z+{Dhbm z^4E=%uSi6Gd!hKtffA;}KGomy0rWaW&_#|sb~7+A$^iKx>50V! zKw1FE2hkkqIhAQr7wfx${0asJ-H43T#1x*rVet$MdKZB5W*I;M_Cw6S7#Q?9fP9sV z+>(luIlN1Nd>tVFNlt#UW1>~7%O3^?1E8S{CAo- z57fliR*+v@0(2-47=a8>U|?omG4X&sqsb&jZ$^{NK8&rp(IEc-LHLr1i{trizA|vL zfcaO>cYFjfAoTwOEDX#CfLsm+CXgr)0QE5+_OkK44hF N29~niY-e^! diff --git a/src/Squidex/app/theme/icomoon/fonts/icomoon.woff b/src/Squidex/app/theme/icomoon/fonts/icomoon.woff index bb203391ec198264aaec094b1ed44939ded04574..1a3044c82ea690cd95de9c13e74c25d6a6c94847 100644 GIT binary patch delta 820 zcmZ`&&ubGw82x5{WE|SmgiLlXTgavoK`dRc`@0<7Dyl;kKc9$!kYl-`p zOC?}{BR38`!Q5QRny*eI-M*=Mc@BWp(7i@5uWxTw%k$)IqCT>RSas&XZS+0@3~7XC zcOO2lRm#%<&L_+i2z|=dhZ>pyr%=K%f>89)T798`9_b&1FYlsjGqY2eyOixkW07!` zpWmsM8+dT`G3r!+D{SIUxn4nU4G(`xSh*q)!XJv|8&#cGfWk#JX4&=%yHUlXkgS4cT8rSq%@}Kl5P5 z*T8qgF}n5g>*Uchzo178^tbKO8zSMn_n`D~D#3Elr~11-fL;%b?&dk=Xd@yk)-u7!i_v delta 368 zcmX?;F(Xx^+~3WOfsp|SjAk%!gK3}1h9(jdb%g8Ll5-Oa7#J8cfWjdlEFN}gcY0zm zNNf*~&jG~(={c2YK(QkX47w2@EOoKIJ0mqQg@Hj|2B^jign9Oc#b*Eofnp%DR6v;h z5c98$+>#2Q7!Wf&0pXK5yi0QOlY#2|3}S!+B_M3o>hdQyu>xqZ!37{+0gM9}qw*4S zQ-NY1fR?v`@Juhx-v#-_Ks`nv10m{|fh=a`6_a@w?HNrb`!ISlnrxoI*s2@N2$TSV z@Ff!$$Mf5KW#DE3^RJxm_y}S^=>G>;7?=+Lxf~2kAWSI3e{{RC6L_Ls%ngo^w Y5tfrx44+KqF)CpLOIdF2GCITv00PNP(f|Me diff --git a/src/Squidex/app/theme/icomoon/selection.json b/src/Squidex/app/theme/icomoon/selection.json index d7cb3da57..b5dd7e9d0 100644 --- a/src/Squidex/app/theme/icomoon/selection.json +++ b/src/Squidex/app/theme/icomoon/selection.json @@ -1,6 +1,39 @@ { "IcoMoonType": "selection", "icons": [ + { + "icon": { + "paths": [ + "M1328 320c-8.832 0-16 7.168-16 16v640c0 8.832-7.168 16-16 16h-1248c-8.832 0-16-7.168-16-16v-640c0-8.832-7.168-16-16-16s-16 7.168-16 16v640c0 26.464 21.536 48 48 48h1248c26.464 0 48-21.536 48-48v-640c0-8.832-7.168-16-16-16zM1296 0h-1248c-26.464 0-48 21.536-48 48v192c0 8.832 7.168 16 16 16h1312c8.832 0 16-7.168 16-16v-192c0-26.464-21.536-48-48-48zM1312 224h-1280v-176c0-8.832 7.168-16 16-16h1248c8.832 0 16 7.168 16 16v176zM560 896c8.832 0 16-7.168 16-16v-512c0-8.832-7.168-16-16-16h-416c-8.832 0-16 7.168-16 16v512c0 8.832 7.168 16 16 16h416zM160 384h384v480h-384v-480zM720 480h480c8.832 0 16-7.168 16-16s-7.168-16-16-16h-480c-8.832 0-16 7.168-16 16s7.168 16 16 16zM720 640h480c8.832 0 16-7.168 16-16s-7.168-16-16-16h-480c-8.832 0-16 7.168-16 16s7.168 16 16 16zM720 800h480c8.832 0 16-7.168 16-16s-7.168-16-16-16h-480c-8.832 0-16 7.168-16 16s7.168 16 16 16zM96 128c0 17.673 14.327 32 32 32s32-14.327 32-32c0-17.673-14.327-32-32-32s-32 14.327-32 32zM224 128c0 17.673 14.327 32 32 32s32-14.327 32-32c0-17.673-14.327-32-32-32s-32 14.327-32 32zM352 128c0 17.673 14.327 32 32 32s32-14.327 32-32c0-17.673-14.327-32-32-32s-32 14.327-32 32z" + ], + "attrs": [ + {} + ], + "width": 1344, + "isMulticolor": false, + "isMulticolor2": false, + "tags": [ + "browser", + "window", + "software", + "program" + ], + "grid": 32 + }, + "attrs": [ + {} + ], + "properties": { + "order": 1, + "id": 0, + "prevSize": 32, + "code": 59701, + "name": "browser" + }, + "setIdx": 0, + "setId": 3, + "iconIdx": 0 + }, { "icon": { "paths": [ @@ -27,7 +60,7 @@ "code": 59699, "name": "unlocked" }, - "setIdx": 0, + "setIdx": 1, "setId": 2, "iconIdx": 0 }, @@ -59,7 +92,7 @@ "code": 59700, "name": "lock" }, - "setIdx": 0, + "setIdx": 1, "setId": 2, "iconIdx": 1 }, @@ -98,7 +131,7 @@ "code": 59694, "name": "reset" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 9 }, @@ -128,7 +161,7 @@ "code": 59695, "name": "pause" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 10 }, @@ -158,7 +191,7 @@ "code": 59696, "name": "play" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 11 }, @@ -193,7 +226,7 @@ "code": 59693, "name": "settings2" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 12 }, @@ -230,7 +263,7 @@ "prevSize": 32, "code": 59650 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 13 }, @@ -260,7 +293,7 @@ "code": 59697, "name": "angle-right" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 0 }, @@ -289,7 +322,7 @@ "prevSize": 28, "code": 59698 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 1 }, @@ -319,7 +352,7 @@ "code": 59689, "name": "caret-right" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 2 }, @@ -349,7 +382,7 @@ "code": 59690, "name": "caret-left" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 3 }, @@ -379,7 +412,7 @@ "code": 59691, "name": "caret-up" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 4 }, @@ -409,7 +442,7 @@ "code": 59692, "name": "caret-down" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 5 }, @@ -439,7 +472,7 @@ "code": 59651, "name": "angle-up" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 6 }, @@ -469,7 +502,7 @@ "code": 59648, "name": "angle-down" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 7 }, @@ -499,7 +532,7 @@ "code": 59649, "name": "angle-left" }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 8 }, @@ -528,7 +561,7 @@ "prevSize": 32, "code": 59652 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 14 }, @@ -557,7 +590,7 @@ "prevSize": 32, "code": 59653 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 15 }, @@ -586,7 +619,7 @@ "prevSize": 32, "code": 59654 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 16 }, @@ -615,7 +648,7 @@ "prevSize": 32, "code": 59655 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 17 }, @@ -644,7 +677,7 @@ "prevSize": 32, "code": 59656 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 18 }, @@ -673,7 +706,7 @@ "prevSize": 32, "code": 59657 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 19 }, @@ -702,7 +735,7 @@ "prevSize": 32, "code": 59658 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 20 }, @@ -731,7 +764,7 @@ "prevSize": 32, "code": 59659 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 21 }, @@ -760,7 +793,7 @@ "prevSize": 32, "code": 59660 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 22 }, @@ -789,7 +822,7 @@ "prevSize": 32, "code": 59661 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 23 }, @@ -818,7 +851,7 @@ "prevSize": 32, "code": 59662 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 24 }, @@ -847,7 +880,7 @@ "prevSize": 32, "code": 59663 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 25 }, @@ -876,7 +909,7 @@ "prevSize": 32, "code": 59664 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 26 }, @@ -905,7 +938,7 @@ "prevSize": 32, "code": 59665 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 27 }, @@ -934,7 +967,7 @@ "prevSize": 32, "code": 59666 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 28 }, @@ -963,7 +996,7 @@ "prevSize": 32, "code": 59667 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 29 }, @@ -992,7 +1025,7 @@ "prevSize": 32, "code": 59668 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 30 }, @@ -1021,7 +1054,7 @@ "prevSize": 32, "code": 59669 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 31 }, @@ -1050,7 +1083,7 @@ "prevSize": 32, "code": 59670 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 32 }, @@ -1079,7 +1112,7 @@ "prevSize": 32, "code": 59671 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 33 }, @@ -1108,7 +1141,7 @@ "prevSize": 32, "code": 59672 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 34 }, @@ -1137,7 +1170,7 @@ "prevSize": 32, "code": 59673 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 35 }, @@ -1166,7 +1199,7 @@ "prevSize": 32, "code": 59674 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 36 }, @@ -1195,7 +1228,7 @@ "prevSize": 32, "code": 59675 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 37 }, @@ -1224,7 +1257,7 @@ "prevSize": 32, "code": 59676 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 38 }, @@ -1253,7 +1286,7 @@ "prevSize": 32, "code": 59677 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 39 }, @@ -1282,7 +1315,7 @@ "prevSize": 32, "code": 59678 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 40 }, @@ -1311,7 +1344,7 @@ "prevSize": 32, "code": 59679 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 41 }, @@ -1340,7 +1373,7 @@ "prevSize": 32, "code": 59680 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 42 }, @@ -1369,7 +1402,7 @@ "prevSize": 32, "code": 59681 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 43 }, @@ -1398,7 +1431,7 @@ "prevSize": 32, "code": 59682 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 44 }, @@ -1427,7 +1460,7 @@ "prevSize": 32, "code": 59683 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 45 }, @@ -1456,7 +1489,7 @@ "prevSize": 32, "code": 59684 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 46 }, @@ -1481,11 +1514,11 @@ "properties": { "order": 24, "id": 3, - "name": "type-datetime", + "name": "type-datetime, type-date-time, type-dateTime", "prevSize": 32, "code": 59685 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 47 }, @@ -1514,7 +1547,7 @@ "prevSize": 32, "code": 59686 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 48 }, @@ -1543,7 +1576,7 @@ "prevSize": 32, "code": 59687 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 49 }, @@ -1572,7 +1605,7 @@ "prevSize": 32, "code": 59688 }, - "setIdx": 1, + "setIdx": 2, "setId": 1, "iconIdx": 50 } diff --git a/src/Squidex/app/theme/icomoon/style.css b/src/Squidex/app/theme/icomoon/style.css index 570917ba6..8dc3b858e 100644 --- a/src/Squidex/app/theme/icomoon/style.css +++ b/src/Squidex/app/theme/icomoon/style.css @@ -1,10 +1,10 @@ @font-face { font-family: 'icomoon'; - src: url('fonts/icomoon.eot?1qun2u'); - src: url('fonts/icomoon.eot?1qun2u#iefix') format('embedded-opentype'), - url('fonts/icomoon.ttf?1qun2u') format('truetype'), - url('fonts/icomoon.woff?1qun2u') format('woff'), - url('fonts/icomoon.svg?1qun2u#icomoon') format('svg'); + src: url('fonts/icomoon.eot?7ja4lm'); + src: url('fonts/icomoon.eot?7ja4lm#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?7ja4lm') format('truetype'), + url('fonts/icomoon.woff?7ja4lm') format('woff'), + url('fonts/icomoon.svg?7ja4lm#icomoon') format('svg'); font-weight: normal; font-style: normal; } @@ -24,6 +24,9 @@ -moz-osx-font-smoothing: grayscale; } +.icon-browser:before { + content: "\e935"; +} .icon-unlocked:before { content: "\e933"; } @@ -189,6 +192,12 @@ .icon-type-datetime:before { content: "\e925"; } +.icon-type-date-time:before { + content: "\e925"; +} +.icon-type-dateTime:before { + content: "\e925"; +} .icon-type-number:before { content: "\e926"; } diff --git a/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs index 52105eab0..065d8d135 100644 --- a/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; diff --git a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs index 9433c02ba..45032934b 100644 --- a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs @@ -270,20 +270,24 @@ namespace Squidex.Core.Schemas Assert.NotNull(edmModel); } - private Schema BuildMixedSchema() + private static Schema BuildMixedSchema() { + var allowedValues = new[] { "1", "2" }.ToImmutableList(); + var schema = Schema.Create("user", new SchemaProperties { Hints = "The User" }) - .AddOrUpdateField(new StringField(1, "firstName", - new StringFieldProperties { Label = "FirstName", IsLocalizable = true, IsRequired = true, AllowedValues = new[] { "1", "2" }.ToImmutableList() })) - .AddOrUpdateField(new StringField(2, "lastName", - new StringFieldProperties { Hints = "Last Name" })) - .AddOrUpdateField(new BooleanField(3, "admin", + .AddOrUpdateField(new StringField(1, "my-string1", + new StringFieldProperties { Label = "My String1", IsLocalizable = true, IsRequired = true, AllowedValues = allowedValues })) + .AddOrUpdateField(new StringField(2, "my-string2", + new StringFieldProperties { Hints = "My String1" })) + .AddOrUpdateField(new NumberField(3, "my-number", + new NumberFieldProperties { MinValue = 1, MaxValue = 10 })) + .AddOrUpdateField(new BooleanField(4, "my-boolean", new BooleanFieldProperties())) - .AddOrUpdateField(new DateTimeField(4, "birtday", - new DateTimeFieldProperties())) - .AddOrUpdateField(new NumberField(5, "age", - new NumberFieldProperties { MinValue = 1, MaxValue = 10 })); + .AddOrUpdateField(new DateTimeField(5, "my-datetime", + new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime })) + .AddOrUpdateField(new DateTimeField(6, "my-date", + new DateTimeFieldProperties { Editor = DateTimeFieldEditor.Date })); return schema; } diff --git a/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs b/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs index ebe848d18..f3e929c96 100644 --- a/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/SchemaValidationTests.cs @@ -6,7 +6,6 @@ // All rights reserved. // ========================================================================== -using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; @@ -297,9 +296,9 @@ namespace Squidex.Core.Schemas .AddOrUpdateField( new StringField(1, "my-string", new StringFieldProperties { DefaultValue = "EN-String", IsLocalizable = true })) .AddOrUpdateField( - new BooleanField(2, "my-boolean", new BooleanFieldProperties { DefaultValue = true })) + new NumberField(2, "my-number", new NumberFieldProperties { DefaultValue = 123 })) .AddOrUpdateField( - new NumberField(3, "my-number", new NumberFieldProperties { DefaultValue = 123 })) + new BooleanField(3, "my-boolean", new BooleanFieldProperties { DefaultValue = true })) .AddOrUpdateField( new DateTimeField(4, "my-datetime", new DateTimeFieldProperties { DefaultValue = now })); diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs index b38f504ca..4140b90f8 100644 --- a/tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Commands/EnrichWithTimestampHandlerTests.cs @@ -6,8 +6,9 @@ // All rights reserved. // ========================================================================== -using System; using System.Threading.Tasks; +using Moq; +using NodaTime; using Xunit; namespace Squidex.Infrastructure.CQRS.Commands @@ -20,46 +21,37 @@ namespace Squidex.Infrastructure.CQRS.Commands private sealed class MyTimestampCommand : ITimestampCommand { - public DateTime Timestamp { get; set; } + public Instant Timestamp { get; set; } } + private readonly Mock clock = new Mock(); + [Fact] public async Task Should_set_timestamp_for_timestamp_command() { - var utc = DateTime.Today; - var sut = new EnrichWithTimestampHandler(() => utc); - - var command = new MyTimestampCommand(); + var utc = Instant.FromUnixTimeSeconds(1000); + var sut = new EnrichWithTimestampHandler(clock.Object); - var result = await sut.HandleAsync(new CommandContext(command)); - - Assert.False(result); - Assert.Equal(utc, command.Timestamp); - } - - [Fact] - public async Task Should_set_with_now_datetime_for_timestamp_command() - { - var now = DateTime.UtcNow; - var sut = new EnrichWithTimestampHandler(); + clock.Setup(x => x.GetCurrentInstant()).Returns(utc); var command = new MyTimestampCommand(); var result = await sut.HandleAsync(new CommandContext(command)); Assert.False(result); - Assert.True(command.Timestamp >= now && command.Timestamp <= DateTime.UtcNow); + Assert.Equal(utc, command.Timestamp); } [Fact] public async Task Should_do_nothing_for_normal_command() { - var utc = DateTime.Today; - var sut = new EnrichWithTimestampHandler(() => utc); + var sut = new EnrichWithTimestampHandler(clock.Object); var result = await sut.HandleAsync(new CommandContext(new MyNormalCommand())); Assert.False(result); + + clock.Verify(x => x.GetCurrentInstant(), Times.Never()); } } } diff --git a/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs b/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs index cd0c1b0d3..01f5fcf72 100644 --- a/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs @@ -7,6 +7,7 @@ // ========================================================================== using System; +using NodaTime; using Xunit; // ReSharper disable UnusedParameter.Local @@ -17,14 +18,14 @@ namespace Squidex.Infrastructure.Reflection { public class MyClass1Base { + public Guid MappedGuid { get; set; } + public string MappedString { get; set; } public string MappedNull { get; set; } public long MappedNumber { get; set; } - public Guid MappedGuid { get; set; } - public long WrongType1 { get; set; } public long WrongType2 { get; set; } @@ -53,9 +54,9 @@ namespace Squidex.Infrastructure.Reflection get { return "Value"; } } - public DateTime WrongType1 { get; set; } + public Instant WrongType1 { get; set; } - public TimeSpan WrongType2 { get; set; } + public Duration WrongType2 { get; set; } } [Fact] diff --git a/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs b/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs index aa9ee394a..aed05cd5b 100644 --- a/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs +++ b/tests/Squidex.Write.Tests/Apps/AppCommandHandlerTests.cs @@ -139,9 +139,7 @@ namespace Squidex.Write.Apps CreateApp(); - var timestamp = DateTime.Today; - - var context = CreateContextForCommand(new AttachClient { Id = clientName, Timestamp = timestamp }); + var context = CreateContextForCommand(new AttachClient { Id = clientName }); await TestUpdate(app, async _ => { diff --git a/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs b/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs index 3b45fc44a..121ab3787 100644 --- a/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs +++ b/tests/Squidex.Write.Tests/Apps/AppDomainObjectTests.cs @@ -218,11 +218,9 @@ namespace Squidex.Write.Apps [Fact] public void AttachClient_should_create_events() { - var now = DateTime.Today; - CreateApp(); - sut.AttachClient(CreateCommand(new AttachClient { Id = clientId, Timestamp = now }), clientSecret); + sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }), clientSecret); sut.GetUncomittedEvents() .ShouldHaveSameEvents( diff --git a/tools/GenerateLanguages/Program.cs b/tools/GenerateLanguages/Program.cs index ebe493473..2f4850679 100644 --- a/tools/GenerateLanguages/Program.cs +++ b/tools/GenerateLanguages/Program.cs @@ -24,7 +24,7 @@ namespace GenerateLanguages var writer = new StringWriter(); writer.WriteLine("// =========================================================================="); - writer.WriteLine("// Langauges.cs"); + writer.WriteLine("// Languages.cs"); writer.WriteLine("// Squidex Headless CMS"); writer.WriteLine("// =========================================================================="); writer.WriteLine("// Copyright (c) Squidex Group");