@@ -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
|
||||
};
|
||||
|
||||
@@ -98,28 +98,6 @@ namespace zero.Core.Collections
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetScope()
|
||||
{
|
||||
Database = Store.ResolvedDatabase;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Scoped(string scope, Func<Task> action)
|
||||
{
|
||||
if (scope.IsNullOrEmpty())
|
||||
{
|
||||
await action();
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyScope(scope);
|
||||
await action();
|
||||
ResetScope();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<T> GetById(string id)
|
||||
{
|
||||
@@ -460,16 +438,6 @@ namespace zero.Core.Collections
|
||||
/// </summary>
|
||||
void ApplyScope(string scope);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the scope to the initial value
|
||||
/// </summary>
|
||||
void ResetScope();
|
||||
|
||||
/// <summary>
|
||||
/// Execute scoped actions
|
||||
/// </summary>
|
||||
Task Scoped(string scope, Func<Task> action);
|
||||
|
||||
/// <summary>
|
||||
/// Get an entity by Id
|
||||
/// </summary>
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace zero.Core.Entities
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Ref ImageId { get; set; }
|
||||
public MediaRef ImageId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Ref IconId { get; set; }
|
||||
public MediaRef IconId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Uri[] Domains { get; set; } = new Uri[] { };
|
||||
@@ -53,12 +53,12 @@ namespace zero.Core.Entities
|
||||
/// <summary>
|
||||
/// Image of the application
|
||||
/// </summary>
|
||||
Ref ImageId { get; set; }
|
||||
MediaRef ImageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Simple image of the application (can be used as favicon)
|
||||
/// </summary>
|
||||
Ref IconId { get; set; }
|
||||
MediaRef IconId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// All assigned domains for this application
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public Ref Image { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,36 @@
|
||||
using Newtonsoft.Json;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Entities
|
||||
namespace zero.Core.Entities
|
||||
{
|
||||
public class Ref<T> : Ref where T : IZeroIdEntity
|
||||
{
|
||||
public Ref() : base() { }
|
||||
public Ref(string id) : base(id) { }
|
||||
|
||||
public static implicit operator Ref<T>(string id) => new Ref<T>(id);
|
||||
|
||||
public static implicit operator string(Ref<T> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace zero.Core.Entities
|
||||
{
|
||||
public class ValueRef<T> : ValueRef<T, string> where T : IZeroIdEntity
|
||||
{
|
||||
public ValueRef() : base() { }
|
||||
public ValueRef(string id, string value) : base(id, value) { }
|
||||
|
||||
public static implicit operator string(ValueRef<T> reference) => reference.Id;
|
||||
}
|
||||
|
||||
|
||||
public class ValueRef<TEntity, TValue> : 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<TEntity, TValue> 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();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace zero.Core.Entities
|
||||
public string SecurityStamp { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Ref AvatarId { get; set; }
|
||||
public MediaRef AvatarId { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string LanguageId { get; set; }
|
||||
@@ -99,7 +99,7 @@ namespace zero.Core.Entities
|
||||
/// <summary>
|
||||
/// Avatar image
|
||||
/// </summary>
|
||||
Ref AvatarId { get; set; }
|
||||
MediaRef AvatarId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backoffice display language
|
||||
|
||||
@@ -21,12 +21,12 @@ namespace zero.Core.Extensions
|
||||
public static T Clone<T>(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<T>(this object obj)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
|
||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj, new RefJsonConverter()), new RefJsonConverter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace zero.Core.Extensions
|
||||
{
|
||||
public static class RavenDocumentStoreExtensions
|
||||
{
|
||||
const char DOT = '.';
|
||||
/// <summary>
|
||||
/// Setup conventions for the document store
|
||||
/// </summary>
|
||||
@@ -24,7 +25,7 @@ namespace zero.Core.Extensions
|
||||
|
||||
Type dbConventionType = typeof(IZeroDbConventions);
|
||||
|
||||
store.Conventions.IdentityPartsSeparator = '.';
|
||||
store.Conventions.IdentityPartsSeparator = DOT;
|
||||
|
||||
store.Conventions.RegisterAsyncIdConvention<IZeroEntity>((_, 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;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: [Array, Object, String],
|
||||
type: [Array, String],
|
||||
default: null
|
||||
},
|
||||
disabled: {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Object, Array],
|
||||
type: [String, Array],
|
||||
default: null
|
||||
},
|
||||
disabled: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Media cache for repetitive queries within an HTTP request
|
||||
@@ -22,10 +21,10 @@ namespace zero.Web.ViewHelpers
|
||||
Dictionary<string, IMedia> Cache { get; set; } = new Dictionary<string, IMedia>();
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IMedia> 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<string, IMedia> remoteItems = await MediaCollection.GetByIds(remoteIds.ToArray());
|
||||
Dictionary<string, IMedia> remoteItems = await MediaApi.GetById(remoteIds);
|
||||
|
||||
foreach (var item in remoteItems)
|
||||
{
|
||||
@@ -103,39 +79,6 @@ namespace zero.Web.ViewHelpers
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<string, IMedia>> GetByIds(Ref[] references)
|
||||
{
|
||||
HashSet<Ref> remoteIds = new HashSet<Ref>();
|
||||
Dictionary<string, IMedia> items = new Dictionary<string, IMedia>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl(string id, bool isAbsolute = false)
|
||||
{
|
||||
@@ -144,28 +87,12 @@ namespace zero.Web.ViewHelpers
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl(Ref reference, bool isAbsolute = false)
|
||||
{
|
||||
IMedia media = await GetById(reference);
|
||||
return media?.Source;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<string, string>> GetUrls(string[] ids, bool isAbsolute = false)
|
||||
{
|
||||
Dictionary<string, IMedia> medias = await GetByIds(ids);
|
||||
return medias.ToDictionary(x => x.Key, x => x.Value?.Source);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<string, string>> GetUrls(Ref[] references, bool isAbsolute = false)
|
||||
{
|
||||
Dictionary<string, IMedia> 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
|
||||
/// </summary>
|
||||
Task<Dictionary<string, string>> GetUrls(string[] ids, bool isAbsolute = false);
|
||||
|
||||
/// <summary>
|
||||
/// Get media by Id
|
||||
/// </summary>
|
||||
Task<IMedia> GetById(Ref reference);
|
||||
|
||||
/// <summary>
|
||||
/// Get media items by Ids
|
||||
/// </summary>
|
||||
Task<Dictionary<string, IMedia>> GetByIds(Ref[] references);
|
||||
|
||||
/// <summary>
|
||||
/// Get source for a media item
|
||||
/// </summary>
|
||||
Task<string> GetUrl(Ref reference, bool isAbsolute = false);
|
||||
|
||||
/// <summary>
|
||||
/// Get source for media items
|
||||
/// </summary>
|
||||
Task<Dictionary<string, string>> GetUrls(Ref[] references, bool isAbsolute = false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user