diff --git a/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs b/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs
new file mode 100644
index 0000000000..c72c2569a1
--- /dev/null
+++ b/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs
@@ -0,0 +1,13 @@
+using umbraco.interfaces;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// A cache refresher that supports refreshing or removing cache based on a custom Json payload
+ ///
+ interface IJsonCacheRefresher : ICacheRefresher
+ {
+ void Refresh(string jsonPayload);
+ void Remove(string jsonPayload);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 419e265e7f..0159621a9e 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -22,8 +22,8 @@ namespace Umbraco.Core.Models
private string _alias;
private string _description;
private int _sortOrder;
- private string _icon;
- private string _thumbnail;
+ private string _icon = "folder.png";
+ private string _thumbnail = "folder.png";
private int _creatorId;
private bool _allowedAsRoot;
private bool _isContainer;
diff --git a/src/Umbraco.Core/Models/ContentTypeExtensions.cs b/src/Umbraco.Core/Models/ContentTypeExtensions.cs
index b328cb5e0b..df8c09b3d1 100644
--- a/src/Umbraco.Core/Models/ContentTypeExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentTypeExtensions.cs
@@ -1,16 +1,17 @@
using System.Collections.Generic;
using System.Linq;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
- public static class ContentTypeExtensions
+ internal static class ContentTypeExtensions
{
///
/// Get all descendant content types
///
///
///
- public static IEnumerable Descendants(this IContentType contentType)
+ public static IEnumerable Descendants(this IContentTypeBase contentType)
{
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
@@ -23,13 +24,21 @@ namespace Umbraco.Core.Models
///
///
///
- public static IEnumerable DescendantsAndSelf(this IContentType contentType)
+ public static IEnumerable DescendantsAndSelf(this IContentTypeBase contentType)
{
- var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
- var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
- .FlattenList(type => contentTypeService.GetContentTypeChildren(type.Id));
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants());
return descendantsAndSelf;
}
+
+ /////
+ ///// Returns the descendant content type Ids for the given content type
+ /////
+ /////
+ /////
+ //public static IEnumerable DescendantIds(this IContentTypeBase contentType)
+ //{
+ // return ((ContentTypeService) ApplicationContext.Current.Services.ContentTypeService)
+ // .GetDescendantContentTypeIds(contentType.Id);
+ //}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs
index 3c58b61695..f5b228aad6 100644
--- a/src/Umbraco.Core/Models/EntityBase/Entity.cs
+++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Reflection;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.EntityBase
@@ -16,6 +17,15 @@ namespace Umbraco.Core.Models.EntityBase
private int? _hash;
private int _id;
private Guid _key;
+ private DateTime _createDate;
+ private DateTime _updateDate;
+
+ private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id);
+ private static readonly PropertyInfo KeySelector = ExpressionHelper.GetPropertyInfo(x => x.Key);
+ private static readonly PropertyInfo CreateDateSelector = ExpressionHelper.GetPropertyInfo(x => x.CreateDate);
+ private static readonly PropertyInfo UpdateDateSelector = ExpressionHelper.GetPropertyInfo(x => x.UpdateDate);
+ private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.HasIdentity);
+
///
/// Integer Id
@@ -29,8 +39,12 @@ namespace Umbraco.Core.Models.EntityBase
}
set
{
- _id = value;
- HasIdentity = true;
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _id = value;
+ HasIdentity = true; //set the has Identity
+ return _id;
+ }, _id, IdSelector);
}
}
@@ -49,20 +63,49 @@ namespace Umbraco.Core.Models.EntityBase
return _key;
}
- set { _key = value; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _key = value;
+ return _key;
+ }, _key, KeySelector);
+ }
}
///
/// Gets or sets the Created Date
///
[DataMember]
- public DateTime CreateDate { get; set; }
+ public DateTime CreateDate
+ {
+ get { return _createDate; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _createDate = value;
+ return _createDate;
+ }, _createDate, CreateDateSelector);
+ }
+ }
///
/// Gets or sets the Modified Date
///
[DataMember]
- public DateTime UpdateDate { get; set; }
+ public DateTime UpdateDate
+ {
+ get { return _updateDate; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _updateDate = value;
+ return _updateDate;
+ }, _updateDate, UpdateDateSelector);
+ }
+ }
internal virtual void ResetIdentity()
{
@@ -98,7 +141,11 @@ namespace Umbraco.Core.Models.EntityBase
}
protected set
{
- _hasIdentity = value;
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _hasIdentity = value;
+ return _hasIdentity;
+ }, _hasIdentity, HasIdentitySelector);
}
}
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index 7fefddfaed..978e54390f 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
public abstract class File : Entity, IFile
{
private string _path;
- private string _content;
+ private string _content = string.Empty; //initialize to empty string, not null
protected File(string path)
{
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
index f577e0f9db..179db898df 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories
///
///
internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase
- where TEntity : IContentTypeComposition
+ where TEntity : class, IContentTypeComposition
{
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work)
: base(work)
diff --git a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs
index f286392e87..a08dec6c76 100644
--- a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Repositories
///
///
internal abstract class PetaPocoRepositoryBase : RepositoryBase
- where TEntity : IAggregateRoot
+ where TEntity : class, IAggregateRoot
{
protected PetaPocoRepositoryBase(IDatabaseUnitOfWork work)
: base(work)
diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs
index 33b4ad392b..c7c5b236e6 100644
--- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// Type of entity for which the repository is used
/// Type of the Id used for this entity
internal abstract class RepositoryBase : DisposableObject, IRepositoryQueryable, IUnitOfWorkRepository
- where TEntity : IAggregateRoot
+ where TEntity : class, IAggregateRoot
{
private readonly IUnitOfWork _work;
private readonly IRepositoryCacheProvider _cache;
@@ -98,13 +98,17 @@ namespace Umbraco.Core.Persistence.Repositories
_cache.Save(typeof(TEntity), entity);
}
- //on initial construction we don't want to have dirty properties tracked
- // http://issues.umbraco.org/issue/U4-1946
- var asEntity = entity as Entity;
- if (asEntity != null)
+ if (entity != null)
{
- asEntity.ResetDirtyProperties(false);
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ Entity asEntity = entity as Entity;
+ if (asEntity != null)
+ {
+ asEntity.ResetDirtyProperties(false);
+ }
}
+
return entity;
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs
index 15266aae16..6364a106fb 100644
--- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs
@@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal abstract class VersionableRepositoryBase : PetaPocoRepositoryBase
- where TEntity : IAggregateRoot
+ where TEntity : class, IAggregateRoot
{
protected VersionableRepositoryBase(IDatabaseUnitOfWork work) : base(work)
{
diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs
index 3c81b7c80a..6afc43d9d8 100644
--- a/src/Umbraco.Core/Services/ContentTypeService.cs
+++ b/src/Umbraco.Core/Services/ContentTypeService.cs
@@ -9,6 +9,7 @@ using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -101,6 +102,26 @@ namespace Umbraco.Core.Services
}
}
+ /////
+ ///// Returns the content type descendant Ids for the content type specified
+ /////
+ /////
+ /////
+ //internal IEnumerable GetDescendantContentTypeIds(int contentTypeId)
+ //{
+ // using (var uow = _uowProvider.GetUnitOfWork())
+ // {
+ // //method to return the child content type ids for the id specified
+ // Func getChildIds =
+ // parentId =>
+ // uow.Database.Fetch("WHERE parentContentTypeId = @Id", new {Id = parentId})
+ // .Select(x => x.ChildId).ToArray();
+
+ // //recursively get all descendant ids
+ // return getChildIds(contentTypeId).FlattenList(getChildIds);
+ // }
+ //}
+
///
/// Checks whether an item has any children
///
@@ -120,20 +141,23 @@ namespace Umbraco.Core.Services
/// This is called after an IContentType is saved and is used to update the content xml structures in the database
/// if they are required to be updated.
///
- ///
+ /// A tuple of a content type and a boolean indicating if it is new (HasIdentity was false before committing)
private void UpdateContentXmlStructure(params IContentType[] contentTypes)
{
- var toUpdate = new List();
+ var toUpdate = new List();
foreach (var contentType in contentTypes)
{
//we need to determine if we need to refresh the xml content in the database. This is to be done when:
+ // - the item is not new (already existed in the db)
// - a content type changes it's alias
// - if a content type has it's property removed
//here we need to check if the alias of the content type changed or if one of the properties was removed.
var dirty = contentType as IRememberBeingDirty;
- if (dirty != null && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved")))
+ if (dirty != null
+ && !dirty.WasPropertyDirty("HasIdentity") //ensure it's now 'new'
+ && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved")))
{
//if the alias was changed then we only need to update the xml structures for content of the current content type.
//if a property was deleted then we need to update the xml structures for any content of the current content type
@@ -145,7 +169,7 @@ namespace Umbraco.Core.Services
}
else
{
- //if a property was deleted (and maybe the alias changed too), the update all content of the current content type
+ //if a property was deleted (and maybe the alias changed too), then update all content of the current content type
// and all of it's desscendant doc types.
toUpdate.AddRange(contentType.DescendantsAndSelf());
}
@@ -207,7 +231,6 @@ namespace Umbraco.Core.Services
using (new WriteLock(Locker))
{
-
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentTypeRepository(uow))
{
diff --git a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs
index f637fd995d..973bbb055d 100644
--- a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs
+++ b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs
@@ -56,6 +56,15 @@ namespace Umbraco.Core.Sync
_getUserNamePasswordDelegate = getUserNamePasswordDelegate;
}
+ public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload)
+ {
+ if (servers == null) throw new ArgumentNullException("servers");
+ if (refresher == null) throw new ArgumentNullException("refresher");
+ if (jsonPayload == null) throw new ArgumentNullException("jsonPayload");
+
+ MessageSeversForIdsOrJson(servers, refresher, MessageType.RefreshByJson, jsonPayload: jsonPayload);
+ }
+
public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher,Func getNumericId, params T[] instances)
{
if (servers == null) throw new ArgumentNullException("servers");
@@ -82,6 +91,15 @@ namespace Umbraco.Core.Sync
instances);
}
+ public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, string jsonPayload)
+ {
+ if (servers == null) throw new ArgumentNullException("servers");
+ if (refresher == null) throw new ArgumentNullException("refresher");
+ if (jsonPayload == null) throw new ArgumentNullException("jsonPayload");
+
+ MessageSeversForIdsOrJson(servers, refresher, MessageType.RemoveByJson, jsonPayload: jsonPayload);
+ }
+
public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances)
{
if (servers == null) throw new ArgumentNullException("servers");
@@ -100,7 +118,7 @@ namespace Umbraco.Core.Sync
if (servers == null) throw new ArgumentNullException("servers");
if (refresher == null) throw new ArgumentNullException("refresher");
- MessageSeversForManyIds(servers, refresher, MessageType.RemoveById, numericIds.Cast