using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Text.RegularExpressions; using Umbraco.Core.IO; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Strings; namespace Umbraco.Core.Models { /// /// Represents a Macro /// [Serializable] [DataContract(IsReference = true)] internal class Macro : Entity, IMacro { public Macro() { _properties = new MacroPropertyCollection(); _properties.CollectionChanged += PropertiesChanged; _addedProperties = new List(); _removedProperties = new List(); } /// /// Creates an item with pre-filled properties /// /// /// /// /// /// /// /// /// /// /// /// /// public Macro(int id, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) : this() { Id = id; UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; ControlAssembly = controlAssembly; XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; ScriptPath = scriptPath; } /// /// Creates an instance for persisting a new item /// /// /// /// /// /// /// /// /// /// /// /// public Macro(string @alias, string name, string controlType = "", string controlAssembly = "", string xsltPath = "", string scriptPath = "", bool cacheByPage = false, bool cacheByMember = false, bool dontRender = true, bool useInEditor = false, int cacheDuration = 0) : this() { UseInEditor = useInEditor; CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; ControlType = controlType; ControlAssembly = controlAssembly; XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; ScriptPath = scriptPath; } private string _alias; private string _name; private bool _useInEditor; private int _cacheDuration; private bool _cacheByPage; private bool _cacheByMember; private bool _dontRender; private string _scriptFile; private string _scriptAssembly; private string _scriptPath; private string _xslt; private MacroPropertyCollection _properties; private List _addedProperties; private List _removedProperties; private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); private static readonly PropertyInfo UseInEditorSelector = ExpressionHelper.GetPropertyInfo(x => x.UseInEditor); private static readonly PropertyInfo CacheDurationSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheDuration); private static readonly PropertyInfo CacheByPageSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByPage); private static readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); private static readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); private static readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); private static readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); private static readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); private static readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); private static readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) { OnPropertyChanged(PropertiesSelector); if (e.Action == NotifyCollectionChangedAction.Add) { //listen for changes var prop = e.NewItems.Cast().First(); prop.PropertyChanged += PropertyDataChanged; var alias = prop.Alias; if (_addedProperties.Contains(alias) == false) { //add to the added props _addedProperties.Add(alias); } } else if (e.Action == NotifyCollectionChangedAction.Remove) { //remove listening for changes var prop = e.OldItems.Cast().First(); prop.PropertyChanged -= PropertyDataChanged; var alias = prop.Alias; if (_removedProperties.Contains(alias) == false) { _removedProperties.Add(alias); } } } /// /// When some data of a property has changed ensure our Properties flag is dirty /// /// /// void PropertyDataChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(PropertiesSelector); } public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties) { _addedProperties.Clear(); _removedProperties.Clear(); base.ResetDirtyProperties(rememberPreviouslyChangedProperties); foreach (var prop in Properties) { ((TracksChangesEntityBase)prop).ResetDirtyProperties(rememberPreviouslyChangedProperties); } } /// /// Used internally to check if we need to add a section in the repository to the db /// internal IEnumerable AddedProperties { get { return _addedProperties; } } /// /// Used internally to check if we need to remove a section in the repository to the db /// internal IEnumerable RemovedProperties { get { return _removedProperties; } } /// /// Gets or sets the alias of the Macro /// [DataMember] public string Alias { get { return _alias; } set { SetPropertyValueAndDetectChanges(o => { _alias = value.ToCleanString(CleanStringType.Alias); return _alias; }, _alias, AliasSelector); } } /// /// Gets or sets the name of the Macro /// [DataMember] public string Name { get { return _name; } set { SetPropertyValueAndDetectChanges(o => { _name = value; return _name; }, _name, NameSelector); } } /// /// Gets or sets a boolean indicating whether the Macro can be used in an Editor /// [DataMember] public bool UseInEditor { get { return _useInEditor; } set { SetPropertyValueAndDetectChanges(o => { _useInEditor = value; return _useInEditor; }, _useInEditor, UseInEditorSelector); } } /// /// Gets or sets the Cache Duration for the Macro /// [DataMember] public int CacheDuration { get { return _cacheDuration; } set { SetPropertyValueAndDetectChanges(o => { _cacheDuration = value; return _cacheDuration; }, _cacheDuration, CacheDurationSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be Cached by Page /// [DataMember] public bool CacheByPage { get { return _cacheByPage; } set { SetPropertyValueAndDetectChanges(o => { _cacheByPage = value; return _cacheByPage; }, _cacheByPage, CacheByPageSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be Cached Personally /// [DataMember] public bool CacheByMember { get { return _cacheByMember; } set { SetPropertyValueAndDetectChanges(o => { _cacheByMember = value; return _cacheByMember; }, _cacheByMember, CacheByMemberSelector); } } /// /// Gets or sets a boolean indicating whether the Macro should be rendered in an Editor /// [DataMember] public bool DontRender { get { return _dontRender; } set { SetPropertyValueAndDetectChanges(o => { _dontRender = value; return _dontRender; }, _dontRender, DontRenderSelector); } } /// /// Gets or sets the path to user control or the Control Type to render /// [DataMember] public string ControlType { get { return _scriptFile; } set { SetPropertyValueAndDetectChanges(o => { _scriptFile = value; return _scriptFile; }, _scriptFile, ControlPathSelector); } } /// /// Gets or sets the name of the assembly, which should be used by the Macro /// /// Will usually only be filled if the ControlType is a Usercontrol [DataMember] public string ControlAssembly { get { return _scriptAssembly; } set { SetPropertyValueAndDetectChanges(o => { _scriptAssembly = value; return _scriptAssembly; }, _scriptAssembly, ControlAssemblySelector); } } /// /// Gets or set the path to the Python file in use /// /// Optional: Can only be one of three Script, Python or Xslt [DataMember] public string ScriptPath { get { return _scriptPath; } set { SetPropertyValueAndDetectChanges(o => { _scriptPath = value; return _scriptPath; }, _scriptPath, ScriptPathSelector); } } /// /// Gets or sets the path to the Xslt file in use /// /// Optional: Can only be one of three Script, Python or Xslt [DataMember] public string XsltPath { get { return _xslt; } set { SetPropertyValueAndDetectChanges(o => { _xslt = value; return _xslt; }, _xslt, XsltPathSelector); } } /// /// Gets or sets a list of Macro Properties /// [DataMember] public MacroPropertyCollection Properties { get { return _properties; } } public override object DeepClone() { var clone = (Macro)base.DeepClone(); clone._addedProperties = new List(); clone._removedProperties = new List(); clone._properties = (MacroPropertyCollection)Properties.DeepClone(); //re-assign the event handler clone._properties.CollectionChanged += clone.PropertiesChanged; clone.ResetDirtyProperties(false); return clone; } } }