using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models { /// /// Implementation of the for internal use. /// public class UmbracoEntity : Entity, IUmbracoEntity { private int _creatorId; private int _level; private string _name; private int _parentId; private string _path; private int _sortOrder; private bool _trashed; private bool _hasChildren; private bool _isPublished; private bool _isDraft; private bool _hasPendingChanges; private string _contentTypeAlias; private Guid _nodeObjectTypeId; private static readonly Lazy Ps = new Lazy(); private class PropertySelectors { public readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); public readonly PropertyInfo LevelSelector = ExpressionHelper.GetPropertyInfo(x => x.Level); public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId); public readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path); public readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); public readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); public readonly PropertyInfo HasChildrenSelector = ExpressionHelper.GetPropertyInfo(x => x.HasChildren); public readonly PropertyInfo IsPublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.IsPublished); public readonly PropertyInfo IsDraftSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDraft); public readonly PropertyInfo HasPendingChangesSelector = ExpressionHelper.GetPropertyInfo(x => x.HasPendingChanges); public readonly PropertyInfo ContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); public readonly PropertyInfo ContentTypeIconSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeIcon); public readonly PropertyInfo ContentTypeThumbnailSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeThumbnail); public readonly PropertyInfo NodeObjectTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeObjectTypeId); } private string _contentTypeIcon; private string _contentTypeThumbnail; public UmbracoEntity() { AdditionalData = new Dictionary(); } public UmbracoEntity(bool trashed) { AdditionalData = new Dictionary(); Trashed = trashed; } // for MySql public UmbracoEntity(UInt64 trashed) { AdditionalData = new Dictionary(); Trashed = trashed == 1; } public int CreatorId { get { return _creatorId; } set { SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector); } } public int Level { get { return _level; } set { SetPropertyValueAndDetectChanges(value, ref _level, Ps.Value.LevelSelector); } } public string Name { get { return _name; } set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); } } public int ParentId { get { return _parentId; } set { SetPropertyValueAndDetectChanges(value, ref _parentId, Ps.Value.ParentIdSelector); } } public string Path { get { return _path; } set { SetPropertyValueAndDetectChanges(value, ref _path, Ps.Value.PathSelector); } } public int SortOrder { get { return _sortOrder; } set { SetPropertyValueAndDetectChanges(value, ref _sortOrder, Ps.Value.SortOrderSelector); } } public bool Trashed { get { return _trashed; } private set { SetPropertyValueAndDetectChanges(value, ref _trashed, Ps.Value.TrashedSelector); } } public IDictionary AdditionalData { get; private set; } public bool HasChildren { get { return _hasChildren; } set { SetPropertyValueAndDetectChanges(value, ref _hasChildren, Ps.Value.HasChildrenSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["HasChildren"] = value; } } public bool IsPublished { get { return _isPublished; } set { SetPropertyValueAndDetectChanges(value, ref _isPublished, Ps.Value.IsPublishedSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["IsPublished"] = value; } } public bool IsDraft { get { return _isDraft; } set { SetPropertyValueAndDetectChanges(value, ref _isDraft, Ps.Value.IsDraftSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["IsDraft"] = value; } } public bool HasPendingChanges { get { return _hasPendingChanges; } set { SetPropertyValueAndDetectChanges(value, ref _hasPendingChanges, Ps.Value.HasPendingChangesSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["HasPendingChanges"] = value; } } public string ContentTypeAlias { get { return _contentTypeAlias; } set { SetPropertyValueAndDetectChanges(value, ref _contentTypeAlias, Ps.Value.ContentTypeAliasSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["ContentTypeAlias"] = value; } } public string ContentTypeIcon { get { return _contentTypeIcon; } set { SetPropertyValueAndDetectChanges(value, ref _contentTypeIcon, Ps.Value.ContentTypeIconSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["ContentTypeIcon"] = value; } } public string ContentTypeThumbnail { get { return _contentTypeThumbnail; } set { SetPropertyValueAndDetectChanges(value, ref _contentTypeThumbnail, Ps.Value.ContentTypeThumbnailSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["ContentTypeThumbnail"] = value; } } public Guid NodeObjectTypeId { get { return _nodeObjectTypeId; } set { SetPropertyValueAndDetectChanges(value, ref _nodeObjectTypeId, Ps.Value.NodeObjectTypeIdSelector); //This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data AdditionalData["NodeObjectTypeId"] = value; } } public override object DeepClone() { var clone = (UmbracoEntity) base.DeepClone(); //turn off change tracking clone.DisableChangeTracking(); //This ensures that any value in the dictionary that is deep cloneable is cloned too foreach (var key in clone.AdditionalData.Keys.ToArray()) { var deepCloneable = clone.AdditionalData[key] as IDeepCloneable; if (deepCloneable != null) { clone.AdditionalData[key] = deepCloneable.DeepClone(); } } //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); //re-enable tracking clone.EnableChangeTracking(); return clone; } /// /// A struction that can be contained in the additional data of an UmbracoEntity representing /// a user defined property /// public class EntityProperty : IDeepCloneable { public string PropertyEditorAlias { get; set; } public object Value { get; set; } public object DeepClone() { //Memberwise clone on Entity will work since it doesn't have any deep elements // for any sub class this will work for standard properties as well that aren't complex object's themselves. var clone = MemberwiseClone(); return clone; } protected bool Equals(EntityProperty other) { return PropertyEditorAlias.Equals(other.PropertyEditorAlias) && string.Equals(Value, other.Value); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((EntityProperty) obj); } public override int GetHashCode() { unchecked { return (PropertyEditorAlias.GetHashCode() * 397) ^ (Value != null ? Value.GetHashCode() : 0); } } } } }