diff --git a/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/PropertyGroupPropertiesSynchronization.cs b/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/PropertyGroupPropertiesSynchronization.cs index 5e139fe..aea4762 100644 --- a/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/PropertyGroupPropertiesSynchronization.cs +++ b/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/PropertyGroupPropertiesSynchronization.cs @@ -2,6 +2,7 @@ using System.Linq; using EPiServer.DataAbstraction; using EPiServer.Security; +using PageTypeBuilder; using PageTypeBuilder.Specs.Helpers.TypeBuildingDsl; using Machine.Specifications; @@ -17,10 +18,10 @@ public class when_a_page_type_property_is_annotated_with_property_group : Synchr Establish context = () => { var propertyGroupAttribute = new PageTypePropertyGroupAttribute - { - EditCaptionPrefix = "Property One - ", - StartSortOrderFrom = 100 - }; + { + EditCaptionPrefix = "Property One - ", + StartSortOrderFrom = 100 + }; SyncContext.CreateAndAddPageTypeClassToAppDomain(type => { @@ -72,9 +73,9 @@ public class when_a_page_type_property_is_annotated_with_property_group : Synchr SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-AltText")).Tab.ID.ShouldEqual(-1); } + [Subject("Synchronization")] public class when_a_page_type_property_is_annotated_with_property_group_with_tab_defined : SynchronizationSpecs { - static string propertyName = "PropertyOne"; static string pageTypeName = "PageTypeName"; @@ -97,7 +98,7 @@ public class when_a_page_type_property_is_annotated_with_property_group_with_tab prop.AddAttributeTemplate(propertyGroupAttribute); }); }); - + SyncContext.AssemblyLocator.Add(typeof(when_a_page_type_property_is_annotated_with_property_group).Assembly); }; @@ -113,6 +114,86 @@ public class when_a_page_type_property_is_annotated_with_property_group_with_tab SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-AltText")).Tab.ID.ShouldEqual(SyncContext.TabDefinitionRepository.List().Where(current => !string.IsNullOrEmpty(current.Name) && current.Name.Equals("Footer")).First().ID); } + [Subject("Synchronization")] + public class when_a_page_type_property_is_annotated_with_property_group_with_property_attribute_values_overridden : SynchronizationSpecs + { + static string propertyName = "PropertyOne"; + static string pageTypeName = "PageTypeName"; + + Establish context = () => + { + var propertyGroupAttribute = new PageTypePropertyGroupAttribute + { + EditCaptionPrefix = "Property One - ", + StartSortOrderFrom = 100, + Tab = typeof(FooterTab) + }; + + var pageTypePropertyOverrideAttribute = new PageTypePropertyGroupPropertyOverrideAttribute + { + PropertyName = "ImageUrl", + EditCaption = "New Caption", + DefaultValue = "NewDefaultValue", + DefaultValueType = DefaultValueType.Value, + DisplayInEditMode = false, + HelpText = "NewHelpText", + Required = true, + Searchable = true, + UniqueValuePerLanguage = true + }; + + SyncContext.CreateAndAddPageTypeClassToAppDomain(type => + { + type.Name = pageTypeName; + type.AddProperty(prop => + { + prop.Name = propertyName; + prop.Type = typeof(Image); + prop.AddAttributeTemplate(propertyGroupAttribute); + prop.AddAttributeTemplate(pageTypePropertyOverrideAttribute); + }); + }); + + SyncContext.AssemblyLocator.Add(typeof(when_a_page_type_property_is_annotated_with_property_group_with_property_attribute_values_overridden).Assembly); + + }; + + Because of = + () => SyncContext.PageTypeSynchronizer.SynchronizePageTypes(); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_edit_caption = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).EditCaption.ShouldEqual("Property One - New Caption"); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_default_value = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).DefaultValue.ShouldEqual("NewDefaultValue"); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_default_value_type = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).DefaultValueType.ShouldEqual(DefaultValueType.Value); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_display_edit_ui = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).DisplayEditUI.ShouldEqual(false); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_help_text = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).HelpText.ShouldEqual("NewHelpText"); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_required = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).Required.ShouldEqual(true); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_searchable = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).Searchable.ShouldEqual(true); + + It should_have_a_property_one_image_url_page_type_property_with_an_updated_unique_value_per_language = + () => + SyncContext.PageDefinitionRepository.List().FirstOrDefault(p => string.Equals(p.Name, "PropertyOne-ImageUrl")).LanguageSpecific.ShouldEqual(true); + } + public class Image : PageTypePropertyGroup { [PageTypeProperty(EditCaption = "Image Url", SortOrder = 0)] @@ -139,4 +220,5 @@ public override int SortIndex get { return 1000; } } } + } diff --git a/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/ValueSetting.cs b/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/ValueSetting.cs index 1f8592f..a9deda7 100644 --- a/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/ValueSetting.cs +++ b/PageTypeBuilder.Specs/Synchronization/PageTypeSynchronization/PropertySynchronization/ValueSetting.cs @@ -168,6 +168,9 @@ public class when_a_page_type_property_matches_existing_page_type_property_but_e PropertyInfo property = pageTypeType.GetProperty(propertyName); propertyAttribute = property.GetCustomAttributes(typeof(PageTypePropertyAttribute), false).First() as PageTypePropertyAttribute; + propertyAttribute.DefaultValue = "true"; + propertyAttribute.DefaultValueType = DefaultValueType.Value; + var existingPageDefinition = new PageDefinition(); existingPageDefinition.Type = SyncContext.PageDefinitionTypeRepository.GetPageDefinitionType(); existingPageDefinition.PageTypeID = existingPageType.ID; @@ -177,8 +180,10 @@ public class when_a_page_type_property_matches_existing_page_type_property_but_e existingPageDefinition.Tab = SyncContext.TabDefinitionRepository.GetTabDefinition(tabName); existingPageDefinition.Required = !propertyAttribute.Required; existingPageDefinition.Searchable = !propertyAttribute.Searchable; - existingPageDefinition.DefaultValue = "asdf"; - existingPageDefinition.DefaultValueType = DefaultValueType.Inherit; + + existingPageDefinition.DefaultValue = propertyAttribute.DefaultValue.ToString(); + existingPageDefinition.DefaultValueType = propertyAttribute.DefaultValueType; + existingPageDefinition.DisplayEditUI = !propertyAttribute.DisplayInEditMode; existingPageDefinition.FieldOrder = propertyAttribute.SortOrder + 100; existingPageDefinition.LanguageSpecific = !propertyAttribute.UniqueValuePerLanguage; diff --git a/PageTypeBuilder.Tests/Discovery/PageTypePropertyDefinitionTests.cs b/PageTypeBuilder.Tests/Discovery/PageTypePropertyDefinitionTests.cs index e843619..20570d6 100644 --- a/PageTypeBuilder.Tests/Discovery/PageTypePropertyDefinitionTests.cs +++ b/PageTypeBuilder.Tests/Discovery/PageTypePropertyDefinitionTests.cs @@ -16,8 +16,8 @@ public void GivenName_Constructor_SetsNameProperty() string name = TestValueUtility.CreateRandomString(); Type propertyType = typeof(string); IPageType pageType = new NativePageType(); - - PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute); + + PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute, null); Assert.Equal(name, definition.Name); @@ -31,7 +31,7 @@ public void GivenType_Constructor_SetsPropertyTypeProperty() Type propertyType = typeof(string); IPageType pageType = new NativePageType(); - PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute); + PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute, null); Assert.Equal(propertyType, definition.PropertyType); @@ -46,7 +46,7 @@ public void GivenPageType_Constructor_SetsPageTypeProperty() IPageType pageType = new NativePageType(); pageType.GUID = Guid.NewGuid(); - PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute); + PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute, null); Assert.Equal(pageType, definition.PageType, new PageTypeComparer()); @@ -73,10 +73,10 @@ public void GivenPageTypePropertyAttribute_Constructor_SetsPageTypePropertyAttri Type propertyType = typeof(string); IPageType pageType = new NativePageType(); - PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute); + PageTypePropertyDefinition definition = new PageTypePropertyDefinition(name, propertyType, pageType, attribute, null); - Assert.Equal(attribute, definition.PageTypePropertyAttribute, + Assert.Equal(attribute, definition.PageTypePropertyAttribute, new PageTypePropertyComparer()); } diff --git a/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/PageDefinitionSynchronizationEngineTestsUtility.cs b/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/PageDefinitionSynchronizationEngineTestsUtility.cs index fe8825f..5f19639 100644 --- a/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/PageDefinitionSynchronizationEngineTestsUtility.cs +++ b/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/PageDefinitionSynchronizationEngineTestsUtility.cs @@ -12,7 +12,7 @@ public static PageTypePropertyDefinition CreatePageTypePropertyDefinition() Type type = typeof(string); IPageType pageType = new NativePageType(); PageTypePropertyAttribute attribute = new PageTypePropertyAttribute(); - return new PageTypePropertyDefinition(name, type, pageType, attribute); + return new PageTypePropertyDefinition(name, type, pageType, attribute, null); } } } \ No newline at end of file diff --git a/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/UpdatePageDefinitionTests.cs b/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/UpdatePageDefinitionTests.cs index 6df9c1c..194ca31 100644 --- a/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/UpdatePageDefinitionTests.cs +++ b/PageTypeBuilder.Tests/Synchronization/PageDefinitionSynchronizationEngineTests/UpdatePageDefinitionTests.cs @@ -162,7 +162,11 @@ public void GivePropertyDefinition_UpdatePageDefinition_UpdatesPageDefinitionDef var pageDefinitionUpdater = GetPageDefinitionUpdater(); PageDefinition pageDefinitionToUpdate = new PageDefinition(); PageTypePropertyDefinition propertyDefinition = CreatePageTypePropertyDefinition(); - propertyDefinition.PageTypePropertyAttribute.DefaultValue = TestValueUtility.CreateRandomString(); + propertyDefinition.PageTypePropertyAttribute.DefaultValue = "true";// TestValueUtility.CreateRandomString(); + + // As we have had to modifiy how default value and value types are handled in definition update + // we now have to specify a default value to satisfy this test + propertyDefinition.PageTypePropertyAttribute.DefaultValueType = DefaultValueType.Value; pageDefinitionUpdater.UpdateExistingPageDefinition(pageDefinitionToUpdate, propertyDefinition); @@ -180,6 +184,10 @@ public void GivePropertyDefinition_UpdatePageDefinition_UpdatesPageDefinitionDef PageTypePropertyDefinition propertyDefinition = CreatePageTypePropertyDefinition(); propertyDefinition.PageTypePropertyAttribute.DefaultValueType = defaultValueType; + // As we have had to modifiy how default value and value types are handled in definition update + // we now have to specify a default value to satisfy this test + propertyDefinition.PageTypePropertyAttribute.DefaultValue = "true"; + pageDefinitionUpdater.UpdateExistingPageDefinition(pageDefinitionToUpdate, propertyDefinition); Assert.Equal(propertyDefinition.PageTypePropertyAttribute.DefaultValueType, pageDefinitionToUpdate.DefaultValueType); diff --git a/PageTypeBuilder.Tests/Synchronization/PageDefinitionTypeMapperTests.cs b/PageTypeBuilder.Tests/Synchronization/PageDefinitionTypeMapperTests.cs index 77f8b7c..4684ecf 100644 --- a/PageTypeBuilder.Tests/Synchronization/PageDefinitionTypeMapperTests.cs +++ b/PageTypeBuilder.Tests/Synchronization/PageDefinitionTypeMapperTests.cs @@ -107,7 +107,7 @@ public void GivenPageTypePropertyDefinitionWithNoTypeAndNonMappedPropertyType_Ge PageDefinitionTypeMapper mapper = new PageDefinitionTypeMapper(null, new NativePageDefinitionsMap()); Type unmappedType = typeof(StringBuilder); PageTypePropertyDefinition definition = new PageTypePropertyDefinition( - TestValueUtility.CreateRandomString(), unmappedType, new NativePageType(), new PageTypePropertyAttribute()); + TestValueUtility.CreateRandomString(), unmappedType, new NativePageType(), new PageTypePropertyAttribute(), null); Exception exception = Record.Exception(() => { mapper.GetPageDefinitionType(definition); }); diff --git a/PageTypeBuilder.Tests/Synchronization/PageTypePropertyDefinitionExtensionsTests.cs b/PageTypeBuilder.Tests/Synchronization/PageTypePropertyDefinitionExtensionsTests.cs index 4c2be38..e4b97ea 100644 --- a/PageTypeBuilder.Tests/Synchronization/PageTypePropertyDefinitionExtensionsTests.cs +++ b/PageTypeBuilder.Tests/Synchronization/PageTypePropertyDefinitionExtensionsTests.cs @@ -1,38 +1,35 @@ -using EPiServer.DataAbstraction; -using PageTypeBuilder.Abstractions; -using PageTypeBuilder.Discovery; -using PageTypeBuilder.Synchronization; -using PageTypeBuilder.Synchronization.PageDefinitionSynchronization; -using Xunit; - -namespace PageTypeBuilder.Tests.Synchronization -{ - public class PageTypePropertyDefinitionExtensionsTests - { - [Fact] - public void GivenPageTypePropertyDefinitionWithNoEditCaption_GetEditCaptionOrName_ReturnsName() - { - string propertyName = TestValueUtility.CreateRandomString(); - PageTypePropertyDefinition definition = - new PageTypePropertyDefinition(propertyName, typeof(string), new NativePageType(), new PageTypePropertyAttribute()); - - string returnedEditCaption = definition.GetEditCaptionOrName(); - - Assert.Equal(propertyName, returnedEditCaption); - } - - [Fact] - public void GivenPageTypePropertyDefinitionWithEditCaption_GetEditCaptionOrName_ReturnsEditCaptionFromAttribute() - { - PageTypePropertyDefinition definition = - new PageTypePropertyDefinition( - TestValueUtility.CreateRandomString(), typeof(string), new NativePageType(), new PageTypePropertyAttribute()); - string editCaption = TestValueUtility.CreateRandomString(); - definition.PageTypePropertyAttribute.EditCaption = editCaption; - - string returnedEditCaption = definition.GetEditCaptionOrName(); - - Assert.Equal(editCaption, returnedEditCaption); - } - } -} +namespace PageTypeBuilder.Tests.Synchronization +{ + using Abstractions; + using PageTypeBuilder.Discovery; + using PageTypeBuilder.Synchronization.PageDefinitionSynchronization; + using Xunit; + + public class PageTypePropertyDefinitionExtensionsTests + { + [Fact] + public void GivenPageTypePropertyDefinitionWithNoEditCaption_GetEditCaptionOrName_ReturnsName() + { + string propertyName = TestValueUtility.CreateRandomString(); + PageTypePropertyDefinition definition = + new PageTypePropertyDefinition(propertyName, typeof(string), new NativePageType(), new PageTypePropertyAttribute(), null); + + string returnedEditCaption = definition.GetEditCaptionOrName(false); + + Assert.Equal(propertyName, returnedEditCaption); + } + + [Fact] + public void GivenPageTypePropertyDefinitionWithEditCaption_GetEditCaptionOrName_ReturnsEditCaptionFromAttribute() + { + PageTypePropertyDefinition definition = + new PageTypePropertyDefinition(TestValueUtility.CreateRandomString(), typeof(string), new NativePageType(), new PageTypePropertyAttribute(), null); + string editCaption = TestValueUtility.CreateRandomString(); + definition.PageTypePropertyAttribute.EditCaption = editCaption; + + string returnedEditCaption = definition.GetEditCaptionOrName(false); + + Assert.Equal(editCaption, returnedEditCaption); + } + } +} diff --git a/PageTypeBuilder/Activation/PageTypePropertiesProxyGenerationHook.cs b/PageTypeBuilder/Activation/PageTypePropertiesProxyGenerationHook.cs index 05697a3..8870514 100644 --- a/PageTypeBuilder/Activation/PageTypePropertiesProxyGenerationHook.cs +++ b/PageTypeBuilder/Activation/PageTypePropertiesProxyGenerationHook.cs @@ -17,6 +17,7 @@ public void NonVirtualMemberNotification(Type type, System.Reflection.MemberInfo public bool ShouldInterceptMethod(Type type, System.Reflection.MethodInfo memberInfo) { return memberInfo.IsGetterOrSetterForPropertyWithAttribute(typeof(PageTypePropertyAttribute)) + && !memberInfo.IsGetterOrSetterForPropertyWithAttribute(typeof(PageTypePropertyGroupPropertyOverrideAttribute)) && memberInfo.IsCompilerGenerated(); } @@ -26,7 +27,7 @@ public void NonProxyableMemberNotification(Type type, System.Reflection.MemberIn public override bool Equals(object obj) { - return ((obj != null) && (obj.GetType() == typeof(PageTypePropertiesProxyGenerationHook))); + return ((obj != null) && (obj.GetType() == typeof(PageTypePropertiesProxyGenerationHook))); } public override int GetHashCode() diff --git a/PageTypeBuilder/Configuration/PageTypeBuilderConfiguration.cs b/PageTypeBuilder/Configuration/PageTypeBuilderConfiguration.cs index 1ad5c89..a4dbab4 100644 --- a/PageTypeBuilder/Configuration/PageTypeBuilderConfiguration.cs +++ b/PageTypeBuilder/Configuration/PageTypeBuilderConfiguration.cs @@ -1,17 +1,27 @@ -using System.Configuration; +using System.Collections.Generic; +using System.Configuration; namespace PageTypeBuilder.Configuration { public class PageTypeBuilderConfiguration : ConfigurationSection { + + public enum AssemblyVersionStamp + { + LastWriteTime, + Version + } + + private static PageTypeBuilderConfiguration _configuration; + public static PageTypeBuilderConfiguration GetConfiguration() { - PageTypeBuilderConfiguration configuration = ConfigurationManager.GetSection("pageTypeBuilder") as PageTypeBuilderConfiguration; + if (_configuration != null) + return _configuration; - if (configuration != null) - return configuration; + _configuration = ConfigurationManager.GetSection("pageTypeBuilder") as PageTypeBuilderConfiguration; - return new PageTypeBuilderConfiguration(); + return _configuration ?? new PageTypeBuilderConfiguration(); } [ConfigurationProperty("disablePageTypeUpdation", IsRequired = false)] @@ -23,6 +33,68 @@ public virtual bool DisablePageTypeUpdation } } + [ConfigurationProperty("oneTimeSynchornizationEnabled", IsRequired = false)] + public virtual bool OneTimeSynchornizationEnabled + { + get + { + return (bool)this["oneTimeSynchornizationEnabled"]; + } + } + + [ConfigurationProperty("oneTimeSynchornizationPollTime", IsRequired = false, DefaultValue = 2000)] + public virtual int OneTimeSynchornizationPollTime + { + get + { + return (int)this["oneTimeSynchornizationPollTime"]; + } + } + + [ConfigurationProperty("oneTimeSynchronizationLogFileDirectoryPath", IsRequired = false, DefaultValue = "")] + public virtual string OneTimeSynchronizationLogFileDirectoryPath + { + get + { + return (string)this["oneTimeSynchronizationLogFileDirectoryPath"]; + } + } + + [ConfigurationProperty("oneTimeSynchronizationAssemblyVersionStamp", IsRequired = false, DefaultValue = AssemblyVersionStamp.Version)] + public virtual AssemblyVersionStamp OneVersionSynchronizationAssemblyVersionStamp + { + get + { + return (AssemblyVersionStamp)this["oneTimeSynchronizationAssemblyVersionStamp"]; + } + } + + [ConfigurationProperty("timingsLogFileDirectoryPath", IsRequired = false, DefaultValue = "")] + public virtual string TimingsLogFileDirectoryPath + { + get + { + return (string)this["timingsLogFileDirectoryPath"]; + } + } + + [ConfigurationProperty("performValidation", IsRequired = false, DefaultValue = true)] + public virtual bool PerformValidation + { + get + { + return (bool)this["performValidation"]; + } + } + + [ConfigurationProperty("scanAssemblies", IsRequired = false)] + public ScanAssemblyCollection ScanAssemblies + { + + get { return ((ScanAssemblyCollection)(this["scanAssemblies"])); } + + } + [ConfigurationProperty("xmlns", IsRequired = false)] public string XmlNamespace { @@ -32,4 +104,47 @@ public string XmlNamespace } } } + + [ConfigurationCollection(typeof(ScanAssemblyElement))] + public class ScanAssemblyCollection : ConfigurationElementCollection + { + + List _elements = new List(); + + protected override ConfigurationElement CreateNewElement() + { + ScanAssemblyElement newElement = new ScanAssemblyElement(); + _elements.Add(newElement); + return newElement; + } + + protected override object GetElementKey(ConfigurationElement element) + { + return _elements.Find(e => e.Equals(element)); + } + + public new IEnumerator GetEnumerator() + { + return _elements.GetEnumerator(); + } + + } + + public class ScanAssemblyElement : ConfigurationElement + { + + [ConfigurationProperty("assemblyName", IsKey = true, IsRequired = true)] + public string AssemblyName + { + get + { + return ((string)(base["assemblyName"])); + } + set + { + base["assemblyName"] = value; + } + } + } + } diff --git a/PageTypeBuilder/Discovery/PageTypePropertyDefinition.cs b/PageTypeBuilder/Discovery/PageTypePropertyDefinition.cs index fdfd5e6..88fe304 100644 --- a/PageTypeBuilder/Discovery/PageTypePropertyDefinition.cs +++ b/PageTypeBuilder/Discovery/PageTypePropertyDefinition.cs @@ -1,22 +1,24 @@ -using System; -using EPiServer.DataAbstraction; -using PageTypeBuilder.Abstractions; - -namespace PageTypeBuilder.Discovery +namespace PageTypeBuilder.Discovery { + using System; + using Abstractions; + public class PageTypePropertyDefinition { - public PageTypePropertyDefinition(string name, Type propertyType, IPageType pageType, PageTypePropertyAttribute pageTypePropertyAttribute) + public PageTypePropertyDefinition(string name, Type propertyType, IPageType pageType, PageTypePropertyAttribute pageTypePropertyAttribute, + PageTypePropertyGroupPropertyOverrideAttribute pageTypePropertyGroupPropertyOverrideAttribute) { Name = name; PropertyType = propertyType; PageType = pageType; PageTypePropertyAttribute = pageTypePropertyAttribute; + PageTypePropertyGroupPropertyOverrideAttribute = pageTypePropertyGroupPropertyOverrideAttribute; } public string Name { get; set; } public Type PropertyType { get; set; } public IPageType PageType { get; set; } public PageTypePropertyAttribute PageTypePropertyAttribute { get; set; } + public PageTypePropertyGroupPropertyOverrideAttribute PageTypePropertyGroupPropertyOverrideAttribute { get; set; } } } \ No newline at end of file diff --git a/PageTypeBuilder/Discovery/PageTypePropertyDefinitionLocator.cs b/PageTypeBuilder/Discovery/PageTypePropertyDefinitionLocator.cs index 57635ad..512775b 100644 --- a/PageTypeBuilder/Discovery/PageTypePropertyDefinitionLocator.cs +++ b/PageTypeBuilder/Discovery/PageTypePropertyDefinitionLocator.cs @@ -19,9 +19,9 @@ public virtual IEnumerable GetPageTypePropertyDefini PageTypePropertyAttribute attribute = GetPageTypePropertyAttribute(property); if (attribute == null) - continue; + continue; - pageTypePropertyDefinitions.Add(new PageTypePropertyDefinition(property.Name, property.PropertyType, pageType, attribute)); + pageTypePropertyDefinitions.Add(new PageTypePropertyDefinition(property.Name, property.PropertyType, pageType, attribute, null)); } // add all page type group property definitions @@ -54,6 +54,13 @@ internal T GetPropertyAttribute(PropertyInfo propertyInfo) return propertyInfo.GetCustomAttributes().FirstOrDefault(); } + internal PageTypePropertyGroupPropertyOverrideAttribute GetPropertyGroupPropertyOverrideAttribute(PropertyInfo propertyGroupProperty, PropertyInfo property) + { + return propertyGroupProperty + .GetCustomAttributes() + .FirstOrDefault(c => string.Equals(c.PropertyName, property.Name)); + } + private IEnumerable GetPropertyGroupPropertyDefinitions(IPageType pageType, PropertyInfo propertyGroupProperty) { PageTypePropertyGroupAttribute groupAttribute = GetPropertyAttribute(propertyGroupProperty); @@ -69,7 +76,12 @@ private IEnumerable GetPropertyGroupPropertyDefiniti string resolvedPropertyName = PageTypePropertyGroupHierarchy.ResolvePropertyName(propertyGroupProperty.Name, property.Name); attribute = AdjustPropertyGroupAttributeProperties(attribute, groupAttribute); - yield return new PageTypePropertyDefinition(resolvedPropertyName, property.PropertyType, pageType, attribute); + PageTypePropertyGroupPropertyOverrideAttribute overrideAttribute = GetPropertyGroupPropertyOverrideAttribute(propertyGroupProperty, property); + + if (overrideAttribute != null) + overrideAttribute = AdjustPropertyGroupAttributeProperties(overrideAttribute, groupAttribute) as PageTypePropertyGroupPropertyOverrideAttribute; + + yield return new PageTypePropertyDefinition(resolvedPropertyName, property.PropertyType, pageType, attribute, overrideAttribute); } } @@ -77,10 +89,12 @@ private PageTypePropertyAttribute AdjustPropertyGroupAttributeProperties(PageTyp { if (groupAttribute != null) { - if (!string.IsNullOrEmpty(groupAttribute.EditCaptionPrefix)) + bool overriding = (attribute is PageTypePropertyGroupPropertyOverrideAttribute); + + if (!string.IsNullOrEmpty(groupAttribute.EditCaptionPrefix) && (!overriding || attribute.EditCaptionSet)) attribute.EditCaption = groupAttribute.EditCaptionPrefix + attribute.EditCaption; - if (groupAttribute.StartSortOrderFrom > 0) + if (!overriding && groupAttribute.StartSortOrderFrom > 0) attribute.SortOrder = groupAttribute.StartSortOrderFrom + attribute.SortOrder; } return attribute; diff --git a/PageTypeBuilder/Discovery/TabLocator.cs b/PageTypeBuilder/Discovery/TabLocator.cs index 6df7a1c..9bfb4db 100644 --- a/PageTypeBuilder/Discovery/TabLocator.cs +++ b/PageTypeBuilder/Discovery/TabLocator.cs @@ -9,6 +9,11 @@ public class TabLocator { private IAssemblyLocator assemblyLocator; + public IAssemblyLocator AssemblyLocator + { + get { return assemblyLocator; } + } + public TabLocator(IAssemblyLocator assemblyLocator) { this.assemblyLocator = assemblyLocator; diff --git a/PageTypeBuilder/Initializer.cs b/PageTypeBuilder/Initializer.cs index e87ff4f..0fc2a7a 100644 --- a/PageTypeBuilder/Initializer.cs +++ b/PageTypeBuilder/Initializer.cs @@ -1,20 +1,15 @@ -using Autofac; -using EPiServer; -using EPiServer.Core.PropertySettings; -using EPiServer.Framework; -using EPiServer.Framework.Initialization; -using PageTypeBuilder.Abstractions; -using PageTypeBuilder.Configuration; -using PageTypeBuilder.Discovery; -using PageTypeBuilder.Reflection; -using PageTypeBuilder.Synchronization; -using PageTypeBuilder.Synchronization.Validation; -using InitializationModule=EPiServer.Web.InitializationModule; - -namespace PageTypeBuilder +namespace PageTypeBuilder { + using Autofac; + using EPiServer; + using EPiServer.Framework; + using EPiServer.Framework.Initialization; + using Configuration; + using Synchronization; + using InitializationModule = EPiServer.Web.InitializationModule; + [ModuleDependency(typeof(InitializationModule))] - public class Initializer : IInitializableModule + public class Initializer : IInitializableModule { public void Initialize(InitializationEngine context) { @@ -22,9 +17,14 @@ public void Initialize(InitializationEngine context) var defaultBootstrapper = new DefaultBootstrapper(); defaultBootstrapper.Configure(containerBuilder); var container = containerBuilder.Build(); - + PageTypeSynchronizer synchronizer = container.Resolve(); - synchronizer.SynchronizePageTypes(); + TimingsLogger.Clear(); + + using (new TimingsLogger("Total time to synchronize")) + { + synchronizer.SynchronizePageTypes(); + } DataFactory.Instance.LoadedPage += DataFactory_LoadedPage; DataFactory.Instance.LoadedChildren += DataFactory_LoadedChildren; @@ -45,7 +45,7 @@ public void Uninitialize(InitializationEngine context) static void DataFactory_LoadedPage(object sender, PageEventArgs e) { - if(e.Page == null) + if (e.Page == null) return; e.Page = PageTypeResolver.Instance.ConvertToTyped(e.Page); diff --git a/PageTypeBuilder/PageTypeBuilder.csproj b/PageTypeBuilder/PageTypeBuilder.csproj index de5f535..d116615 100644 --- a/PageTypeBuilder/PageTypeBuilder.csproj +++ b/PageTypeBuilder/PageTypeBuilder.csproj @@ -75,6 +75,10 @@ False c:\Program Files (x86)\EPiServer\CMS\6.0.530.0\bin\EPiServer.BaseLibrary.dll + + False + c:\Program Files (x86)\EPiServer\Framework\6.0.318.113\bin\EPiServer.Data.dll + False c:\Program Files (x86)\EPiServer\Framework\6.0.318.113\bin\EPiServer.Framework.dll @@ -146,6 +150,7 @@ + @@ -159,6 +164,7 @@ + @@ -191,6 +197,7 @@ + ASPXCodeBehind diff --git a/PageTypeBuilder/PageTypePropertyGroupPropertyOverrideAttribute.cs b/PageTypeBuilder/PageTypePropertyGroupPropertyOverrideAttribute.cs new file mode 100644 index 0000000..285d61b --- /dev/null +++ b/PageTypeBuilder/PageTypePropertyGroupPropertyOverrideAttribute.cs @@ -0,0 +1,10 @@ +namespace PageTypeBuilder +{ + using System; + + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)] + public class PageTypePropertyGroupPropertyOverrideAttribute : PageTypePropertyAttribute + { + public string PropertyName { get; set; } + } +} diff --git a/PageTypeBuilder/Reflection/AppDomainAssemblyLocator.cs b/PageTypeBuilder/Reflection/AppDomainAssemblyLocator.cs index b1b1dfc..12fc039 100644 --- a/PageTypeBuilder/Reflection/AppDomainAssemblyLocator.cs +++ b/PageTypeBuilder/Reflection/AppDomainAssemblyLocator.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using PageTypeBuilder.Configuration; namespace PageTypeBuilder.Reflection { @@ -8,7 +10,13 @@ public class AppDomainAssemblyLocator : IAssemblyLocator { public IEnumerable GetAssemblies() { - return AppDomain.CurrentDomain.GetAssemblies(); + ScanAssemblyCollection scanAssemblies = PageTypeBuilderConfiguration.GetConfiguration().ScanAssemblies; + + if (scanAssemblies == null || scanAssemblies.Count == 0) + return AppDomain.CurrentDomain.GetAssemblies(); + + return AppDomain.CurrentDomain.GetAssemblies() + .Where(c => scanAssemblies.Cast().Any(a => string.Equals(c.GetName().Name, a.AssemblyName))); } } } \ No newline at end of file diff --git a/PageTypeBuilder/Reflection/TypeExtensions.cs b/PageTypeBuilder/Reflection/TypeExtensions.cs index a73f348..009c8b5 100644 --- a/PageTypeBuilder/Reflection/TypeExtensions.cs +++ b/PageTypeBuilder/Reflection/TypeExtensions.cs @@ -14,7 +14,9 @@ public static PropertyInfo[] GetPublicOrPrivateProperties(this Type type) internal static IEnumerable GetPageTypePropertiesOnClass(this Type pageTypeType) { - return pageTypeType.GetPublicOrPrivateProperties().Where(propertyInfo => propertyInfo.HasAttribute(typeof(PageTypePropertyAttribute))); + return pageTypeType.GetPublicOrPrivateProperties() + .Where(propertyInfo => propertyInfo.HasAttribute(typeof(PageTypePropertyAttribute)) && + !propertyInfo.HasAttribute(typeof(PageTypePropertyGroupPropertyOverrideAttribute))); } internal static IEnumerable GetPageTypePropertyGroupProperties(this Type pageTypeType) diff --git a/PageTypeBuilder/Synchronization/GlobalPropertySettingsSynchronizer.cs b/PageTypeBuilder/Synchronization/GlobalPropertySettingsSynchronizer.cs index 260f550..7868c6f 100644 --- a/PageTypeBuilder/Synchronization/GlobalPropertySettingsSynchronizer.cs +++ b/PageTypeBuilder/Synchronization/GlobalPropertySettingsSynchronizer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using EPiServer.Core.PropertySettings; using PageTypeBuilder.Discovery; @@ -9,6 +10,7 @@ public class GlobalPropertySettingsSynchronizer { private Func _propertySettingsRepositoryMethod; private IGlobalPropertySettingsLocator globalPropertySettingsLocator; + internal List globalSettingsIds = new List(); public GlobalPropertySettingsSynchronizer(Func propertySettingsRepositoryMethod, IGlobalPropertySettingsLocator globalPropertySettingsLocator) { @@ -19,6 +21,7 @@ public GlobalPropertySettingsSynchronizer(Func prop public void Synchronize() { var updaters = globalPropertySettingsLocator.GetGlobalPropertySettingsUpdaters(); + foreach (var updater in updaters) { var existingWrappers = _propertySettingsRepositoryMethod().GetGlobals(updater.SettingsType); @@ -27,15 +30,20 @@ public void Synchronize() matchingWrappers.ForEach(wrapper => { var existingWrapperValues = WrapperValuesSerialized(updater, wrapper); + if (updater.OverWriteExisting) { updater.UpdateSettings(wrapper.PropertySettings); } + UpdateWrapperValues(updater, wrapper); var isChanged = !WrapperValuesSerialized(updater, wrapper).Equals(existingWrapperValues); + if (updater.OverWriteExisting && isChanged) { _propertySettingsRepositoryMethod().SaveGlobal(wrapper); + globalSettingsIds.Add(wrapper.Id); + //globalSettingsIds.Add(wrapper.PropertySettings.Id); } }); diff --git a/PageTypeBuilder/Synchronization/IPageDefinitionUpdater.cs b/PageTypeBuilder/Synchronization/IPageDefinitionUpdater.cs index 6c31f6b..82d9804 100644 --- a/PageTypeBuilder/Synchronization/IPageDefinitionUpdater.cs +++ b/PageTypeBuilder/Synchronization/IPageDefinitionUpdater.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using EPiServer.DataAbstraction; using PageTypeBuilder.Discovery; @@ -5,6 +6,7 @@ namespace PageTypeBuilder.Synchronization { public interface IPageDefinitionUpdater { + List UpdatedPageDefinitions { get; } void UpdateExistingPageDefinition(PageDefinition pageDefinition, PageTypePropertyDefinition pageTypePropertyDefinition); void CreateNewPageDefinition(PageTypePropertyDefinition propertyDefinition); } diff --git a/PageTypeBuilder/Synchronization/IPageTypeLocator.cs b/PageTypeBuilder/Synchronization/IPageTypeLocator.cs index 3fda402..0785747 100644 --- a/PageTypeBuilder/Synchronization/IPageTypeLocator.cs +++ b/PageTypeBuilder/Synchronization/IPageTypeLocator.cs @@ -1,11 +1,11 @@ -using EPiServer.DataAbstraction; -using PageTypeBuilder.Abstractions; +using PageTypeBuilder.Abstractions; using PageTypeBuilder.Discovery; namespace PageTypeBuilder.Synchronization { public interface IPageTypeLocator { + IPageTypeRepository PageTypeRepository { get; } IPageType GetExistingPageType(PageTypeDefinition definition); } } diff --git a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSpecificPropertySettingsUpdater.cs b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSpecificPropertySettingsUpdater.cs index 714a8b2..295eff2 100644 --- a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSpecificPropertySettingsUpdater.cs +++ b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSpecificPropertySettingsUpdater.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using EPiServer.Core.PropertySettings; @@ -14,6 +15,7 @@ public class PageDefinitionSpecificPropertySettingsUpdater Func _propertySettingsRepositoryMethod; IGlobalPropertySettingsLocator globalPropertySettingsLocator; IPageDefinitionRepository pageDefinitionRepository; + internal List updatedPropertySettingsCacheKeys; public PageDefinitionSpecificPropertySettingsUpdater( Func propertySettingsRepositoryMethod, @@ -23,6 +25,7 @@ public PageDefinitionSpecificPropertySettingsUpdater( this._propertySettingsRepositoryMethod = propertySettingsRepositoryMethod; this.globalPropertySettingsLocator = globalPropertySettingsLocator; this.pageDefinitionRepository = pageDefinitionRepository; + updatedPropertySettingsCacheKeys = new List(); } protected internal virtual void UpdatePropertySettings(PageTypeDefinition pageTypeDefinition, PageTypePropertyDefinition propertyDefinition, PageDefinition pageDefinition) @@ -66,6 +69,7 @@ void UpdatePageDefinitionsLocalPropertySettings(PageTypePropertyDefinition prope if (hashBeforeUpdate != hashAfterUpdate || !settingsAlreadyExists) { _propertySettingsRepositoryMethod().Save(container); + AddToUpdatedCacheKeys(container); } }); } @@ -90,10 +94,18 @@ void UpdatePageDefinitionsGlobalPropertySettings(PageTypePropertyDefinition prop //TODO: Add spec validating that exception is thrown with the below uncommented (An item with the same key has already been added.) //container.Settings.Add(globalSettingsUpdater.SettingsType.FullName, wrapper); _propertySettingsRepositoryMethod().Save(container); + AddToUpdatedCacheKeys(container); } } } + private void AddToUpdatedCacheKeys(PropertySettingsContainer container) + { + string cacheKey = string.Format(CultureInfo.InvariantCulture, "EP:{0}", new object[] { typeof(PropertySettingsRepository).FullName }); + cacheKey = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", new object[] { cacheKey, container.Id }); + updatedPropertySettingsCacheKeys.Add(cacheKey); + } + private PropertySettingsContainer GetPropertySettingsContainer(PageDefinition pageDefinition) { PropertySettingsContainer container; diff --git a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSynchronizationEngine.cs b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSynchronizationEngine.cs index 0b6f8fe..cd4fa2a 100644 --- a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSynchronizationEngine.cs +++ b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionSynchronizationEngine.cs @@ -14,6 +14,16 @@ public class PageDefinitionSynchronizationEngine PageDefinitionSpecificPropertySettingsUpdater pageDefinitionSpecificPropertySettingsUpdater; IPageTypeRepository pageTypeRepository; + internal IPageDefinitionUpdater PageDefinitionUpdater + { + get { return pageDefinitionUpdater; } + } + + internal PageDefinitionSpecificPropertySettingsUpdater PageDefinitionSpecificPropertySettingsUpdater + { + get { return pageDefinitionSpecificPropertySettingsUpdater; } + } + public PageDefinitionSynchronizationEngine( IPageDefinitionUpdater pageDefinitionUpdater, PageTypePropertyDefinitionLocator pageTypePropertyDefinitionLocator, @@ -34,10 +44,14 @@ protected internal virtual void UpdatePageTypePropertyDefinitions(IPageType page foreach (PageTypePropertyDefinition propertyDefinition in definitions) { PageDefinition pageDefinition = GetExistingPageDefinition(pageType, propertyDefinition); + if (pageDefinition == null) { - pageDefinitionUpdater.CreateNewPageDefinition(propertyDefinition); - pageDefinition = GetExistingPageDefinition(pageType, propertyDefinition); + using (new TimingsLogger(string.Format("Creating new page definition '{0}' for page type {1}: ", propertyDefinition.Name, pageType.Name))) + { + pageDefinitionUpdater.CreateNewPageDefinition(propertyDefinition); + pageDefinition = GetExistingPageDefinition(pageType, propertyDefinition); + } } else { diff --git a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionUpdater.cs b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionUpdater.cs index c144ca1..23850d2 100644 --- a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionUpdater.cs +++ b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageDefinitionUpdater.cs @@ -1,14 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EPiServer.DataAbstraction; -using log4net; -using PageTypeBuilder.Abstractions; -using PageTypeBuilder.Discovery; - -namespace PageTypeBuilder.Synchronization.PageDefinitionSynchronization +namespace PageTypeBuilder.Synchronization.PageDefinitionSynchronization { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using EPiServer.Core; + using EPiServer.DataAbstraction; + using log4net; + using Abstractions; + using Discovery; + public class PageDefinitionUpdater : IPageDefinitionUpdater { private static readonly ILog log = LogManager.GetLogger(typeof(PageDefinitionSynchronizationEngine)); @@ -16,9 +17,15 @@ public class PageDefinitionUpdater : IPageDefinitionUpdater private ITabDefinitionRepository tabDefinitionRepository; private PageDefinitionTypeMapper pageDefinitionTypeMapper; private List newlyCreatedPageDefinitions; + private List updatedPageDefinitions; + + public List UpdatedPageDefinitions + { + get { return updatedPageDefinitions; } + } public PageDefinitionUpdater( - IPageDefinitionRepository pageDefinitionRepository, + IPageDefinitionRepository pageDefinitionRepository, ITabDefinitionRepository tabDefinitionRepository, PageDefinitionTypeMapper pageDefinitionTypeMapper) { @@ -26,6 +33,7 @@ public PageDefinitionUpdater( this.tabDefinitionRepository = tabDefinitionRepository; this.pageDefinitionTypeMapper = pageDefinitionTypeMapper; newlyCreatedPageDefinitions = new List(); + updatedPageDefinitions = new List(); } public virtual void CreateNewPageDefinition(PageTypePropertyDefinition propertyDefinition) @@ -33,9 +41,9 @@ public virtual void CreateNewPageDefinition(PageTypePropertyDefinition propertyD PageDefinition pageDefinition = new PageDefinition(); pageDefinition.PageTypeID = propertyDefinition.PageType.ID; pageDefinition.Name = propertyDefinition.Name; - pageDefinition.EditCaption = propertyDefinition.GetEditCaptionOrName(); + pageDefinition.EditCaption = propertyDefinition.GetEditCaptionOrName(false); SetPageDefinitionType(pageDefinition, propertyDefinition); - + newlyCreatedPageDefinitions.Add(GetPageDefinitionKey(pageDefinition)); UpdatePageDefinitionValues(pageDefinition, propertyDefinition); @@ -52,18 +60,60 @@ protected internal virtual PageDefinitionType GetPageDefinitionType(PageTypeProp return pageDefinitionTypeMapper.GetPageDefinitionType(definition); } + private void HandleDefaultValues(PageDefinition pageDefinition) + { + if (pageDefinition.DefaultValue != null && pageDefinition.DefaultValue == string.Empty) + pageDefinition.DefaultValue = null; + + if (pageDefinition.DefaultValue != null) + { + object value; + + try + { + value = PropertyData.CreatePropertyDataObject(pageDefinition.Type.DataType).ParseToObject(pageDefinition.DefaultValue).Value; + } + catch + { + value = null; + } + + if (value == null) + { + pageDefinition.DefaultValue = null; + } + } + + if (pageDefinition.DefaultValue == null && pageDefinition.DefaultValueType == DefaultValueType.Value && pageDefinition.Type.DataType != PropertyDataType.Boolean) + pageDefinition.DefaultValueType = DefaultValueType.None; + } + public virtual void UpdateExistingPageDefinition(PageDefinition pageDefinition, PageTypePropertyDefinition pageTypePropertyDefinition) { + // LC: Reset default value if no default value type is defined + if (pageTypePropertyDefinition.PageTypePropertyAttribute.DefaultValueSet && pageTypePropertyDefinition.PageTypePropertyAttribute.DefaultValueType == DefaultValueType.None || pageTypePropertyDefinition.PageTypePropertyAttribute.DefaultValueType == DefaultValueType.Inherit) + pageTypePropertyDefinition.PageTypePropertyAttribute.DefaultValue = null; + string oldValues = SerializeValues(pageDefinition); UpdatePageDefinitionValues(pageDefinition, pageTypePropertyDefinition); + // LC: Change to follow logic in PageDefintion.Save for resetting default values and default value types + HandleDefaultValues(pageDefinition); + string updatedValues = SerializeValues(pageDefinition); - if (updatedValues != oldValues) + + if (updatedValues == oldValues) + return; + + log.Debug(string.Format("Updating PageDefintion, old values: {0}, new values: {1}.", oldValues, updatedValues)); + + using (new TimingsLogger(string.Format("Updating page definition '{0}', page type: {1}{2}{3}{2}{4}", pageDefinition.Name, pageDefinition.PageTypeID, Environment.NewLine, oldValues, updatedValues))) { - log.Debug(string.Format("Updating PageDefintion, old values: {0}, new values: {1}.", oldValues, updatedValues)); pageDefinitionRepository.Save(pageDefinition); } + + updatedPageDefinitions.Add(pageDefinition.ID); } protected virtual string SerializeValues(PageDefinition pageDefinition) @@ -74,7 +124,8 @@ protected virtual string SerializeValues(PageDefinition pageDefinition) builder.Append(pageDefinition.Name); builder.Append("|"); builder.Append("Type: "); - builder.Append(pageDefinition.Type.TypeName); + //builder.Append(pageDefinition.Type.TypeName); + builder.Append(string.Format("{0}_{1}", pageDefinition.Type.ID, pageDefinition.Type.Name)); builder.Append("|"); builder.Append("EditCaption:"); builder.Append(pageDefinition.EditCaption); @@ -89,7 +140,13 @@ protected virtual string SerializeValues(PageDefinition pageDefinition) builder.Append(pageDefinition.Searchable); builder.Append("|"); builder.Append("DefaultValue:"); - builder.Append(pageDefinition.DefaultValue); + + string defaultValue = pageDefinition.DefaultValue; + + if (pageDefinition.Type.DataType == PropertyDataType.Boolean && string.Equals(defaultValue, "true", StringComparison.OrdinalIgnoreCase) || string.Equals(defaultValue, "false", StringComparison.OrdinalIgnoreCase)) + defaultValue = defaultValue.ToLower(); + + builder.Append(defaultValue); builder.Append("|"); builder.Append("DefaultValueType:"); builder.Append(pageDefinition.DefaultValueType); @@ -105,7 +162,7 @@ protected virtual string SerializeValues(PageDefinition pageDefinition) builder.Append("|"); builder.Append("Tab.ID:"); builder.Append(pageDefinition.Tab.ID); - builder.Append("|"); + builder.Append("|"); return builder.ToString(); } @@ -120,22 +177,35 @@ private string GetPageDefinitionKey(PageDefinition pageDefinition) return string.Format("{0}_{1}", pageDefinition.PageTypeID, pageDefinition.Name); } - protected virtual void UpdatePageDefinitionValues(PageDefinition pageDefinition, PageTypePropertyDefinition pageTypePropertyDefinition) + protected virtual void UpdatePageDefinitionValues(PageDefinition pageDefinition, PageTypePropertyDefinition pageTypePropertyDefinition, + bool propertyGroupOverride) { - pageDefinition.Name = pageTypePropertyDefinition.Name; + if (!propertyGroupOverride) + pageDefinition.Name = pageTypePropertyDefinition.Name; - PageTypePropertyAttribute propertyAttribute = pageTypePropertyDefinition.PageTypePropertyAttribute; - - var specifiedType = GetPageDefinitionType(pageTypePropertyDefinition); - var currentType = pageDefinition.Type; - - if(specifiedType.DataType == currentType.DataType) - pageDefinition.Type = specifiedType; + PageTypePropertyAttribute propertyAttribute = propertyGroupOverride + ? pageTypePropertyDefinition.PageTypePropertyGroupPropertyOverrideAttribute + : pageTypePropertyDefinition.PageTypePropertyAttribute; + + if (!propertyGroupOverride) + { + var specifiedType = GetPageDefinitionType(pageTypePropertyDefinition); + var currentType = pageDefinition.Type; + + if (specifiedType.DataType == currentType.DataType) + pageDefinition.Type = specifiedType; + + if (CanModifyProperty(pageDefinition, propertyAttribute.TabSet)) + UpdatePageDefinitionTab(pageDefinition, propertyAttribute); + + if (CanModifyProperty(pageDefinition, propertyAttribute.SortOrderSet)) + pageDefinition.FieldOrder = GetFieldOrder(pageDefinition, propertyAttribute); + } if (CanModifyProperty(pageDefinition, propertyAttribute.EditCaptionSet)) - pageDefinition.EditCaption = pageTypePropertyDefinition.GetEditCaptionOrName(); + pageDefinition.EditCaption = pageTypePropertyDefinition.GetEditCaptionOrName(propertyGroupOverride); else if (!propertyAttribute.EditCaptionSet && string.IsNullOrEmpty(pageDefinition.EditCaption)) - pageDefinition.EditCaption = pageTypePropertyDefinition.GetEditCaptionOrName(); + pageDefinition.EditCaption = pageTypePropertyDefinition.GetEditCaptionOrName(propertyGroupOverride); if (CanModifyProperty(pageDefinition, propertyAttribute.HelpTextSet)) pageDefinition.HelpText = propertyAttribute.HelpText ?? string.Empty; @@ -157,12 +227,14 @@ protected virtual void UpdatePageDefinitionValues(PageDefinition pageDefinition, if (CanModifyProperty(pageDefinition, propertyAttribute.DisplayInEditModeSet)) pageDefinition.DisplayEditUI = propertyAttribute.DisplayInEditMode; + } - if (CanModifyProperty(pageDefinition, propertyAttribute.SortOrderSet)) - pageDefinition.FieldOrder = GetFieldOrder(pageDefinition, propertyAttribute); + protected virtual void UpdatePageDefinitionValues(PageDefinition pageDefinition, PageTypePropertyDefinition pageTypePropertyDefinition) + { + UpdatePageDefinitionValues(pageDefinition, pageTypePropertyDefinition, false); - if (CanModifyProperty(pageDefinition, propertyAttribute.TabSet)) - UpdatePageDefinitionTab(pageDefinition, propertyAttribute); + if (pageTypePropertyDefinition.PageTypePropertyGroupPropertyOverrideAttribute != null) + UpdatePageDefinitionValues(pageDefinition, pageTypePropertyDefinition, true); } protected virtual int GetFieldOrder(PageDefinition pageDefinition, PageTypePropertyAttribute propertyAttribute) @@ -185,7 +257,7 @@ protected virtual void UpdatePageDefinitionTab(PageDefinition pageDefinition, Pa var tab = tabDefinitionRepository.List().First(); if (propertyAttribute.Tab != null) { - Tab definedTab = (Tab) Activator.CreateInstance(propertyAttribute.Tab); + Tab definedTab = (Tab)Activator.CreateInstance(propertyAttribute.Tab); tab = tabDefinitionRepository.GetTabDefinition(definedTab.Name); } pageDefinition.Tab = tab; diff --git a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageTypePropertyDefinitionExtensions.cs b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageTypePropertyDefinitionExtensions.cs index d1df30d..1d77a90 100644 --- a/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageTypePropertyDefinitionExtensions.cs +++ b/PageTypeBuilder/Synchronization/PageDefinitionSynchronization/PageTypePropertyDefinitionExtensions.cs @@ -4,11 +4,14 @@ namespace PageTypeBuilder.Synchronization.PageDefinitionSynchronization { public static class PageTypePropertyDefinitionExtensions { - public static string GetEditCaptionOrName(this PageTypePropertyDefinition pageTypePropertyDefinition) + public static string GetEditCaptionOrName(this PageTypePropertyDefinition pageTypePropertyDefinition, bool propertyGroupOverride) { string editCaption = pageTypePropertyDefinition.PageTypePropertyAttribute.EditCaption; - - if(string.IsNullOrEmpty(editCaption)) + + if (propertyGroupOverride && pageTypePropertyDefinition.PageTypePropertyGroupPropertyOverrideAttribute.EditCaptionSet) + editCaption = pageTypePropertyDefinition.PageTypePropertyGroupPropertyOverrideAttribute.EditCaption; + + if (string.IsNullOrEmpty(editCaption)) editCaption = pageTypePropertyDefinition.Name; return editCaption; diff --git a/PageTypeBuilder/Synchronization/PageTypeLocator.cs b/PageTypeBuilder/Synchronization/PageTypeLocator.cs index 74bb587..182c61b 100644 --- a/PageTypeBuilder/Synchronization/PageTypeLocator.cs +++ b/PageTypeBuilder/Synchronization/PageTypeLocator.cs @@ -8,6 +8,11 @@ public class PageTypeLocator : IPageTypeLocator { private IPageTypeRepository pageTypeRepository; + public IPageTypeRepository PageTypeRepository + { + get { return pageTypeRepository; } + } + public PageTypeLocator(IPageTypeRepository pageTypeRepository) { this.pageTypeRepository = pageTypeRepository; @@ -19,21 +24,16 @@ public virtual IPageType GetExistingPageType(PageTypeDefinition definition) Type type = definition.Type; PageTypeAttribute attribute = definition.Attribute; if (attribute.Guid.HasValue) - { existingPageType = pageTypeRepository.Load(attribute.Guid.Value); - } if (existingPageType == null && attribute.Name != null) - { existingPageType = pageTypeRepository.Load(attribute.Name); - } if (existingPageType == null) - { existingPageType = pageTypeRepository.Load(type.Name); - } - return existingPageType; + return existingPageType; } + } } diff --git a/PageTypeBuilder/Synchronization/PageTypeSynchronizer.cs b/PageTypeBuilder/Synchronization/PageTypeSynchronizer.cs index 753704b..769e66b 100644 --- a/PageTypeBuilder/Synchronization/PageTypeSynchronizer.cs +++ b/PageTypeBuilder/Synchronization/PageTypeSynchronizer.cs @@ -1,14 +1,16 @@ -using System.Collections.Generic; -using System.Linq; -using PageTypeBuilder.Abstractions; -using PageTypeBuilder.Configuration; -using PageTypeBuilder.Discovery; -using PageTypeBuilder.Synchronization.Hooks; -using PageTypeBuilder.Synchronization.PageDefinitionSynchronization; -using PageTypeBuilder.Synchronization.Validation; - -namespace PageTypeBuilder.Synchronization +namespace PageTypeBuilder.Synchronization { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using Abstractions; + using Configuration; + using Discovery; + using Hooks; + using PageDefinitionSynchronization; + using Validation; + public class PageTypeSynchronizer { private IPageTypeLocator _pageTypeLocator; @@ -33,7 +35,12 @@ public PageTypeSynchronizer(IPageTypeDefinitionLocator pageTypeDefinitionLocator this.pageTypeResolver = pageTypeResolver; TabLocator = tabLocator; TabDefinitionUpdater = tabDefinitionUpdater; - _pageTypeDefinitions = pageTypeDefinitionLocator.GetPageTypeDefinitions(); + + using (new TimingsLogger("Getting page type definitions")) + { + _pageTypeDefinitions = pageTypeDefinitionLocator.GetPageTypeDefinitions(); + } + PageTypeUpdater = pageTypeUpdater; PageDefinitionSynchronizationEngine = pageDefinitionSynchronizationEngine; PageTypeDefinitionValidator = pageTypeDefinitionValidator; @@ -42,35 +49,124 @@ public PageTypeSynchronizer(IPageTypeDefinitionLocator pageTypeDefinitionLocator this.hooksHandler = hooksHandler; } - internal void SynchronizePageTypes() - { - hooksHandler.InvokePreSynchronizationHooks(); - if (!_configuration.DisablePageTypeUpdation) - { - UpdateTabDefinitions(); - globalPropertySettingsSynchronizer.Synchronize(); - } - - IEnumerable pageTypeDefinitions = _pageTypeDefinitions; + public void SynchronizePageTypes() + { + SynchronizePageTypes(false); + } - ValidatePageTypeDefinitions(pageTypeDefinitions); + public void SynchronizePageTypes(bool forcePageTypeUpdation) + { + SynchronizationHelper.AssemblyLocator = TabLocator.AssemblyLocator; + bool disablePageTypeUpdation = _configuration.DisablePageTypeUpdation; - if (!_configuration.DisablePageTypeUpdation) - CreateNonExistingPageTypes(pageTypeDefinitions); + if (forcePageTypeUpdation) + disablePageTypeUpdation = false; - if (_configuration.DisablePageTypeUpdation) + bool oneTimeSynchornizationEnabled = !disablePageTypeUpdation && SynchronizationHelper.OneTimeSynchornizationEnabled; + bool iAmSynching = true; + + if (oneTimeSynchornizationEnabled) { - IEnumerable nonExistingPageTypes = GetNonExistingPageTypes(pageTypeDefinitions); - pageTypeDefinitions = pageTypeDefinitions.Except(nonExistingPageTypes).ToList(); + SynchronizationHelper.ClearLogInfo(); + bool isBeingSynchronized = SynchronizationHelper.IsBeingSynchronized(out iAmSynching); + + if (isBeingSynchronized && !iAmSynching) + { + using (new TimingsLogger("Waiting for synchornization to finish on synching site.")) + { + while (SynchronizationHelper.IsBeingSynchronized(out iAmSynching) && !iAmSynching) + Thread.Sleep(_configuration.OneTimeSynchornizationPollTime); + } + } } - else + + try { - UpdatePageTypes(pageTypeDefinitions); - UpdatePageTypePropertyDefinitions(pageTypeDefinitions); + using (new TimingsLogger("Pre synchoronization hooks")) + { + if (iAmSynching) + hooksHandler.InvokePreSynchronizationHooks(); + } + + if (!disablePageTypeUpdation && iAmSynching) + { + using (new TimingsLogger("updating tab definitions")) + { + UpdateTabDefinitions(); + } + + using (new TimingsLogger("updating global property settings")) + { + globalPropertySettingsSynchronizer.Synchronize(); + } + } + + IEnumerable pageTypeDefinitions = _pageTypeDefinitions.ToList(); + + if (!disablePageTypeUpdation && _configuration.PerformValidation && iAmSynching) + { + using (new TimingsLogger("Validating page type definitions")) + { + ValidatePageTypeDefinitions(pageTypeDefinitions); + } + } + + if (!disablePageTypeUpdation && iAmSynching) + { + using (new TimingsLogger("Creating non existing page types")) + { + CreateNonExistingPageTypes(pageTypeDefinitions); + } + } + + if (disablePageTypeUpdation || !iAmSynching) + { + using (new TimingsLogger("Getting non existing page types")) + { + IEnumerable nonExistingPageTypes = GetNonExistingPageTypes(pageTypeDefinitions); + pageTypeDefinitions = pageTypeDefinitions.Except(nonExistingPageTypes).ToList(); + } + } + else + { + using (new TimingsLogger("Updating page types")) + { + UpdatePageTypes(pageTypeDefinitions); + } + + using (new TimingsLogger("Updating page type property definitions")) + { + UpdatePageTypePropertyDefinitions(pageTypeDefinitions); + } + } + + using (new TimingsLogger("Adding page types to resolver")) + { + AddPageTypesToResolver(pageTypeDefinitions); + } + + if (oneTimeSynchornizationEnabled && iAmSynching) + { + SynchronizationHelper.UpateSynchronizationCache(PageTypeUpdater.UpdatedPageTypeIds, + PageDefinitionSynchronizationEngine.PageDefinitionUpdater.UpdatedPageDefinitions, + TabDefinitionUpdater.updatedTabIds, + PageDefinitionSynchronizationEngine.PageDefinitionSpecificPropertySettingsUpdater.updatedPropertySettingsCacheKeys, + globalPropertySettingsSynchronizer.globalSettingsIds); + + SynchronizationHelper.SynchingComplete(); + } + + if (oneTimeSynchornizationEnabled && !iAmSynching) + SynchronizationHelper.InvalidateCache(); } + catch (Exception) + { + if (oneTimeSynchornizationEnabled && iAmSynching) + SynchronizationHelper.RevertSyncronizationStatus(); - AddPageTypesToResolver(pageTypeDefinitions); + throw; + } } protected internal virtual void UpdateTabDefinitions() @@ -86,10 +182,18 @@ protected internal virtual void ValidatePageTypeDefinitions(IEnumerable pageTypeDefinitions) { - IEnumerable nonExistingPageTypes = GetNonExistingPageTypes(pageTypeDefinitions); + IEnumerable nonExistingPageTypes; + + using (new TimingsLogger("GetNonExistingPageTypes")) + { + nonExistingPageTypes = GetNonExistingPageTypes(pageTypeDefinitions).ToList(); + } - foreach (PageTypeDefinition definition in nonExistingPageTypes) - PageTypeUpdater.CreateNewPageType(definition); + using (new TimingsLogger("PageTypeUpdater.CreateNewPageTypes")) + { + foreach (PageTypeDefinition definition in nonExistingPageTypes) + PageTypeUpdater.CreateNewPageType(definition); + } } protected internal virtual IEnumerable GetNonExistingPageTypes(IEnumerable pageTypeDefinitions) diff --git a/PageTypeBuilder/Synchronization/PageTypeUpdater.cs b/PageTypeBuilder/Synchronization/PageTypeUpdater.cs index 7941f44..3a4875f 100644 --- a/PageTypeBuilder/Synchronization/PageTypeUpdater.cs +++ b/PageTypeBuilder/Synchronization/PageTypeUpdater.cs @@ -19,7 +19,8 @@ public class PageTypeUpdater private IPageTypeLocator _pageTypeLocator; private IEnumerable _pageTypeDefinitions; private IPageTypeValueExtractor _pageTypeValueExtractor; - internal List NewlyCreatedPageTypes; + internal List NewlyCreatedPageTypes; + internal List UpdatedPageTypeIds; public PageTypeUpdater(IPageTypeDefinitionLocator pageTypeDefinitionLocator, IPageTypeRepository pageTypeRepository, @@ -32,6 +33,7 @@ public PageTypeUpdater(IPageTypeDefinitionLocator pageTypeDefinitionLocator, _pageTypeValueExtractor = pageTypeValueExtractor; _pageTypeLocator = pageTypeLocator; NewlyCreatedPageTypes = new List(); + UpdatedPageTypeIds = new List(); } protected internal virtual IPageType GetExistingPageType(PageTypeDefinition definition) @@ -55,11 +57,14 @@ protected internal virtual IPageType CreateNewPageType(PageTypeDefinition defini if (definition.Attribute.Guid.HasValue) pageType.GUID = definition.Attribute.Guid.Value; - string filename = attribute.Filename; - if (string.IsNullOrEmpty(filename)) - { + string filename = attribute.Filename; + + Version version = typeof(PageData).Assembly.GetName().Version; + bool cms6R2AndAbove = version.Major >= 6 && version.Minor >= 1; + + if (string.IsNullOrEmpty(filename) && cms6R2AndAbove) filename = DefaultFilename; - } + pageType.FileName = filename; PageTypeRepository.Save(pageType); @@ -100,10 +105,13 @@ protected internal virtual void UpdatePageType(PageTypeDefinition definition) if (!availablePageTypesSet && CanModifyProperty(pageType, attribute.ExcludedPageTypesSet) && attribute.ExcludedPageTypes != null) UpdateAvailablePageTypesExcluded(pageType, attribute.ExcludedPageTypes); - string newValuesString = SerializeValues(pageType); - - if (newValuesString != oldValueString) - PageTypeRepository.Save(pageType); + string newValuesString = SerializeValues(pageType); + + if (newValuesString == oldValueString) + return; + + UpdatedPageTypeIds.Add(pageType.ID); + PageTypeRepository.Save(pageType); } protected internal virtual string SerializeValues(IPageType pageType) diff --git a/PageTypeBuilder/Synchronization/SynchronizationHelper.cs b/PageTypeBuilder/Synchronization/SynchronizationHelper.cs new file mode 100644 index 0000000..fff6bd2 --- /dev/null +++ b/PageTypeBuilder/Synchronization/SynchronizationHelper.cs @@ -0,0 +1,419 @@ +namespace PageTypeBuilder.Synchronization +{ + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Data; + using System.Data.SqlClient; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Web.Hosting; + using Configuration; + using EPiServer.Data.Cache; + using Reflection; + + internal static class SynchronizationHelper + { + private static readonly Guid SyncGuid = new Guid("14f94889-d8e3-4294-b84f-9c5d990b019d"); + private static readonly Guid SyncCacheGuid = new Guid("9F85BF43-2509-48B6-AE3D-B75DE9918542"); + private const string SynchornizingStatusText = "Synchornizing"; + private const string PageTypeBuilderSynchornization = "PageTypeBuilderSynchornization"; + private const string ConnectionStringName = "EPiServerDB"; + private static string _siteId; + private static DirectoryInfo _logFileDirectory; + private static bool _logFileDirectoryRetrieved; + + public static bool OneTimeSynchornizationEnabled + { + get + { + return PageTypeBuilderConfiguration.GetConfiguration().OneTimeSynchornizationEnabled && + ConfigurationManager.ConnectionStrings[ConnectionStringName].ProviderName.Equals("System.Data.SqlClient"); + } + } + + public static IAssemblyLocator AssemblyLocator { get; set; } + + public static bool IsBeingSynchronized(out bool iAmSynching) + { + LogInfo("Checking whether synchornization is in progress"); + iAmSynching = false; + bool isBeingSynchronized = false; + + try + { + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString)) + { + connection.Open(); + bool commit = false; + + using (SqlTransaction transaction = connection.BeginTransaction()) + { + string sql = string.Format("SELECT COUNT(*) FROM tblItem WITH (TABLOCKX) WHERE pkID = '{0}'", SyncGuid); + + using (SqlCommand command = new SqlCommand(sql, connection, transaction)) + { + int count = (int)command.ExecuteScalar(); + + if (count == 0) + { + LogInfo("Creating new record in tblItem as we are now synching"); + SaveSynchornizationStatus(connection, transaction, SyncGuid, Encoding.UTF8.GetBytes(SynchornizingStatusText)); + iAmSynching = true; + isBeingSynchronized = true; + commit = true; + } + else + { + string value = GetSynchornizationStatus(connection, transaction, SyncGuid); + + if (value.Trim() == SynchornizingStatusText) + { + LogInfo("Already being synched by another site"); + isBeingSynchronized = true; + } + else + { + string timestamps = GetAssemblyTimeStamps(); + + LogInfo("current application assembly timestamps: " + timestamps + "|"); + LogInfo("current timestamps in database:" + value.Trim() + "|"); + + if (!string.Equals(value.Trim(), timestamps)) + { + LogInfo("time stamps have changed, so now synching"); + SaveSynchornizationStatus(connection, transaction, SyncGuid, Encoding.UTF8.GetBytes(SynchornizingStatusText)); + iAmSynching = true; + isBeingSynchronized = true; + commit = true; + } + } + } + } + + if (commit) + transaction.Commit(); + else + transaction.Rollback(); + } + + connection.Close(); + } + } + catch (Exception) + { + RevertSyncronizationStatus(); + throw; + } + return isBeingSynchronized; + } + + public static void SynchingComplete() + { + try + { + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString)) + { + connection.Open(); + + using (SqlTransaction transaction = connection.BeginTransaction()) + { + string sql = string.Format("SELECT COUNT(*) FROM tblItem WITH (TABLOCKX) WHERE pkID = '{0}'", SyncGuid); + + using (SqlCommand command = new SqlCommand(sql, connection, transaction)) + { + command.ExecuteScalar(); + SaveSynchornizationStatus(connection, transaction, SyncGuid, Encoding.UTF8.GetBytes(GetAssemblyTimeStamps())); + LogInfo("Updated timestamps with value: " + GetAssemblyTimeStamps()); + } + + transaction.Commit(); + } + + connection.Close(); + } + } + catch (Exception) + { + RevertSyncronizationStatus(); + throw; + } + } + + private static void SaveSynchornizationStatus(SqlConnection connection, SqlTransaction transaction, Guid id, byte[] data) + { + using (SqlCommand command = new SqlCommand("[dbo].[ItemSave]", connection, transaction)) + { + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@Id", id.ToString()); + command.Parameters.AddWithValue("@DbSchemaId", 1); + command.Parameters.AddWithValue("@Name", PageTypeBuilderSynchornization); + command.Parameters.Add("@ItemData", SqlDbType.Image); + command.Parameters["@ItemData"].Value = data; + command.ExecuteNonQuery(); + } + } + + private static string GetSynchornizationStatus(SqlConnection connection, SqlTransaction transaction, Guid id) + { + using (SqlCommand command = new SqlCommand("[dbo].[ItemLoad]", connection, transaction)) + { + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@Id", id.ToString()); + byte[] bytes = (byte[])command.ExecuteScalar(); + string value = Encoding.UTF8.GetString(bytes); + LogInfo("tblItem value is currently: " + value); + return value; + } + } + + public static void RevertSyncronizationStatus() + { + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString)) + { + connection.Open(); + + using (SqlTransaction transaction = connection.BeginTransaction()) + { + string sql = string.Format("SELECT COUNT(*) FROM tblItem WITH (TABLOCKX) WHERE pkID = '{0}'", SyncGuid); + + using (SqlCommand command = new SqlCommand(sql, connection, transaction)) + { + command.ExecuteScalar(); + SaveSynchornizationStatus(connection, transaction, SyncGuid, new byte[0]); + LogInfo("Reverted synchronization status."); + } + + transaction.Commit(); + } + + connection.Close(); + } + } + + public static void UpateSynchronizationCache(List pageTypeIds, List pageDefinitionIds, List updatedTabDefinitionIds, + List updatedPropertySettingsCacheKeys, List globalPropertySettingsIds) + { + try + { + StringBuilder stringBuilder = new StringBuilder(); + AppendItems(stringBuilder, "pageTypeIds", pageTypeIds.Select(c => c as object).ToArray()); + AppendItems(stringBuilder, "pageDefinitionIds", pageDefinitionIds.Select(c => c as object).ToArray()); + AppendItems(stringBuilder, "tabDefinitionIds", updatedTabDefinitionIds.Select(c => c as object).ToArray()); + AppendItems(stringBuilder, "updatedPropertySettingsCacheKeys", updatedPropertySettingsCacheKeys.Select(c => c as object).ToArray()); + AppendItems(stringBuilder, "updatedGlobalPropertySettingsIds", globalPropertySettingsIds.Select(c => c as object).ToArray()); + + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString)) + { + connection.Open(); + SaveSynchornizationStatus(connection, null, SyncCacheGuid, Encoding.UTF8.GetBytes(stringBuilder.ToString())); + connection.Close(); + } + } + catch + { + } + } + + private static void AppendItems(StringBuilder stringBuilder, string key, IEnumerable items) + { + stringBuilder.AppendFormat("{0}:", key); + + foreach (object item in items) + stringBuilder.AppendFormat("{0}|", item); + + stringBuilder.AppendLine(); + } + + public static void InvalidateCache() + { + string value = string.Empty; + + using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString)) + { + connection.Open(); + value = GetSynchornizationStatus(connection, null, SyncCacheGuid); + connection.Close(); + } + + if (string.IsNullOrEmpty(value)) + return; + + string[] parts = value.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + string part; + + // page types + if (!string.IsNullOrEmpty(part = GetCacheInvalidationKeyPart(parts, "pageTypeIds", 0))) + { + IEnumerable pageTypeIds = part.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + + if (pageTypeIds.Any()) + EPiServer.DataAbstraction.PageType.ClearCache(); + } + + // page definitions + if (!string.IsNullOrEmpty(part = GetCacheInvalidationKeyPart(parts, "pageDefinitionIds", 1))) + { + List pageDefinitionIds = part.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList(); + + if (pageDefinitionIds.Any()) + { + foreach (int pageDefinitionId in pageDefinitionIds) + { + try + { + EPiServer.DataAbstraction.PageDefinition pageDefinition = EPiServer.DataAbstraction.PageDefinition.Load(pageDefinitionId); + pageDefinition.ClearCache(); + } + catch + { + } + } + } + } + + // tab definitions + if (!string.IsNullOrEmpty(part = GetCacheInvalidationKeyPart(parts, "tabDefinitionIds", 2))) + { + IEnumerable tabDefinitionIds = part.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + + if (tabDefinitionIds.Any()) + EPiServer.DataAbstraction.TabDefinition.ClearCache(); + } + + // property settings and property settings referencing global + if (!string.IsNullOrEmpty(part = GetCacheInvalidationKeyPart(parts, "updatedPropertySettingsCacheKeys", 3))) + { + List cacheKeys = part.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + + foreach (string cacheKey in cacheKeys) + EPiServer.CacheManager.Remove(cacheKey); + } + + // global property settings + if (!string.IsNullOrEmpty(part = GetCacheInvalidationKeyPart(parts, "updatedGlobalPropertySettingsIds", 4))) + { + IEnumerable ids = part.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(c => new Guid(c)); + + foreach (Guid id in ids) + CacheProvider.Instance.Remove(id.ToString()); + } + } + + private static string GetCacheInvalidationKeyPart(IList parts, string key, int index) + { + if (parts.Count <= index || !parts[index].StartsWith(key + ":")) + return string.Empty; + + return parts[index].Substring(parts[index].IndexOf(":") + 1); + } + + private static string GetAssemblyTimeStamps() + { + StringBuilder timeStamps = new StringBuilder(); + IEnumerable assemblies = AssemblyLocator.AssembliesWithReferenceToAssemblyOf(); + + if (PageTypeBuilderConfiguration.GetConfiguration().OneVersionSynchronizationAssemblyVersionStamp == PageTypeBuilderConfiguration.AssemblyVersionStamp.Version) + { + foreach (Assembly assembly in assemblies) + timeStamps.AppendFormat("{0} - {1}|", assembly.GetName().Name, assembly.GetName().Version); + } + else + { + DirectoryInfo binDirectory = new DirectoryInfo(HostingEnvironment.MapPath("~/bin")); + + foreach (FileInfo assemblyFile in binDirectory.GetFiles("*.dll") + .Where(current => assemblies.Any(assembly => string.Equals(assembly.GetName().Name, current.Name.Substring(0, current.Name.Length - 4), StringComparison.OrdinalIgnoreCase))) + .OrderBy(current => current.Name)) + { + timeStamps.AppendFormat("{0} - {1}|", assemblyFile.Name, assemblyFile.LastWriteTime.ToString("yyyyMMdd hh:mm:ss")); + } + } + + return timeStamps.ToString(); + } + + private static void LogInfo(string message) + { + DirectoryInfo logDirectory = GetLogDirectory(); + + if (logDirectory == null || !logDirectory.Exists) + return; + + if (_siteId == null) + _siteId = GetSiteId(); + + try + { + message = string.Format("{0} - {1}: {2}{2}", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), message, Environment.NewLine); + string logFileName = GetLogFileName(logDirectory, _siteId); + File.AppendAllText(logFileName, message); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("Error while trying to log messages using PageTypeBuilder.SynchronizationHelper. " + ex.Message); + } + } + + public static void ClearLogInfo() + { + DirectoryInfo logDirectory = GetLogDirectory(); + + if (logDirectory == null || !logDirectory.Exists) + return; + + string siteId = GetSiteId(); + string logFileName = GetLogFileName(logDirectory, siteId); + + if (!File.Exists(logFileName)) + return; + + try + { + File.Delete(logFileName); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(string.Format("Error while trying to delete file '{0}'. {1}", logFileName, ex.Message)); + } + } + + private static DirectoryInfo GetLogDirectory() + { + if (_logFileDirectoryRetrieved && (_logFileDirectory == null || !_logFileDirectory.Exists)) + return null; + + _logFileDirectoryRetrieved = true; + string directoryPath = PageTypeBuilderConfiguration.GetConfiguration().OneTimeSynchronizationLogFileDirectoryPath; + + if (string.IsNullOrEmpty(directoryPath)) + return null; + + if (!directoryPath.EndsWith(@"\")) + directoryPath += @"\"; + + _logFileDirectory = new DirectoryInfo(directoryPath); + return !_logFileDirectory.Exists ? null : _logFileDirectory; + } + + private static string GetSiteId() + { + try + { + return string.Format("{0}_{1}", HostingEnvironment.ApplicationHost.GetSiteName(), HostingEnvironment.ApplicationHost.GetSiteID()); + } + catch + { + return string.Empty; + } + } + + private static string GetLogFileName(DirectoryInfo logDirectory, string siteId) + { + return string.Format("{0}PageTypeBuilderOneTimeSynchornizationLog_{1}.txt", logDirectory.FullName, siteId); + } + + } +} diff --git a/PageTypeBuilder/Synchronization/TabDefinitionUpdater.cs b/PageTypeBuilder/Synchronization/TabDefinitionUpdater.cs index 87f7c3a..94fea9c 100644 --- a/PageTypeBuilder/Synchronization/TabDefinitionUpdater.cs +++ b/PageTypeBuilder/Synchronization/TabDefinitionUpdater.cs @@ -6,9 +6,13 @@ namespace PageTypeBuilder.Synchronization { public class TabDefinitionUpdater { + + internal List updatedTabIds; + public TabDefinitionUpdater(ITabDefinitionRepository tabDefinitionRepository) { TabDefinitionRepository = tabDefinitionRepository; + updatedTabIds = new List(); } public virtual void UpdateTabDefinitions(IEnumerable tabs) @@ -21,6 +25,7 @@ public virtual void UpdateTabDefinitions(IEnumerable tabs) { UpdateTabDefinition(tabDefinition, tab); TabDefinitionRepository.SaveTabDefinition(tabDefinition); + updatedTabIds.Add(tabDefinition.ID); } } } diff --git a/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionPropertiesValidator.cs b/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionPropertiesValidator.cs index e53b3a4..404bc9d 100644 --- a/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionPropertiesValidator.cs +++ b/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionPropertiesValidator.cs @@ -45,9 +45,7 @@ protected internal virtual void ValidatePageTypeProperties(PageTypeDefinition de protected internal virtual void ValidatePageTypeProperty(PropertyInfo propertyInfo) { ValidateCompilerGeneratedProperty(propertyInfo); - ValidatePageTypePropertyAttribute(propertyInfo); - ValidatePageTypePropertyType(propertyInfo); } diff --git a/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionValidator.cs.cs b/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionValidator.cs.cs index f0f27b0..226fcaf 100644 --- a/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionValidator.cs.cs +++ b/PageTypeBuilder/Synchronization/Validation/PageTypeDefinitionValidator.cs.cs @@ -21,11 +21,17 @@ public PageTypeDefinitionValidator(PageDefinitionTypeMapper pageDefinitionTypeMa public virtual void ValidatePageTypeDefinitions(IEnumerable pageTypeDefinitions) { - ValidatePageTypesHaveGuidOrUniqueName(pageTypeDefinitions); - + using (new TimingsLogger("ValidatePagetypesHaveGuidOrUniqueName")) + { + ValidatePageTypesHaveGuidOrUniqueName(pageTypeDefinitions); + } + foreach (PageTypeDefinition definition in pageTypeDefinitions) { - ValidatePageTypeDefinition(definition, pageTypeDefinitions); + using (new TimingsLogger("ValidatePageTypeDefinition")) + { + ValidatePageTypeDefinition(definition, pageTypeDefinitions); + } } } @@ -55,16 +61,12 @@ IEnumerable> definitionsWithNoGuidAndSameN } public virtual void ValidatePageTypeDefinition(PageTypeDefinition definition, IEnumerable allPageTypeDefinitions) - { - ValidateNameLength(definition); - - ValidateInheritsFromBasePageType(definition); - + { + ValidateNameLength(definition); + ValidateInheritsFromBasePageType(definition); ValidateAvailablePageTypes(definition, allPageTypeDefinitions); - - ValidateExcludedPageTypes(definition, allPageTypeDefinitions); - - PropertiesValidator.ValidatePageTypeProperties(definition); + ValidateExcludedPageTypes(definition, allPageTypeDefinitions); + PropertiesValidator.ValidatePageTypeProperties(definition); } protected internal virtual void ValidateNameLength(PageTypeDefinition definition) diff --git a/PageTypeBuilder/TimingsLogger.cs b/PageTypeBuilder/TimingsLogger.cs new file mode 100644 index 0000000..2c356a8 --- /dev/null +++ b/PageTypeBuilder/TimingsLogger.cs @@ -0,0 +1,100 @@ +namespace PageTypeBuilder +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Web.Hosting; + using Configuration; + + public class TimingsLogger : IDisposable + { + private readonly string _message; + private readonly Stopwatch _stopwatch; + private readonly DirectoryInfo _logDirectory; + private static string _siteId; + + public TimingsLogger(string message) + { + _message = message; + _logDirectory = GetLogDirectory(); + + if (_logDirectory != null && _logDirectory.Exists) + _stopwatch = Stopwatch.StartNew(); + } + + public static void Clear() + { + DirectoryInfo logDirectory = GetLogDirectory(); + + if (logDirectory == null || !logDirectory.Exists) + return; + + string siteId = GetSiteId(); + string logFileName = GetLogFileName(logDirectory, siteId); + + if (!File.Exists(logFileName)) + return; + + try + { + File.Delete(logFileName); + } + catch (Exception ex) + { + Debug.WriteLine(string.Format("Error while trying to delete file '{0}'. {1}", logFileName, ex.Message)); + } + } + + private static DirectoryInfo GetLogDirectory() + { + string directoryPath = PageTypeBuilderConfiguration.GetConfiguration().TimingsLogFileDirectoryPath; + + if (string.IsNullOrEmpty(directoryPath)) + return null; + + if (!directoryPath.EndsWith(@"\")) + directoryPath += @"\"; + + DirectoryInfo logDirectory = new DirectoryInfo(directoryPath); + return !logDirectory.Exists ? null : logDirectory; + } + + private static string GetSiteId() + { + try + { + return string.Format("{0}_{1}", HostingEnvironment.ApplicationHost.GetSiteName(), HostingEnvironment.ApplicationHost.GetSiteID()); + } + catch + { + return string.Empty; + } + } + + private static string GetLogFileName(DirectoryInfo logDirectory, string siteId) + { + return string.Format("{0}PageTypeBuilderTimingsLog_{1}.txt", logDirectory.FullName, siteId); + } + + public void Dispose() + { + if (_logDirectory == null || !_logDirectory.Exists) + return; + + if (_siteId == null) + _siteId = GetSiteId(); + + try + { + string message = string.Format("{0} - {1}: {2}{3}{3}", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), _message, _stopwatch.ElapsedMilliseconds, Environment.NewLine); + string logFileName = GetLogFileName(_logDirectory, _siteId); + File.AppendAllText(logFileName, message); + } + catch (Exception ex) + { + Debug.WriteLine("Error while trying to log messages using PageTypeBuilder.TimingsLogger. " + ex.Message); + } + + } + } +}