diff --git a/zero.Core/Api/RevisionsApi.cs b/zero.Core/Api/RevisionsApi.cs index e416879b..1ddaab3d 100644 --- a/zero.Core/Api/RevisionsApi.cs +++ b/zero.Core/Api/RevisionsApi.cs @@ -81,7 +81,7 @@ namespace zero.Core.Api { revision.User = new RevisionUser() { - AvatarId = user.AvatarId?.Id, + AvatarId = user.AvatarId, Id = user.Id, Name = user.Name }; diff --git a/zero.Core/Collections/CollectionBase.cs b/zero.Core/Collections/CollectionBase.cs index 1ce1c61b..f3b56e78 100644 --- a/zero.Core/Collections/CollectionBase.cs +++ b/zero.Core/Collections/CollectionBase.cs @@ -98,28 +98,6 @@ namespace zero.Core.Collections } - /// - public void ResetScope() - { - Database = Store.ResolvedDatabase; - } - - - /// - public async Task Scoped(string scope, Func action) - { - if (scope.IsNullOrEmpty()) - { - await action(); - return; - } - - ApplyScope(scope); - await action(); - ResetScope(); - } - - /// public virtual async Task GetById(string id) { @@ -460,16 +438,6 @@ namespace zero.Core.Collections /// void ApplyScope(string scope); - /// - /// Resets the scope to the initial value - /// - void ResetScope(); - - /// - /// Execute scoped actions - /// - Task Scoped(string scope, Func action); - /// /// Get an entity by Id /// diff --git a/zero.Core/Entities/Applications/Application.cs b/zero.Core/Entities/Applications/Application.cs index ce6c7001..87bd341a 100644 --- a/zero.Core/Entities/Applications/Application.cs +++ b/zero.Core/Entities/Applications/Application.cs @@ -19,10 +19,10 @@ namespace zero.Core.Entities public string Email { get; set; } /// - public Ref ImageId { get; set; } + public MediaRef ImageId { get; set; } /// - public Ref IconId { get; set; } + public MediaRef IconId { get; set; } /// public Uri[] Domains { get; set; } = new Uri[] { }; @@ -53,12 +53,12 @@ namespace zero.Core.Entities /// /// Image of the application /// - Ref ImageId { get; set; } + MediaRef ImageId { get; set; } /// /// Simple image of the application (can be used as favicon) /// - Ref IconId { get; set; } + MediaRef IconId { get; set; } /// /// All assigned domains for this application diff --git a/zero.Core/Entities/PreviewModel.cs b/zero.Core/Entities/PreviewModel.cs index 7cbcd6a8..efeb61ca 100644 --- a/zero.Core/Entities/PreviewModel.cs +++ b/zero.Core/Entities/PreviewModel.cs @@ -6,8 +6,6 @@ public string Icon { get; set; } - public Ref Image { get; set; } - public string Text { get; set; } public string Name { get; set; } diff --git a/zero.Core/Entities/Refs/MediaRef.cs b/zero.Core/Entities/Refs/MediaRef.cs new file mode 100644 index 00000000..b610e7fb --- /dev/null +++ b/zero.Core/Entities/Refs/MediaRef.cs @@ -0,0 +1,17 @@ +namespace zero.Core.Entities +{ + public class MediaRef : Ref + { + public bool IsCore { get; set; } + + public MediaRef() : base() { } + public MediaRef(string id, bool isCore) : base(id) + { + IsCore = isCore; + } + + public static implicit operator string(MediaRef reference) => reference.Id; + + public static implicit operator MediaRef(string id) => new MediaRef(id, false); + } +} diff --git a/zero.Core/Entities/Refs/Ref.cs b/zero.Core/Entities/Refs/Ref.cs index c5a963f6..48c3478e 100644 --- a/zero.Core/Entities/Refs/Ref.cs +++ b/zero.Core/Entities/Refs/Ref.cs @@ -1,36 +1,36 @@ -using Newtonsoft.Json; -using zero.Core.Extensions; - -namespace zero.Core.Entities +namespace zero.Core.Entities { + public class Ref : Ref where T : IZeroIdEntity + { + public Ref() : base() { } + public Ref(string id) : base(id) { } + + public static implicit operator Ref(string id) => new Ref(id); + + public static implicit operator string(Ref reference) => reference.Id; + } + + public class Ref { - public string Id { get; set; } - - [JsonProperty("Core")] - public bool IsCore { get; set; } - public Ref() { } - public Ref(string id) : this() + public Ref(string id) { Id = id; } - public Ref(string id, bool isCore) : this(id) - { - IsCore = isCore; - } + public string Id { get; set; } public override string ToString() { - return (IsCore ? "core:" : string.Empty) + Id; + return Id; } - public static Ref Create(string id, bool isCore = false) - { - return id.IsNullOrWhiteSpace() ? null : new Ref(id, isCore); - } + + public static implicit operator Ref(string id) => new Ref(id); + + public static implicit operator string(Ref reference) => reference.Id; } } diff --git a/zero.Core/Entities/Refs/ValueRef.cs b/zero.Core/Entities/Refs/ValueRef.cs new file mode 100644 index 00000000..b3782db8 --- /dev/null +++ b/zero.Core/Entities/Refs/ValueRef.cs @@ -0,0 +1,42 @@ +namespace zero.Core.Entities +{ + public class ValueRef : ValueRef where T : IZeroIdEntity + { + public ValueRef() : base() { } + public ValueRef(string id, string value) : base(id, value) { } + + public static implicit operator string(ValueRef reference) => reference.Id; + } + + + public class ValueRef : Ref where TEntity : IZeroIdEntity + { + public ValueRef() : base() { } + public ValueRef(string id, TValue value) : base(id) + { + Value = value; + } + + public TValue Value { get; set; } + + public static implicit operator string(ValueRef reference) => reference.Id; + } + + //public class ValueRef : Ref + //{ + // public ValueRef() : base() { } + + // public ValueRef(string id, string value) : base(id) + // { + // Value = value; + // } + + // public string Value { get; set; } + + + // public override string ToString() + // { + // return (Id, Value).ToString(); + // } + //} +} diff --git a/zero.Core/Entities/User/BackofficeUser.cs b/zero.Core/Entities/User/BackofficeUser.cs index ebe1b1ec..74f426dc 100644 --- a/zero.Core/Entities/User/BackofficeUser.cs +++ b/zero.Core/Entities/User/BackofficeUser.cs @@ -32,7 +32,7 @@ namespace zero.Core.Entities public string SecurityStamp { get; set; } /// - public Ref AvatarId { get; set; } + public MediaRef AvatarId { get; set; } /// public string LanguageId { get; set; } @@ -99,7 +99,7 @@ namespace zero.Core.Entities /// /// Avatar image /// - Ref AvatarId { get; set; } + MediaRef AvatarId { get; set; } /// /// Backoffice display language diff --git a/zero.Core/Extensions/ObjectExtensions.cs b/zero.Core/Extensions/ObjectExtensions.cs index 34c5b613..c62b534c 100644 --- a/zero.Core/Extensions/ObjectExtensions.cs +++ b/zero.Core/Extensions/ObjectExtensions.cs @@ -21,12 +21,12 @@ namespace zero.Core.Extensions public static T Clone(this T obj) { Type type = obj.GetType(); - return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj), type); + return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj, new RefJsonConverter()), type, new RefJsonConverter()); } public static T CloneLax(this object obj) { - return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj)); + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj, new RefJsonConverter()), new RefJsonConverter()); } } } diff --git a/zero.Core/Extensions/RavenDocumentStoreExtensions.cs b/zero.Core/Extensions/RavenDocumentStoreExtensions.cs index 2bce6ed7..8f889893 100644 --- a/zero.Core/Extensions/RavenDocumentStoreExtensions.cs +++ b/zero.Core/Extensions/RavenDocumentStoreExtensions.cs @@ -15,6 +15,7 @@ namespace zero.Core.Extensions { public static class RavenDocumentStoreExtensions { + const char DOT = '.'; /// /// Setup conventions for the document store /// @@ -24,7 +25,7 @@ namespace zero.Core.Extensions Type dbConventionType = typeof(IZeroDbConventions); - store.Conventions.IdentityPartsSeparator = '.'; + store.Conventions.IdentityPartsSeparator = DOT; store.Conventions.RegisterAsyncIdConvention((_, entity) => { @@ -39,6 +40,15 @@ namespace zero.Core.Extensions return store.Conventions.AsyncDocumentIdGenerator(_, entity); }); + (store.Conventions.Serialization as NewtonsoftJsonSerializationConventions).CustomizeJsonDeserializer = x => + { + x.Converters.Add(new RefJsonConverter()); + }; + (store.Conventions.Serialization as NewtonsoftJsonSerializationConventions).CustomizeJsonSerializer = x => + { + x.Converters.Add(new RefJsonConverter()); + }; + store.Conventions.FindCollectionName = type => { Type finalType = type; diff --git a/zero.Core/Utils/RefJsonConverter.cs b/zero.Core/Utils/RefJsonConverter.cs new file mode 100644 index 00000000..fb4b7b17 --- /dev/null +++ b/zero.Core/Utils/RefJsonConverter.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using zero.Core.Entities; +using zero.Core.Extensions; + +namespace zero.Core.Utils +{ + public class RefJsonConverter : JsonConverter + { + private readonly Type type; + + public RefJsonConverter() + { + type = typeof(Ref<>); + } + + + public override bool CanRead => true; + + public override bool CanWrite => true; + + public override bool CanConvert(Type objectType) => objectType.IsGenericType && objectType.GetGenericTypeDefinition() == type; + + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + string id = null; + + if (value is Ref) + { + id = (value as Ref).Id; + } + + if (id.IsNullOrEmpty()) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(id); + } + } + + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (!(reader.Value is string)) + { + return null; + } + + if (((string)reader.Value).IsNullOrEmpty()) + { + return null; + } + + return Activator.CreateInstance(objectType, new object[1] { reader.Value }); + } + } +} diff --git a/zero.Web.UI/App/components/pickers/mediaPicker/mediapicker.vue b/zero.Web.UI/App/components/pickers/mediaPicker/mediapicker.vue index d168289e..63a207b7 100644 --- a/zero.Web.UI/App/components/pickers/mediaPicker/mediapicker.vue +++ b/zero.Web.UI/App/components/pickers/mediaPicker/mediapicker.vue @@ -69,7 +69,7 @@ props: { value: { - type: [Array, Object, String], + type: [Array, String], default: null }, disabled: { diff --git a/zero.Web.UI/App/editor/fields/media.vue b/zero.Web.UI/App/editor/fields/media.vue index dd8e20c3..a30d63e1 100644 --- a/zero.Web.UI/App/editor/fields/media.vue +++ b/zero.Web.UI/App/editor/fields/media.vue @@ -7,7 +7,7 @@ export default { props: { value: { - type: [String, Object, Array], + type: [String, Array], default: null }, disabled: { diff --git a/zero.Web.UI/app/api/media.js b/zero.Web.UI/app/api/media.js index 28a42953..5e0b09d8 100644 --- a/zero.Web.UI/app/api/media.js +++ b/zero.Web.UI/app/api/media.js @@ -23,11 +23,6 @@ const upload = async (file, folderId, onProgress, isTemporary) => const getImageSource = (id, thumb, shared) => { - if (typeof id === 'object') - { - shared = id.isCore; - id = id.id; - } if (!id || id.indexOf('http') === 0) { return id; diff --git a/zero.Web/Controllers/UsersController.cs b/zero.Web/Controllers/UsersController.cs index a75b4262..ed4f27de 100644 --- a/zero.Web/Controllers/UsersController.cs +++ b/zero.Web/Controllers/UsersController.cs @@ -47,7 +47,7 @@ namespace zero.Web.Controllers return Previews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel() { Id = item.Id, - Image = item.AvatarId, + Icon = item.AvatarId, Name = item.Name }); } diff --git a/zero.Web/Models/UserEditModel.cs b/zero.Web/Models/UserEditModel.cs index eb34831b..3425009b 100644 --- a/zero.Web/Models/UserEditModel.cs +++ b/zero.Web/Models/UserEditModel.cs @@ -14,7 +14,7 @@ namespace zero.Web.Models public bool IsEmailConfirmed { get; set; } - public Ref AvatarId { get; set; } + public string AvatarId { get; set; } public string LanguageId { get; set; } diff --git a/zero.Web/ViewHelpers/ZeroMediaHelper.cs b/zero.Web/ViewHelpers/ZeroMediaHelper.cs index 6282910e..c309a4ca 100644 --- a/zero.Web/ViewHelpers/ZeroMediaHelper.cs +++ b/zero.Web/ViewHelpers/ZeroMediaHelper.cs @@ -6,7 +6,6 @@ using zero.Core.Api; using zero.Core.Entities; using zero.Core.Routing; using zero.Core.Extensions; -using zero.Core.Collections; namespace zero.Web.ViewHelpers { @@ -14,7 +13,7 @@ namespace zero.Web.ViewHelpers { HttpContext HttpContext; - IMediaCollection MediaCollection; + IMediaApi MediaApi; /// /// Media cache for repetitive queries within an HTTP request @@ -22,10 +21,10 @@ namespace zero.Web.ViewHelpers Dictionary Cache { get; set; } = new Dictionary(); - public ZeroMediaHelper(IHttpContextAccessor httpContextAccessor, IMediaCollection mediaCollection) + public ZeroMediaHelper(IHttpContextAccessor httpContextAccessor, IMediaApi mediaApi) { HttpContext = httpContextAccessor.HttpContext; - MediaCollection = mediaCollection; + MediaApi = mediaApi; } @@ -39,30 +38,7 @@ namespace zero.Web.ViewHelpers if (!Cache.TryGetValue(id, out IMedia media)) { - media = await MediaCollection.GetById(id); - Cache.Add(id, media); - } - - return media; - } - - - /// - public async Task GetById(Ref reference) - { - if (reference == null) - { - return null; - } - - string id = reference.ToString(); - - if (!Cache.TryGetValue(id, out IMedia media)) - { - await MediaCollection.Scoped(reference.IsCore ? "shared" : null, async() => - { - media = await MediaCollection.GetById(reference.Id); - }); + media = await MediaApi.GetById(id); Cache.Add(id, media); } @@ -90,7 +66,7 @@ namespace zero.Web.ViewHelpers if (remoteIds.Count > 0) { - Dictionary remoteItems = await MediaCollection.GetByIds(remoteIds.ToArray()); + Dictionary remoteItems = await MediaApi.GetById(remoteIds); foreach (var item in remoteItems) { @@ -103,39 +79,6 @@ namespace zero.Web.ViewHelpers } - /// - public async Task> GetByIds(Ref[] references) - { - HashSet remoteIds = new HashSet(); - Dictionary items = new Dictionary(); - - foreach (Ref reference in references) - { - string id = reference.ToString(); - - if (Cache.TryGetValue(id, out IMedia media)) - { - items.Add(id, media); - } - else - { - remoteIds.Add(reference); - } - } - - if (remoteIds.Count > 0) - { - foreach (Ref reference in remoteIds) - { - // TODO this is super unperformant as we are switching scopes per request here - items.Add(reference.Id, await GetById(reference)); - } - } - - return items; - } - - /// public async Task GetUrl(string id, bool isAbsolute = false) { @@ -144,28 +87,12 @@ namespace zero.Web.ViewHelpers } - /// - public async Task GetUrl(Ref reference, bool isAbsolute = false) - { - IMedia media = await GetById(reference); - return media?.Source; - } - - /// public async Task> GetUrls(string[] ids, bool isAbsolute = false) { Dictionary medias = await GetByIds(ids); return medias.ToDictionary(x => x.Key, x => x.Value?.Source); } - - - /// - public async Task> GetUrls(Ref[] references, bool isAbsolute = false) - { - Dictionary medias = await GetByIds(references); - return medias.ToDictionary(x => x.Key, x => x.Value?.Source); - } } @@ -190,25 +117,5 @@ namespace zero.Web.ViewHelpers /// Get source for media items /// Task> GetUrls(string[] ids, bool isAbsolute = false); - - /// - /// Get media by Id - /// - Task GetById(Ref reference); - - /// - /// Get media items by Ids - /// - Task> GetByIds(Ref[] references); - - /// - /// Get source for a media item - /// - Task GetUrl(Ref reference, bool isAbsolute = false); - - /// - /// Get source for media items - /// - Task> GetUrls(Ref[] references, bool isAbsolute = false); } }