using System; using System.Linq; using System.Reflection; using System.Runtime.Serialization; namespace Umbraco.Core.Models { /// /// Represents a Content object /// [Serializable] [DataContract(IsReference = true)] public class Content : ContentBase, IContent { private IContentType _contentType; private ITemplate _template; private bool _published; private string _language; private DateTime? _releaseDate; private DateTime? _expireDate; private int _writer; private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name. /// /// Constructor for creating a Content object /// /// Name of the content /// Parent object /// ContentType for the current Content object public Content(string name, IContent parent, IContentType contentType) : this(name, parent, contentType, new PropertyCollection()) { } /// /// Constructor for creating a Content object /// /// Name of the content /// Parent object /// ContentType for the current Content object /// Collection of properties public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties) : base(name, parent, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); _contentType = contentType; } /// /// Constructor for creating a Content object /// /// Name of the content /// Id of the Parent content /// ContentType for the current Content object public Content(string name, int parentId, IContentType contentType) : this(name, parentId, contentType, new PropertyCollection()) { } /// /// Constructor for creating a Content object /// /// Name of the content /// Id of the Parent content /// ContentType for the current Content object /// Collection of properties public Content(string name, int parentId, IContentType contentType, PropertyCollection properties) : base(name, parentId, contentType, properties) { Mandate.ParameterNotNull(contentType, "contentType"); _contentType = contentType; } private static readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo(x => x.Template); private static readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.Published); private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language); private static readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ReleaseDate); private static readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate); private static readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); private static readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeName); /// /// Gets or sets the template used by the Content. /// This is used to override the default one from the ContentType. /// /// /// If no template is explicitly set on the Content object, /// the Default template from the ContentType will be returned. /// [DataMember] public virtual ITemplate Template { get { if (_template == null) return _contentType.DefaultTemplate; return _template; } set { _template = value; OnPropertyChanged(TemplateSelector); } } /// /// Gets the current status of the Content /// [IgnoreDataMember] public ContentStatus Status { get { if(Trashed) return ContentStatus.Trashed; if(ExpireDate.HasValue && ExpireDate.Value > DateTime.MinValue && DateTime.Now > ExpireDate.Value) return ContentStatus.Expired; if(ReleaseDate.HasValue && ReleaseDate.Value > DateTime.MinValue && ReleaseDate.Value > DateTime.Now) return ContentStatus.AwaitingRelease; if(Published) return ContentStatus.Published; return ContentStatus.Unpublished; } } /// /// Boolean indicating whether this Content is Published or not /// /// Setting Published to true/false should be private or internal [DataMember] public bool Published { get { return _published; } internal set { _published = value; OnPropertyChanged(PublishedSelector); } } /// /// Language of the data contained within this Content object. /// /// /// Left internal until multilingual support is implemented. /// [DataMember] public string Language { get { return _language; } set { _language = value; OnPropertyChanged(LanguageSelector); } } /// /// The date this Content should be released and thus be published /// [DataMember] public DateTime? ReleaseDate { get { return _releaseDate; } set { _releaseDate = value; OnPropertyChanged(ReleaseDateSelector); } } /// /// The date this Content should expire and thus be unpublished /// [DataMember] public DateTime? ExpireDate { get { return _expireDate; } set { _expireDate = value; OnPropertyChanged(ExpireDateSelector); } } /// /// Id of the user who wrote/updated this Content /// [DataMember] public virtual int WriterId { get { return _writer; } set { _writer = value; OnPropertyChanged(WriterSelector); } } /// /// Name of the Node (non-localized). /// /// /// This Property is kept internal until localization is introduced. /// internal string NodeName { get { return _nodeName; } set { _nodeName = value; OnPropertyChanged(NodeNameSelector); } } /// /// Gets the ContentType used by this content object /// [IgnoreDataMember] public IContentType ContentType { get { return _contentType; } } /// /// Changes the for the current content object /// /// New ContentType for this content /// Leaves PropertyTypes intact after change public void ChangeContentType(IContentType contentType) { ContentTypeId = contentType.Id; _contentType = contentType; ContentTypeBase = contentType; Properties.EnsurePropertyTypes(PropertyTypes); Properties.CollectionChanged += PropertiesChanged; } /// /// Changes the for the current content object and removes PropertyTypes, /// which are not part of the new ContentType. /// /// New ContentType for this content /// Boolean indicating whether to clear PropertyTypes upon change public void ChangeContentType(IContentType contentType, bool clearProperties) { if(clearProperties) { ContentTypeId = contentType.Id; _contentType = contentType; ContentTypeBase = contentType; Properties.EnsureCleanPropertyTypes(PropertyTypes); Properties.CollectionChanged += PropertiesChanged; return; } ChangeContentType(contentType); } /// /// Changes the Published state of the content object /// public void ChangePublishedState(PublishedState state) { Published = state == PublishedState.Published; PublishedState = state; } internal PublishedState PublishedState { get; set; } /// /// Changes the Trashed state of the content object /// /// Boolean indicating whether content is trashed (true) or not trashed (false) /// public override void ChangeTrashedState(bool isTrashed, int parentId = -1) { Trashed = isTrashed; //If Content is trashed the parent id should be set to that of the RecycleBin if(isTrashed) { ParentId = -20; } else//otherwise set the parent id to the optional parameter, -1 being the fallback { ParentId = parentId; } //If the content is trashed and is published it should be marked as unpublished if (isTrashed && Published) { ChangePublishedState(PublishedState.Unpublished); } } /// /// Creates a clone of the current entity /// /// public IContent Clone() { var clone = (Content)this.MemberwiseClone(); clone.Key = Guid.Empty; clone.Version = Guid.NewGuid(); clone.ResetIdentity(); return clone; } /// /// Indicates whether a specific property on the current entity is dirty. /// /// Name of the property to check /// True if Property is dirty, otherwise False public override bool IsPropertyDirty(string propertyName) { bool existsInEntity = base.IsPropertyDirty(propertyName); if (existsInEntity) return true; return Properties.Any(x => x.IsPropertyDirty(propertyName)); } /// /// Indicates whether the current entity is dirty. /// /// True if entity is dirty, otherwise False public override bool IsDirty() { bool dirtyEntity = base.IsDirty(); bool dirtyProperties = Properties.Any(x => x.IsDirty()); return dirtyEntity || dirtyProperties; } /// /// Resets dirty properties by clearing the dictionary used to track changes. /// /// /// Please note that resetting the dirty properties could potentially /// obstruct the saving of a new or updated entity. /// public override void ResetDirtyProperties() { base.ResetDirtyProperties(); foreach (var property in Properties) { property.ResetDirtyProperties(); } } /// /// Method to call when Entity is being saved /// /// Created date is set and a Unique key is assigned internal override void AddingEntity() { base.AddingEntity(); if(Key == Guid.Empty) Key = Guid.NewGuid(); } /// /// Method to call when Entity is being updated /// /// Modified Date is set and a new Version guid is set internal override void UpdatingEntity() { base.UpdatingEntity(); Version = Guid.NewGuid(); } } }