start sharing of entities
This commit is contained in:
@@ -1,132 +0,0 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Attributes;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
using zero.Core.Utils;
|
||||
|
||||
namespace zero.Core.Api
|
||||
{
|
||||
public abstract class ApiBase
|
||||
{
|
||||
protected IDocumentStore Raven { get; set; }
|
||||
|
||||
protected const string ASTERISK = "*";
|
||||
|
||||
protected const string NEW_ID = "new:";
|
||||
|
||||
|
||||
public ApiBase(IDocumentStore raven)
|
||||
{
|
||||
Raven = raven;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get an entity by Id
|
||||
/// </summary>
|
||||
protected async Task<T> GetById<T>(string id)
|
||||
{
|
||||
using (IAsyncDocumentSession session = Raven.OpenAsyncSession())
|
||||
{
|
||||
// TODO store-aware?
|
||||
return await session.LoadAsync<T>(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get an entity by Id and transform the result
|
||||
/// </summary>
|
||||
protected async Task<EntityResult<T>> Save<T>(T model, IValidator<T> validator = null) where T : IZeroEntity
|
||||
{
|
||||
// check for alias
|
||||
//if (model is IUrlAliasEntity)
|
||||
//{
|
||||
// IUrlAliasEntity entity = operation.Model as IUrlAliasEntity;
|
||||
// entity.Alias = entity.Alias?.ToLower().ToUrlSegment();
|
||||
//}
|
||||
|
||||
// run validator
|
||||
if (validator != null)
|
||||
{
|
||||
ValidationResult validation = await validator.ValidateAsync(model);
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
return EntityResult<T>.Fail(validation);
|
||||
}
|
||||
}
|
||||
|
||||
// find all Raven Ids
|
||||
List<ObjectTraverser.Result<GenerateIdAttribute>> ravenIds = ObjectTraverser.FindAttribute<GenerateIdAttribute>(model);
|
||||
|
||||
// set unset Raven Ids
|
||||
foreach (ObjectTraverser.Result<GenerateIdAttribute> item in ravenIds)
|
||||
{
|
||||
string id = item.Property.GetValue(item.Parent, null) as string;
|
||||
if (String.IsNullOrWhiteSpace(id) || id.StartsWith(NEW_ID))
|
||||
{
|
||||
item.Property.SetValue(item.Parent, item.Item.Length.HasValue ? Raven.Id(item.Item.Length.Value) : Raven.Id());
|
||||
}
|
||||
}
|
||||
|
||||
// set default properties
|
||||
if (model.Id.IsNullOrEmpty())
|
||||
{
|
||||
model.CreatedDate = DateTimeOffset.Now;
|
||||
|
||||
if (model is IAppAwareEntity)
|
||||
{
|
||||
(model as IAppAwareEntity).AppId = Constants.Database.SharedAppId; // TODO correct app id
|
||||
}
|
||||
|
||||
if (model is ILanguageAwareEntity)
|
||||
{
|
||||
(model as ILanguageAwareEntity).LanguageId = "zero.languages.1-A"; // TODO correct language id
|
||||
}
|
||||
}
|
||||
|
||||
// update name alias
|
||||
model.Alias = Safenames.Alias(model.Name);
|
||||
|
||||
using (IAsyncDocumentSession session = Raven.OpenAsyncSession())
|
||||
{
|
||||
await session.StoreAsync(model);
|
||||
await session.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return EntityResult<T>.Success(model);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an entity by Id
|
||||
/// </summary>
|
||||
protected async Task<EntityResult<T>> DeleteById<T>(string id)
|
||||
{
|
||||
using (IAsyncDocumentSession session = Raven.OpenAsyncSession())
|
||||
{
|
||||
T entity = await session.LoadAsync<T>(id);
|
||||
|
||||
// TODO || !Store.Has(entity))
|
||||
if (entity == null)
|
||||
{
|
||||
return EntityResult<T>.Fail("@errors.ondelete.idnotfound");
|
||||
}
|
||||
|
||||
session.Delete(entity);
|
||||
|
||||
await session.SaveChangesAsync();
|
||||
|
||||
return EntityResult<T>.Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,8 +111,9 @@ namespace zero.Core.Api
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<EntityResult<T>> SaveModel<T>(T model, IValidator<T> validator = null, Action<IMetadataDictionary> meta = null) where T : IZeroIdEntity
|
||||
public async Task<EntityResult<T>> SaveModel<T>(T model, IValidator<T> validator = null, Action<IMetadataDictionary> meta = null) where T : IZeroEntity
|
||||
{
|
||||
bool isCreate = false;
|
||||
// check for alias
|
||||
//if (model is IUrlAliasEntity)
|
||||
//{
|
||||
@@ -122,7 +123,6 @@ namespace zero.Core.Api
|
||||
|
||||
// get specifics
|
||||
IAppAwareEntity appAwareEntity = model as IAppAwareEntity;
|
||||
IZeroEntity zeroEntity = model as IZeroEntity;
|
||||
|
||||
// run validator
|
||||
if (validator != null)
|
||||
@@ -194,11 +194,10 @@ namespace zero.Core.Api
|
||||
// set default properties
|
||||
if (model.Id.IsNullOrEmpty())
|
||||
{
|
||||
if (zeroEntity != null)
|
||||
{
|
||||
zeroEntity.CreatedDate = DateTimeOffset.Now;
|
||||
zeroEntity.CreatedById = userId;
|
||||
}
|
||||
isCreate = true;
|
||||
|
||||
model.CreatedDate = DateTimeOffset.Now;
|
||||
model.CreatedById = userId;
|
||||
|
||||
if (model is ILanguageAwareEntity)
|
||||
{
|
||||
@@ -207,22 +206,27 @@ namespace zero.Core.Api
|
||||
}
|
||||
|
||||
// update name alias and last modified
|
||||
if (zeroEntity != null)
|
||||
{
|
||||
zeroEntity.Alias = Safenames.Alias(zeroEntity.Name);
|
||||
zeroEntity.LastModifiedById = userId;
|
||||
zeroEntity.LastModifiedDate = DateTimeOffset.Now;
|
||||
zeroEntity.CreatedById ??= userId;
|
||||
}
|
||||
model.Alias = Safenames.Alias(model.Name);
|
||||
model.LastModifiedById = userId;
|
||||
model.LastModifiedDate = DateTimeOffset.Now;
|
||||
model.CreatedById ??= userId;
|
||||
|
||||
using IAsyncDocumentSession session = Raven.OpenAsyncSession();
|
||||
|
||||
session.Advanced.WaitForIndexesAfterSaveChanges(throwOnTimeout: false);
|
||||
|
||||
await session.StoreAsync(model);
|
||||
|
||||
meta?.Invoke(session.Advanced.GetMetadataFor(model));
|
||||
|
||||
if (isCreate)
|
||||
{
|
||||
await new SyncPseudo().OnCreate(session, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
await new SyncPseudo().OnUpdate(session, model);
|
||||
}
|
||||
|
||||
await session.SaveChangesAsync();
|
||||
|
||||
return EntityResult<T>.Success(model);
|
||||
@@ -309,7 +313,7 @@ namespace zero.Core.Api
|
||||
/// <summary>
|
||||
/// Updates or creates an entity with an optional validator
|
||||
/// </summary>
|
||||
Task<EntityResult<T>> SaveModel<T>(T model, IValidator<T> validator = null, Action<IMetadataDictionary> meta = null) where T : IZeroIdEntity;
|
||||
Task<EntityResult<T>> SaveModel<T>(T model, IValidator<T> validator = null, Action<IMetadataDictionary> meta = null) where T : IZeroEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an entity by Id
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class Country : ZeroEntity, ICountry
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPreferred { get; set; }
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class Language : ZeroEntity, ILanguage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Code { get; set; }
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ namespace zero.Core.Entities
|
||||
/// <inheritdoc />
|
||||
public class Media : ZeroEntity, IMedia
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FileId { get; set; }
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ namespace zero.Core.Entities
|
||||
/// <inheritdoc />
|
||||
public class MediaFolder : ZeroEntity, IMediaFolder
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ParentId { get; set; }
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace zero.Core.Entities
|
||||
/// <inheritdoc />
|
||||
public string PageTypeAlias { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset? PublishDate { get; set; }
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class Preview : ZeroEntity, IPreview
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string OriginalId { get; set; }
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class RecycledEntity : ZeroEntity, IRecycledEntity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string OriginalId { get; set; }
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string SpaceAlias { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class Translation : ZeroEntity, ITranslation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string LanguageId { get; set; }
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class User : ZeroEntity, IUser, IZeroDbConventions
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CurrentAppId { get; set; }
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ namespace zero.Core.Entities
|
||||
{
|
||||
public class UserRole : ZeroEntity, IUserRole, IZeroDbConventions
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Description { get; set; }
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace zero.Core.Entities
|
||||
/// <inheritdoc/>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -38,7 +41,7 @@ namespace zero.Core.Entities
|
||||
}
|
||||
|
||||
|
||||
public interface IZeroEntity : IZeroIdEntity
|
||||
public interface IZeroEntity : IZeroIdEntity, IAppAwareEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Full name of the entity
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace zero.Core.Extensions
|
||||
/// <summary>
|
||||
/// Check if this value is unique within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity
|
||||
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IBackofficeStore store) where T : IZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
@@ -110,7 +110,7 @@ namespace zero.Core.Extensions
|
||||
using IAsyncDocumentSession session = store.Raven.OpenAsyncSession();
|
||||
|
||||
bool any = await session.Advanced.AsyncDocumentQuery<T>()
|
||||
.Scope(store.AppContext.AppId, includeShared)
|
||||
.Scope(entity.AppId, false)
|
||||
.WhereNotEquals(nameof(IZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.Rule.PropertyName.ToPascalCaseId(), value)
|
||||
.AnyAsync(cancellation);
|
||||
@@ -123,7 +123,7 @@ namespace zero.Core.Extensions
|
||||
/// <summary>
|
||||
/// Check if this value is at least set once to the expected value within a collection
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, TProperty> ExpectAnyUnique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IBackofficeStore store, TProperty expectedValue) where T : IZeroIdEntity
|
||||
public static IRuleBuilderOptions<T, TProperty> ExpectAnyUnique<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder, IBackofficeStore store, TProperty expectedValue) where T : IZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, value, context, cancellation) =>
|
||||
{
|
||||
@@ -132,7 +132,7 @@ namespace zero.Core.Extensions
|
||||
using IAsyncDocumentSession session = store.Raven.OpenAsyncSession();
|
||||
|
||||
return await session.Advanced.AsyncDocumentQuery<T>()
|
||||
.Scope(store.AppContext.AppId, includeShared)
|
||||
.Scope(entity.AppId, includeShared)
|
||||
.WhereNotEquals(nameof(IZeroIdEntity.Id), entity.Id)
|
||||
.WhereEquals(context.Rule.PropertyName.ToPascalCaseId(), expectedValue)
|
||||
.AnyAsync(cancellation);
|
||||
@@ -143,7 +143,7 @@ namespace zero.Core.Extensions
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T>(this IRuleBuilder<T, string> ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity
|
||||
public static IRuleBuilderOptions<T, string> Exists<T>(this IRuleBuilder<T, string> ruleBuilder, IBackofficeStore store) where T : IZeroEntity
|
||||
{
|
||||
return ruleBuilder.Exists<T, T>(store);
|
||||
}
|
||||
@@ -152,7 +152,7 @@ namespace zero.Core.Extensions
|
||||
/// <summary>
|
||||
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Exists<T, TReference>(this IRuleBuilder<T, string> ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity where TReference : IZeroIdEntity
|
||||
public static IRuleBuilderOptions<T, string> Exists<T, TReference>(this IRuleBuilder<T, string> ruleBuilder, IBackofficeStore store) where T : IZeroEntity where TReference : IZeroEntity
|
||||
{
|
||||
return ruleBuilder.MustAsync(async (entity, id, context, cancellation) =>
|
||||
{
|
||||
@@ -168,7 +168,7 @@ namespace zero.Core.Extensions
|
||||
|
||||
if (typeof(IAppAwareEntity).IsAssignableFrom(typeof(TReference)))
|
||||
{
|
||||
return model != null && ((IAppAwareEntity)model).InScope(store.AppContext.AppId);
|
||||
return model != null && ((IAppAwareEntity)model).InScope(entity.AppId);
|
||||
}
|
||||
|
||||
return model != null;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core
|
||||
{
|
||||
public class SyncPseudo
|
||||
{
|
||||
public SyncPseudo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task OnCreate<T>(IAsyncDocumentSession session, T model) where T : IZeroEntity
|
||||
{
|
||||
await OnSave<T>(session, model, true);
|
||||
}
|
||||
|
||||
|
||||
public async Task OnUpdate<T>(IAsyncDocumentSession session, T model) where T : IZeroEntity
|
||||
{
|
||||
await OnSave<T>(session, model, false);
|
||||
}
|
||||
|
||||
|
||||
async Task OnSave<T>(IAsyncDocumentSession session, T model, bool isCreate = false) where T : IZeroEntity
|
||||
{
|
||||
string sharedId = Constants.Database.SharedAppId;
|
||||
|
||||
if (model.AppId != sharedId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IList<T> inheritedModels = new List<T>();
|
||||
IList<IApplication> apps = await session.Query<IApplication>().Where(x => x.Id != sharedId).ToListAsync();
|
||||
|
||||
if (!isCreate)
|
||||
{
|
||||
string id = model.Id;
|
||||
inheritedModels = await session.Query<T>().Where(x => x.BlueprintId == id).ToListAsync();
|
||||
}
|
||||
|
||||
foreach (IApplication app in apps)
|
||||
{
|
||||
T inheritedModel = inheritedModels.FirstOrDefault(x => x.AppId == app.Id);
|
||||
|
||||
// the model does not yet exist in the app, so we need to create it
|
||||
if (inheritedModel == null)
|
||||
{
|
||||
inheritedModel = model.Clone();
|
||||
inheritedModel.Id = null;
|
||||
}
|
||||
// we need to override allowed properties in the inherited model
|
||||
else
|
||||
{
|
||||
// TODO correctly override properties
|
||||
if (inheritedModel is ITranslation)
|
||||
{
|
||||
((ITranslation)inheritedModel).Key = ((ITranslation)model).Key;
|
||||
((ITranslation)inheritedModel).Value = ((ITranslation)model).Value;
|
||||
((ITranslation)inheritedModel).LastModifiedById = ((ITranslation)model).LastModifiedById;
|
||||
((ITranslation)inheritedModel).LastModifiedDate = ((ITranslation)model).LastModifiedDate;
|
||||
}
|
||||
}
|
||||
|
||||
inheritedModel.AppId = app.Id;
|
||||
inheritedModel.BlueprintId = model.Id;
|
||||
|
||||
await session.StoreAsync(inheritedModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export default function (el, binding)
|
||||
if (!column.as || column.as === 'text' || column.as === 'html')
|
||||
{
|
||||
const hasFunc = typeof column.render === 'function';
|
||||
const hasSharedIndicator = column.shared === true && !!item.blueprintId;
|
||||
const hasSharedIndicator = column.shared === true && item.appId === zero.sharedAppId;
|
||||
|
||||
let html = hasFunc ? column.render(item, column) : value;
|
||||
let isHtml = column.as === 'html' || hasSharedIndicator;
|
||||
@@ -87,7 +87,7 @@ export default function (el, binding)
|
||||
// render global flag
|
||||
else if (column.as === 'shared')
|
||||
{
|
||||
render(!!item.blueprintId ? '<i class="ui-table-field-shared fth-radio"></i>' : '', true);
|
||||
render(item.appId === zero.sharedAppId ? '<i class="ui-table-field-shared fth-radio"></i>' : '', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace zero.Web.Defaults
|
||||
zero.Sections.Add<SettingsSection>();
|
||||
|
||||
zero.Settings.AddGroup<SystemSettings>();
|
||||
zero.Settings.AddGroup<PluginSettings>();
|
||||
//zero.Settings.AddGroup<PluginSettings>();
|
||||
|
||||
zero.Mapper.Add<UserMapperConfig>();
|
||||
zero.Mapper.Add<CountryMapperConfig>();
|
||||
|
||||
Reference in New Issue
Block a user