diff --git a/zero.Core/Api/ApiBase.cs b/zero.Core/Api/ApiBase.cs
deleted file mode 100644
index 6aca7896..00000000
--- a/zero.Core/Api/ApiBase.cs
+++ /dev/null
@@ -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;
- }
-
-
- ///
- /// Get an entity by Id
- ///
- protected async Task GetById(string id)
- {
- using (IAsyncDocumentSession session = Raven.OpenAsyncSession())
- {
- // TODO store-aware?
- return await session.LoadAsync(id);
- }
- }
-
-
-
- ///
- /// Get an entity by Id and transform the result
- ///
- protected async Task> Save(T model, IValidator 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.Fail(validation);
- }
- }
-
- // find all Raven Ids
- List> ravenIds = ObjectTraverser.FindAttribute(model);
-
- // set unset Raven Ids
- foreach (ObjectTraverser.Result 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.Success(model);
- }
-
-
- ///
- /// Deletes an entity by Id
- ///
- protected async Task> DeleteById(string id)
- {
- using (IAsyncDocumentSession session = Raven.OpenAsyncSession())
- {
- T entity = await session.LoadAsync(id);
-
- // TODO || !Store.Has(entity))
- if (entity == null)
- {
- return EntityResult.Fail("@errors.ondelete.idnotfound");
- }
-
- session.Delete(entity);
-
- await session.SaveChangesAsync();
-
- return EntityResult.Success();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/zero.Core/Api/BackofficeApi.cs b/zero.Core/Api/BackofficeApi.cs
index d61e0868..7146eb90 100644
--- a/zero.Core/Api/BackofficeApi.cs
+++ b/zero.Core/Api/BackofficeApi.cs
@@ -111,8 +111,9 @@ namespace zero.Core.Api
///
- public async Task> SaveModel(T model, IValidator validator = null, Action meta = null) where T : IZeroIdEntity
+ public async Task> SaveModel(T model, IValidator validator = null, Action 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.Success(model);
@@ -309,7 +313,7 @@ namespace zero.Core.Api
///
/// Updates or creates an entity with an optional validator
///
- Task> SaveModel(T model, IValidator validator = null, Action meta = null) where T : IZeroIdEntity;
+ Task> SaveModel(T model, IValidator validator = null, Action meta = null) where T : IZeroEntity;
///
/// Deletes an entity by Id
diff --git a/zero.Core/Entities/Country.cs b/zero.Core/Entities/Country.cs
index e5d335be..0bf270dd 100644
--- a/zero.Core/Entities/Country.cs
+++ b/zero.Core/Entities/Country.cs
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
{
public class Country : ZeroEntity, ICountry
{
- ///
- public string AppId { get; set; }
-
///
public bool IsPreferred { get; set; }
diff --git a/zero.Core/Entities/Language.cs b/zero.Core/Entities/Language.cs
index 1f3ee8e7..336a7532 100644
--- a/zero.Core/Entities/Language.cs
+++ b/zero.Core/Entities/Language.cs
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
{
public class Language : ZeroEntity, ILanguage
{
- ///
- public string AppId { get; set; }
-
///
public string Code { get; set; }
diff --git a/zero.Core/Entities/Media/Media.cs b/zero.Core/Entities/Media/Media.cs
index 08cc077c..0df91dab 100644
--- a/zero.Core/Entities/Media/Media.cs
+++ b/zero.Core/Entities/Media/Media.cs
@@ -6,9 +6,6 @@ namespace zero.Core.Entities
///
public class Media : ZeroEntity, IMedia
{
- ///
- public string AppId { get; set; }
-
///
public string FileId { get; set; }
diff --git a/zero.Core/Entities/Media/MediaFolder.cs b/zero.Core/Entities/Media/MediaFolder.cs
index fb8c2678..70bb5b87 100644
--- a/zero.Core/Entities/Media/MediaFolder.cs
+++ b/zero.Core/Entities/Media/MediaFolder.cs
@@ -5,9 +5,6 @@ namespace zero.Core.Entities
///
public class MediaFolder : ZeroEntity, IMediaFolder
{
- ///
- public string AppId { get; set; }
-
///
public string ParentId { get; set; }
}
diff --git a/zero.Core/Entities/Pages/Page.cs b/zero.Core/Entities/Pages/Page.cs
index 52ce6b0a..68a20038 100644
--- a/zero.Core/Entities/Pages/Page.cs
+++ b/zero.Core/Entities/Pages/Page.cs
@@ -16,9 +16,6 @@ namespace zero.Core.Entities
///
public string PageTypeAlias { get; set; }
- ///
- public string AppId { get; set; }
-
///
public DateTimeOffset? PublishDate { get; set; }
diff --git a/zero.Core/Entities/Preview.cs b/zero.Core/Entities/Preview.cs
index c6342f71..d0255f72 100644
--- a/zero.Core/Entities/Preview.cs
+++ b/zero.Core/Entities/Preview.cs
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
{
public class Preview : ZeroEntity, IPreview
{
- ///
- public string AppId { get; set; }
-
///
public string OriginalId { get; set; }
diff --git a/zero.Core/Entities/RecycleBin/RecycledEntity.cs b/zero.Core/Entities/RecycleBin/RecycledEntity.cs
index 385f25c1..c026d6d4 100644
--- a/zero.Core/Entities/RecycleBin/RecycledEntity.cs
+++ b/zero.Core/Entities/RecycleBin/RecycledEntity.cs
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
{
public class RecycledEntity : ZeroEntity, IRecycledEntity
{
- ///
- public string AppId { get; set; }
-
///
public string OriginalId { get; set; }
diff --git a/zero.Core/Entities/Spaces/SpaceContent.cs b/zero.Core/Entities/Spaces/SpaceContent.cs
index 1fcad4df..a6838da8 100644
--- a/zero.Core/Entities/Spaces/SpaceContent.cs
+++ b/zero.Core/Entities/Spaces/SpaceContent.cs
@@ -10,9 +10,6 @@ namespace zero.Core.Entities
{
///
public string SpaceAlias { get; set; }
-
- ///
- public string AppId { get; set; }
}
diff --git a/zero.Core/Entities/Translation.cs b/zero.Core/Entities/Translation.cs
index 717888d6..51428c7d 100644
--- a/zero.Core/Entities/Translation.cs
+++ b/zero.Core/Entities/Translation.cs
@@ -4,9 +4,6 @@ namespace zero.Core.Entities
{
public class Translation : ZeroEntity, ITranslation
{
- ///
- public string AppId { get; set; }
-
///
public string LanguageId { get; set; }
diff --git a/zero.Core/Entities/User/User.cs b/zero.Core/Entities/User/User.cs
index 118ffb0e..10d2e14e 100644
--- a/zero.Core/Entities/User/User.cs
+++ b/zero.Core/Entities/User/User.cs
@@ -6,9 +6,6 @@ namespace zero.Core.Entities
{
public class User : ZeroEntity, IUser, IZeroDbConventions
{
- ///
- public string AppId { get; set; }
-
///
public string CurrentAppId { get; set; }
diff --git a/zero.Core/Entities/User/UserRole.cs b/zero.Core/Entities/User/UserRole.cs
index ad768aeb..383415a8 100644
--- a/zero.Core/Entities/User/UserRole.cs
+++ b/zero.Core/Entities/User/UserRole.cs
@@ -5,9 +5,6 @@ namespace zero.Core.Entities
{
public class UserRole : ZeroEntity, IUserRole, IZeroDbConventions
{
- ///
- public string AppId { get; set; }
-
///
public string Description { get; set; }
diff --git a/zero.Core/Entities/ZeroEntity.cs b/zero.Core/Entities/ZeroEntity.cs
index 29a4a6ce..63ba2add 100644
--- a/zero.Core/Entities/ZeroEntity.cs
+++ b/zero.Core/Entities/ZeroEntity.cs
@@ -9,6 +9,9 @@ namespace zero.Core.Entities
///
public string Id { get; set; }
+ ///
+ public string AppId { get; set; }
+
///
public string Name { get; set; }
@@ -38,7 +41,7 @@ namespace zero.Core.Entities
}
- public interface IZeroEntity : IZeroIdEntity
+ public interface IZeroEntity : IZeroIdEntity, IAppAwareEntity
{
///
/// Full name of the entity
diff --git a/zero.Core/Extensions/ValidatorExtensions.cs b/zero.Core/Extensions/ValidatorExtensions.cs
index 615cc000..7b1509bd 100644
--- a/zero.Core/Extensions/ValidatorExtensions.cs
+++ b/zero.Core/Extensions/ValidatorExtensions.cs
@@ -101,7 +101,7 @@ namespace zero.Core.Extensions
///
/// Check if this value is unique within a collection
///
- public static IRuleBuilderOptions Unique(this IRuleBuilder ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity
+ public static IRuleBuilderOptions Unique(this IRuleBuilder 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()
- .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
///
/// Check if this value is at least set once to the expected value within a collection
///
- public static IRuleBuilderOptions ExpectAnyUnique(this IRuleBuilder ruleBuilder, IBackofficeStore store, TProperty expectedValue) where T : IZeroIdEntity
+ public static IRuleBuilderOptions ExpectAnyUnique(this IRuleBuilder 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()
- .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
///
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
///
- public static IRuleBuilderOptions Exists(this IRuleBuilder ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity
+ public static IRuleBuilderOptions Exists(this IRuleBuilder ruleBuilder, IBackofficeStore store) where T : IZeroEntity
{
return ruleBuilder.Exists(store);
}
@@ -152,7 +152,7 @@ namespace zero.Core.Extensions
///
/// Check if this reference exists and is an entity which can be referenced (appId = shared for shareable entities or appId = current)
///
- public static IRuleBuilderOptions Exists(this IRuleBuilder ruleBuilder, IBackofficeStore store) where T : IZeroIdEntity where TReference : IZeroIdEntity
+ public static IRuleBuilderOptions Exists(this IRuleBuilder 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;
diff --git a/zero.Core/SyncPseudo.cs b/zero.Core/SyncPseudo.cs
new file mode 100644
index 00000000..f56a72a1
--- /dev/null
+++ b/zero.Core/SyncPseudo.cs
@@ -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(IAsyncDocumentSession session, T model) where T : IZeroEntity
+ {
+ await OnSave(session, model, true);
+ }
+
+
+ public async Task OnUpdate(IAsyncDocumentSession session, T model) where T : IZeroEntity
+ {
+ await OnSave(session, model, false);
+ }
+
+
+ async Task OnSave(IAsyncDocumentSession session, T model, bool isCreate = false) where T : IZeroEntity
+ {
+ string sharedId = Constants.Database.SharedAppId;
+
+ if (model.AppId != sharedId)
+ {
+ return;
+ }
+
+ IList inheritedModels = new List();
+ IList apps = await session.Query().Where(x => x.Id != sharedId).ToListAsync();
+
+ if (!isCreate)
+ {
+ string id = model.Id;
+ inheritedModels = await session.Query().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);
+ }
+ }
+ }
+}
diff --git a/zero.Web.UI/App/components/tables/table-value.js b/zero.Web.UI/App/components/tables/table-value.js
index bd6258a9..4c3daab6 100644
--- a/zero.Web.UI/App/components/tables/table-value.js
+++ b/zero.Web.UI/App/components/tables/table-value.js
@@ -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 ? '' : '', true);
+ render(item.appId === zero.sharedAppId ? '' : '', true);
}
else
{
diff --git a/zero.Web/Defaults/ZeroBackofficePlugin.cs b/zero.Web/Defaults/ZeroBackofficePlugin.cs
index 23db6821..0cad09a5 100644
--- a/zero.Web/Defaults/ZeroBackofficePlugin.cs
+++ b/zero.Web/Defaults/ZeroBackofficePlugin.cs
@@ -75,7 +75,7 @@ namespace zero.Web.Defaults
zero.Sections.Add();
zero.Settings.AddGroup();
- zero.Settings.AddGroup();
+ //zero.Settings.AddGroup();
zero.Mapper.Add();
zero.Mapper.Add();