Inheritance: CodeFirstRegistration
        /// <summary>
        /// Creates a new property on the ContentType under the correct tab
        /// </summary>
        /// <param name="newContentType"></param>
        /// <param name="tabName"></param>
        /// <param name="dataTypeService"></param>
        /// <param name="atTabGeneric"></param>
        /// <param name="item"></param>
        public PropertyRegistration CreateProperty(IContentTypeBase newContentType, TabRegistration tab, PropertyInfo item, Type documentClrType)
        {
            ContentPropertyAttribute attribute = item.GetCodeFirstAttribute <ContentPropertyAttribute>();
            var tabPostfix = tab == null || !attribute.AddTabAliasToPropertyAlias ? null : tab.OriginalName == null ? tab.Name : tab.OriginalName;
            var dataType   = _dataTypeModule.GetDataType(item);
            var property   = new PropertyRegistration();

            property.Name              = attribute.Name;
            property.Alias             = tabPostfix == null ? attribute.Alias : StringHelperExtensions.HyphenToUnderscore(StringHelperExtensions.ParseUrl(attribute.Alias + "_" + tabPostfix, false));
            property.DataType          = dataType;
            property.PropertyAttribute = attribute;
            property.Metadata          = item;

            PropertyType propertyType = new PropertyType(dataType.Definition);

            propertyType.Name             = property.Name;
            propertyType.Alias            = property.Alias;
            propertyType.Description      = attribute.Description;
            propertyType.Mandatory        = attribute.Mandatory;
            propertyType.SortOrder        = attribute.SortOrder;
            propertyType.ValidationRegExp = attribute.ValidationRegularExpression;


            var propertyDeclaredOnThisDocType = property.Metadata.DeclaringType == documentClrType || property.Metadata.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>(false) != null;
            var propertyDeclaredOnThisTab     = tab == null ? false : property.Metadata.DeclaringType == tab.ClrType;
            var tabDeclaredOnThisDocType      = tab == null ? false : tab.PropertyOfParent.DeclaringType == documentClrType || tab.PropertyOfParent.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>() != null;
            var declaringTypeIsDocType        = property.Metadata.DeclaringType.GetCodeFirstAttribute <ContentTypeAttribute>(false) != null;
            var propertyIsFromCommonBase      = tab == null?
                                                property.Metadata.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == documentClrType.BaseType
                                                :
                                                property.Metadata.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == tab.ClrType.BaseType;

            if (tab == null)
            {
                if (propertyDeclaredOnThisDocType || propertyIsFromCommonBase)                               //only if property declared at this level (or inherited from a non-doctype class)
                {
                    if (!propertyIsFromCommonBase || !newContentType.PropertyTypeExists(propertyType.Alias)) //check if common base properties already exist
                    {
                        CodeFirstManager.Current.Log("Adding property " + property.Name + " on content type " + newContentType.Name, this);
                        newContentType.AddPropertyType(propertyType);
                    }
                }
            }
            else if (tabDeclaredOnThisDocType || propertyIsFromCommonBase)                                   //only if tab declared at this level
            {
                if (propertyDeclaredOnThisTab || propertyIsFromCommonBase)                                   //only if property declared at this level
                {
                    if (!propertyIsFromCommonBase || !newContentType.PropertyTypeExists(propertyType.Alias)) //check if common base properties already exist
                    {
                        CodeFirstManager.Current.Log("Adding property " + property.Name + " on tab " + tab.Name + " of content type " + newContentType.Name, this);
                        newContentType.AddPropertyType(propertyType, tab.Name);
                    }
                }
            }

            return(property);
        }
        /// <summary>
        /// Creates a new property on the ContentType under the correct tab
        /// </summary>
        /// <param name="newContentType"></param>
        /// <param name="tabName"></param>
        /// <param name="dataTypeService"></param>
        /// <param name="atTabGeneric"></param>
        /// <param name="item"></param>
        public PropertyRegistration CreateProperty(IContentTypeBase newContentType, TabRegistration tab, PropertyInfo item, Type documentClrType)
        {
            ContentPropertyAttribute attribute = item.GetCodeFirstAttribute<ContentPropertyAttribute>();
            var tabPostfix = tab == null || !attribute.AddTabAliasToPropertyAlias ? null : tab.OriginalName == null ? tab.Name : tab.OriginalName;
            var dataType = _dataTypeModule.GetDataType(item);
            var property = new PropertyRegistration();
            property.Name = attribute.Name;
            property.Alias = tabPostfix == null ? attribute.Alias : StringHelperExtensions.HyphenToUnderscore(StringHelperExtensions.ParseUrl(attribute.Alias + "_" + tabPostfix, false));
            property.DataType = dataType;
            property.PropertyAttribute = attribute;
            property.Metadata = item;

            PropertyType propertyType = new PropertyType(dataType.Definition);
            propertyType.Name = property.Name;
            propertyType.Alias = property.Alias;
            propertyType.Description = attribute.Description;
            propertyType.Mandatory = attribute.Mandatory;
            propertyType.SortOrder = attribute.SortOrder;
            propertyType.ValidationRegExp = attribute.ValidationRegularExpression;

            var propertyDeclaredOnThisDocType = property.Metadata.DeclaringType == documentClrType || property.Metadata.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>(false) != null;
            var propertyDeclaredOnThisTab = tab == null ? false : property.Metadata.DeclaringType == tab.ClrType;
            var tabDeclaredOnThisDocType = tab == null ? false : tab.PropertyOfParent.DeclaringType == documentClrType || tab.PropertyOfParent.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>() != null;
            var declaringTypeIsDocType = property.Metadata.DeclaringType.GetCodeFirstAttribute<ContentTypeAttribute>(false) != null;
            var propertyIsFromCommonBase = tab == null ?
                property.Metadata.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == documentClrType.BaseType
                :
                property.Metadata.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == tab.ClrType.BaseType;

            if (tab == null)
            {
                if (propertyDeclaredOnThisDocType || propertyIsFromCommonBase) //only if property declared at this level (or inherited from a non-doctype class)
                {
                    if (!propertyIsFromCommonBase || !newContentType.PropertyTypeExists(propertyType.Alias)) //check if common base properties already exist
                    {
                        CodeFirstManager.Current.Log("Adding property " + property.Name + " on content type " + newContentType.Name, this);
                        newContentType.AddPropertyType(propertyType);
                    }
                }
            }
            else if (tabDeclaredOnThisDocType || propertyIsFromCommonBase) //only if tab declared at this level
            {
                if (propertyDeclaredOnThisTab || propertyIsFromCommonBase) //only if property declared at this level
                {
                    if (!propertyIsFromCommonBase || !newContentType.PropertyTypeExists(propertyType.Alias)) //check if common base properties already exist
                    {
                        CodeFirstManager.Current.Log("Adding property " + property.Name + " on tab " + tab.Name + " of content type " + newContentType.Name, this);
                        newContentType.AddPropertyType(propertyType, tab.Name);
                    }
                }
            }

            return property;
        }
 private void LogPropertySyncInfo(IContentTypeBase contentType, TabRegistration tab, PropertyRegistration property, string action, [CallerMemberName] string sourceMethod = null)
 {
     if (tab == null)
     {
         CodeFirstManager.Current.Log(string.Format("{0} property {1} on content type {2}", action, property.Name, contentType.Name), this, sourceMethod);
     }
     else
     {
         CodeFirstManager.Current.Log(string.Format("{0} property {1} on tab {2} of content type {3}", action, tab.Name, property.Name, contentType.Name), this, sourceMethod);
     }
 }
        private bool IsInheritedProperty(TabRegistration tab, Type documentClrType, PropertyRegistration property)
        {
            //If there is no current tab
            if (tab == null)
            {
                //and the declaring type is not the current type
                if (property.Metadata.DeclaringType != documentClrType)
                {
                    //and the declaring type is also an Umbraco content type
                    if (property.Metadata.DeclaringType.GetCodeFirstAttribute <ContentTypeAttribute>(false) != null)
                    {
                        //Inherited property. Don't persist at this level.
                        return(true);
                    }
                }
            }
            //else if the current tab is not directly declared on the current type
            else if (tab.PropertyOfParent.DeclaringType != documentClrType)
            {
                //and the declaring type of the tab is also an Umbraco content type
                if (tab.PropertyOfParent.DeclaringType.GetCodeFirstAttribute <ContentTypeAttribute>(false) != null)
                {
                    //Inherited property. Don't persist at this level.
                    return(true);
                }
            }
            //else if the current property is not directly declared on the current tab type
            else if (property.Metadata.DeclaringType != tab.ClrType)
            {
                var parent = documentClrType.BaseType.GetProperties().Where(x => x.IsParentTabFor(tab)).FirstOrDefault();

                //If any property (inherited or otherwise) of the document type's parent declares a parent tab for the current tab
                if (parent != null)
                {
                    //and that parent tab has this property on it
                    if (parent.PropertyType.GetProperties().Any(x => x.MetadataToken == property.Metadata.MetadataToken))
                    {
                        //Inherited property. Don't persist at this level.
                        return(true);
                    }
                }
            }
            return(false);
        }
 private void LogPropertySyncInfo(IContentTypeBase contentType, TabRegistration tab, PropertyRegistration property, string action, [CallerMemberName]string sourceMethod = null)
 {
     if (tab == null)
     {
         CodeFirstManager.Current.Log(string.Format("{0} property {1} on content type {2}", action, property.Name, contentType.Name), this, sourceMethod);
     }
     else
     {
         CodeFirstManager.Current.Log(string.Format("{0} property {1} on tab {2} of content type {3}", action, tab.Name, property.Name, contentType.Name), this, sourceMethod);
     }
 }
        private bool IsInheritedProperty(TabRegistration tab, Type documentClrType, PropertyRegistration property)
        {
            //If there is no current tab
            if (tab == null)
            {
                //and the declaring type is not the current type
                if (property.Metadata.DeclaringType != documentClrType)
                {
                    //and the declaring type is also an Umbraco content type
                    if (property.Metadata.DeclaringType.GetCodeFirstAttribute<ContentTypeAttribute>(false) != null)
                    {
                        //Inherited property. Don't persist at this level.
                        return true;
                    }
                }
            }
            //else if the current tab is not directly declared on the current type
            else if (tab.PropertyOfParent.DeclaringType != documentClrType)
            {
                //and the declaring type of the tab is also an Umbraco content type
                if (tab.PropertyOfParent.DeclaringType.GetCodeFirstAttribute<ContentTypeAttribute>(false) != null)
                {
                    //Inherited property. Don't persist at this level.
                    return true;
                }
            }
            //else if the current property is not directly declared on the current tab type
            else if (property.Metadata.DeclaringType != tab.ClrType)
            {
                var parent = documentClrType.BaseType.GetProperties().Where(x => x.IsParentTabFor(tab)).FirstOrDefault();

                //If any property (inherited or otherwise) of the document type's parent declares a parent tab for the current tab
                if (parent != null)
                {
                    //and that parent tab has this property on it
                    if (parent.PropertyType.GetProperties().Any(x => x.MetadataToken == property.Metadata.MetadataToken))
                    {
                        //Inherited property. Don't persist at this level.
                        return true;
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Checks whether a property exists and adds if if it does not. The data type, alias, description and mandatory flag are update for existing properties, but not persisted.
        /// Callers should persist the value.
        /// </summary>
        public PropertyRegistration VerifyExistingProperty(IContentTypeBase contentType, TabRegistration tab, PropertyInfo item, Type documentClrType, ref bool modified)
        {
            ContentPropertyAttribute attribute = item.GetCodeFirstAttribute<ContentPropertyAttribute>();
            var tabPostfix = tab == null || !attribute.AddTabAliasToPropertyAlias ? null : tab.OriginalName == null ? tab.Name : tab.OriginalName;
            var property = new PropertyRegistration();
            var alias = property.Alias = tabPostfix == null ? attribute.Alias : StringHelperExtensions.HyphenToUnderscore(StringHelperExtensions.ParseUrl(attribute.Alias + "_" + tabPostfix, false));
            var dataType = property.DataType = _dataTypeModule.GetDataType(item);
            property.Name = attribute.Name;
            property.PropertyAttribute = attribute;
            property.Metadata = item;

            LogPropertySyncInfo(contentType, tab, property, "Syncing");

            bool alreadyExisted = contentType.PropertyTypeExists(alias);
            PropertyType umbracoProperty = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == alias);

            if (umbracoProperty == null && alreadyExisted)
            {
                //This is a property from an underlying tab. Leave it alone. Log a warning in case this is an orphaned property.
                LogPropertySyncInfo(contentType, tab, property, "Ignoring inherited");
                return property;
            }

            if(alreadyExisted)
            {
                modified = modified ||
                               !umbracoProperty.Name.Equals(attribute.Name, StringComparison.InvariantCultureIgnoreCase) ||
                               umbracoProperty.Mandatory != attribute.Mandatory ||
                               (umbracoProperty.SortOrder != attribute.SortOrder && attribute.SortOrder != 0); //don't count sort order changes if no sort order is specified, as Umbraco will have assigned an automatic one

                if (umbracoProperty.ValidationRegExp != attribute.ValidationRegularExpression)
                {
                    //If not both null/empty
                    if (!(string.IsNullOrEmpty(umbracoProperty.ValidationRegExp) && string.IsNullOrEmpty(attribute.ValidationRegularExpression)))
                    {
                        modified = true;
                        LogPropertySyncInfo(contentType, tab, property, "ValidationRegExp changed on");
                    }
                }

                if (umbracoProperty.Description != attribute.Description)
                {
                    //If not both null/empty
                    if (!(string.IsNullOrEmpty(umbracoProperty.Description) && string.IsNullOrEmpty(attribute.Description)))
                    {
                        modified = true;
                        LogPropertySyncInfo(contentType, tab, property, "Description changed on");
                    }
                }

                if (modified)
                {
                    if (!umbracoProperty.Name.Equals(attribute.Name, StringComparison.InvariantCultureIgnoreCase))
                        LogPropertySyncInfo(contentType, tab, property, "Name changed on");

                    if (umbracoProperty.Mandatory != attribute.Mandatory)
                        LogPropertySyncInfo(contentType, tab, property, "Mandatory changed on");

                    if ((umbracoProperty.SortOrder != attribute.SortOrder && attribute.SortOrder != 0))
                        LogPropertySyncInfo(contentType, tab, property, "SortOrder changed on");
                }
            }

            if (umbracoProperty == null)
            {
                try
                {
                    modified = true;
                    umbracoProperty = new PropertyType(dataType.Definition);
                    LogPropertySyncInfo(contentType, tab, property, "Creating new");
                }
                catch (Exception ex)
                {

                }
            }
            else if (umbracoProperty.DataTypeDefinitionId != dataType.Definition.Id)
            {
                modified = true;
                umbracoProperty.DataTypeDefinitionId = dataType.Definition.Id;
                LogPropertySyncInfo(contentType, tab, property, "Data type changed for");
            }

            umbracoProperty.Name = attribute.Name;
            umbracoProperty.Alias = alias;
            umbracoProperty.Description = attribute.Description;
            umbracoProperty.Mandatory = attribute.Mandatory;
            umbracoProperty.SortOrder = attribute.SortOrder;
            umbracoProperty.ValidationRegExp = attribute.ValidationRegularExpression;

            var propertyDeclaredOnThisDocType = property.Metadata.DeclaringType == documentClrType;
            var propertyDeclaredOnThisTab = tab == null ? false : property.Metadata.DeclaringType == tab.ClrType;
            var tabDeclaredOnThisDocType = tab == null ? false : tab.PropertyOfParent.DeclaringType == documentClrType || tab.PropertyOfParent.DeclaringType.GetCustomAttribute<ContentTypeAttribute>() == null;
            var declaringTypeIsDocType = property.Metadata.DeclaringType.GetCodeFirstAttribute<ContentTypeAttribute>(false) != null;
            var propertyIsFromCommonBase = tab == null ?
                property.Metadata.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == documentClrType.BaseType
                :
                property.Metadata.DeclaringType.GetCodeFirstAttribute<CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == tab.ClrType.BaseType;

            if (alreadyExisted)
            {
                if (propertyIsFromCommonBase || (tabDeclaredOnThisDocType && propertyDeclaredOnThisTab))
                {
                    var currentTab = contentType.PropertyGroups.Where(x => x.PropertyTypes.Any(y => y.Alias == alias)).FirstOrDefault();
                    if (currentTab == null || !currentTab.Name.Equals(tab.Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        modified = true;
                        contentType.MovePropertyType(alias, tab.Name);
                        LogPropertySyncInfo(contentType, tab, property, string.Format("Moved from tab {0}:", tab.Name));
                    }
                }
            }
            else
            {
                if (tab == null)
                {
                    if (propertyDeclaredOnThisDocType || !declaringTypeIsDocType) //only if property declared at this level (or inherited from common base)
                    {
                        if (!propertyIsFromCommonBase || !contentType.PropertyTypeExists(umbracoProperty.Alias)) //check if common base properties already exist
                        {
                            LogPropertySyncInfo(contentType, tab, property, "Adding");
                            contentType.AddPropertyType(umbracoProperty);
                        }
                    }
                }
                else if (tabDeclaredOnThisDocType || propertyIsFromCommonBase) //only if tab declared at this level
                {
                    if (propertyDeclaredOnThisTab || propertyIsFromCommonBase) //only if property declared at this level
                    {
                        if (!propertyIsFromCommonBase || (tabDeclaredOnThisDocType && !contentType.PropertyTypeExists(umbracoProperty.Alias))) //check if common base properties already exist
                        {
                            LogPropertySyncInfo(contentType, tab, property, "Adding");
                            contentType.AddPropertyType(umbracoProperty, tab.Name);
                        }
                    }
                }
            }

            return property;
        }
        /// <summary>
        /// Checks whether a property exists and adds if if it does not. The data type, alias, description and mandatory flag are update for existing properties, but not persisted.
        /// Callers should persist the value.
        /// </summary>
        public PropertyRegistration VerifyExistingProperty(IContentTypeBase contentType, TabRegistration tab, PropertyInfo item, Type documentClrType, ref bool modified)
        {
            ContentPropertyAttribute attribute = item.GetCodeFirstAttribute <ContentPropertyAttribute>();
            var tabPostfix = tab == null || !attribute.AddTabAliasToPropertyAlias ? null : tab.OriginalName == null ? tab.Name : tab.OriginalName;
            var property   = new PropertyRegistration();
            var alias      = property.Alias = tabPostfix == null ? attribute.Alias : StringHelperExtensions.HyphenToUnderscore(StringHelperExtensions.ParseUrl(attribute.Alias + "_" + tabPostfix, false));
            var dataType   = property.DataType = _dataTypeModule.GetDataType(item);

            property.Name = attribute.Name;
            property.PropertyAttribute = attribute;
            property.Metadata          = item;

            LogPropertySyncInfo(contentType, tab, property, "Syncing");

            bool         alreadyExisted  = contentType.PropertyTypeExists(alias);
            PropertyType umbracoProperty = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == alias);

            if (umbracoProperty == null && alreadyExisted)
            {
                //This is a property from an underlying tab. Leave it alone. Log a warning in case this is an orphaned property.
                LogPropertySyncInfo(contentType, tab, property, "Ignoring inherited");
                return(property);
            }

            if (alreadyExisted)
            {
                modified = modified ||
                           !umbracoProperty.Name.Equals(attribute.Name, StringComparison.InvariantCultureIgnoreCase) ||
                           umbracoProperty.Mandatory != attribute.Mandatory ||
                           (umbracoProperty.SortOrder != attribute.SortOrder && attribute.SortOrder != 0);     //don't count sort order changes if no sort order is specified, as Umbraco will have assigned an automatic one

                if (umbracoProperty.ValidationRegExp != attribute.ValidationRegularExpression)
                {
                    //If not both null/empty
                    if (!(string.IsNullOrEmpty(umbracoProperty.ValidationRegExp) && string.IsNullOrEmpty(attribute.ValidationRegularExpression)))
                    {
                        modified = true;
                        LogPropertySyncInfo(contentType, tab, property, "ValidationRegExp changed on");
                    }
                }

                if (umbracoProperty.Description != attribute.Description)
                {
                    //If not both null/empty
                    if (!(string.IsNullOrEmpty(umbracoProperty.Description) && string.IsNullOrEmpty(attribute.Description)))
                    {
                        modified = true;
                        LogPropertySyncInfo(contentType, tab, property, "Description changed on");
                    }
                }

                if (modified)
                {
                    if (!umbracoProperty.Name.Equals(attribute.Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        LogPropertySyncInfo(contentType, tab, property, "Name changed on");
                    }

                    if (umbracoProperty.Mandatory != attribute.Mandatory)
                    {
                        LogPropertySyncInfo(contentType, tab, property, "Mandatory changed on");
                    }

                    if ((umbracoProperty.SortOrder != attribute.SortOrder && attribute.SortOrder != 0))
                    {
                        LogPropertySyncInfo(contentType, tab, property, "SortOrder changed on");
                    }
                }
            }

            if (umbracoProperty == null)
            {
                try
                {
                    modified        = true;
                    umbracoProperty = new PropertyType(dataType.Definition);
                    LogPropertySyncInfo(contentType, tab, property, "Creating new");
                }
                catch (Exception ex)
                {
                }
            }
            else if (umbracoProperty.DataTypeDefinitionId != dataType.Definition.Id)
            {
                modified = true;
                umbracoProperty.DataTypeDefinitionId = dataType.Definition.Id;
                LogPropertySyncInfo(contentType, tab, property, "Data type changed for");
            }

            umbracoProperty.Name             = attribute.Name;
            umbracoProperty.Alias            = alias;
            umbracoProperty.Description      = attribute.Description;
            umbracoProperty.Mandatory        = attribute.Mandatory;
            umbracoProperty.SortOrder        = attribute.SortOrder;
            umbracoProperty.ValidationRegExp = attribute.ValidationRegularExpression;

            var propertyDeclaredOnThisDocType = property.Metadata.DeclaringType == documentClrType;
            var propertyDeclaredOnThisTab     = tab == null ? false : property.Metadata.DeclaringType == tab.ClrType;
            var tabDeclaredOnThisDocType      = tab == null ? false : tab.PropertyOfParent.DeclaringType == documentClrType || tab.PropertyOfParent.DeclaringType.GetCustomAttribute <ContentTypeAttribute>() == null;
            var declaringTypeIsDocType        = property.Metadata.DeclaringType.GetCodeFirstAttribute <ContentTypeAttribute>(false) != null;
            var propertyIsFromCommonBase      = tab == null?
                                                property.Metadata.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == documentClrType.BaseType
                                                :
                                                property.Metadata.DeclaringType.GetCodeFirstAttribute <CodeFirstCommonBaseAttribute>() != null && property.Metadata.DeclaringType == tab.ClrType.BaseType;

            if (alreadyExisted)
            {
                if (propertyIsFromCommonBase || (tabDeclaredOnThisDocType && propertyDeclaredOnThisTab))
                {
                    var currentTab = contentType.PropertyGroups.Where(x => x.PropertyTypes.Any(y => y.Alias == alias)).FirstOrDefault();
                    if (currentTab == null || !currentTab.Name.Equals(tab.Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        modified = true;
                        contentType.MovePropertyType(alias, tab.Name);
                        LogPropertySyncInfo(contentType, tab, property, string.Format("Moved from tab {0}:", tab.Name));
                    }
                }
            }
            else
            {
                if (tab == null)
                {
                    if (propertyDeclaredOnThisDocType || !declaringTypeIsDocType)                                //only if property declared at this level (or inherited from common base)
                    {
                        if (!propertyIsFromCommonBase || !contentType.PropertyTypeExists(umbracoProperty.Alias)) //check if common base properties already exist
                        {
                            LogPropertySyncInfo(contentType, tab, property, "Adding");
                            contentType.AddPropertyType(umbracoProperty);
                        }
                    }
                }
                else if (tabDeclaredOnThisDocType || propertyIsFromCommonBase)                                                                 //only if tab declared at this level
                {
                    if (propertyDeclaredOnThisTab || propertyIsFromCommonBase)                                                                 //only if property declared at this level
                    {
                        if (!propertyIsFromCommonBase || (tabDeclaredOnThisDocType && !contentType.PropertyTypeExists(umbracoProperty.Alias))) //check if common base properties already exist
                        {
                            LogPropertySyncInfo(contentType, tab, property, "Adding");
                            contentType.AddPropertyType(umbracoProperty, tab.Name);
                        }
                    }
                }
            }

            return(property);
        }
        /// <summary>
        /// Scans for properties on the model which have the UmbracoTab attribute
        /// </summary>
        /// <param name="newContentType"></param>
        /// <param name="model"></param>
        /// <param name="dataTypeService"></param>
        private void CreateTabs(IContentTypeBase newContentType, List<TabRegistration> tabRegister, Type contentClrType)
        {
            var properties = contentClrType.GetProperties().Where(x => x.GetCodeFirstAttribute<ContentTabAttribute>() != null).ToArray();
            int length = properties.Length;

            for (int i = 0; i < length; i++)
            {
                var tabAttribute = properties[i].GetCodeFirstAttribute<ContentTabAttribute>();
                var reg = new TabRegistration();
                var props = new List<PropertyRegistration>();
                reg._properties = props;
                reg.ClrType = properties[i].PropertyType;
                reg.Name = tabAttribute.Name;
                reg.OriginalName = tabAttribute.OriginalName;
                reg.TabAttribute = tabAttribute;
                reg.PropertyOfParent = properties[i];

                CodeFirstManager.Current.Log("Creating tab " + tabAttribute.Name + " on content type " + newContentType.Name, this);
                newContentType.AddPropertyGroup(tabAttribute.Name);
                newContentType.PropertyGroups.Where(x => x.Name == tabAttribute.Name).FirstOrDefault().SortOrder = tabAttribute.SortOrder;
                CreateProperties(properties[i], newContentType, reg, props, contentClrType);

                tabRegister.Add(reg);
            }
        }
 /// <summary>
 /// Every property on the Tab object is scanned for the UmbracoProperty attribute
 /// </summary>
 /// <param name="propertyInfo"></param>
 /// <param name="newContentType"></param>
 /// <param name="tabName"></param>
 /// <param name="dataTypeService"></param>
 /// <param name="atTabGeneric"></param>
 private void CreateProperties(PropertyInfo propertyInfo, IContentTypeBase newContentType, TabRegistration tab, List<PropertyRegistration> propertyRegister, Type documentClrType)
 {
     Type type = propertyInfo.PropertyType;
     var properties = type.GetProperties().Where(x => x.GetCustomAttribute<ContentPropertyAttribute>() != null); //Do NOT initialise attributes here, causes exception
     if (properties.Count() > 0)
     {
         foreach (var item in properties)
         {
             var reg = _propertyModule.CreateProperty(newContentType, tab, item, documentClrType);
             propertyRegister.Add(reg);
         }
     }
 }
        /// <summary>
        /// Loop through all properties and remove existing ones if necessary
        /// </summary>
        /// <param name="contentType"></param>
        /// <param name="type"></param>
        /// <param name="dataTypeService"></param>
        private void VerifyProperties(IContentTypeBase contentType, Type documentClrType, List<PropertyRegistration> propertyRegister, List<TabRegistration> tabRegister, ref bool modified)
        {
            var tabProperties = documentClrType.GetProperties().Where(x => x.GetCodeFirstAttribute<ContentTabAttribute>() != null).ToArray();
            var propertiesOfRoot = documentClrType.GetProperties().Where(x => x.GetCodeFirstAttribute<ContentPropertyAttribute>() != null);

            //Remove any properties which aren't in the CF definition
            Prune(contentType, documentClrType, tabProperties, propertiesOfRoot);

            foreach (var tabProperty in tabProperties)
            {
                var tabAttribute = tabProperty.GetCodeFirstAttribute<ContentTabAttribute>();
                var tabReg = new TabRegistration();
                tabReg.TabAttribute = tabAttribute;
                tabReg.Name = tabAttribute.Name;
                tabReg.OriginalName = tabAttribute.OriginalName;
                tabReg.PropertyOfParent = tabProperty;
                CodeFirstManager.Current.Log("Syncing tab " + tabReg.Name + " on content type " + contentType.Name, this);
                VerifyAllPropertiesOnTab(tabProperty, contentType, tabReg, documentClrType, ref modified);
                tabRegister.Add(tabReg);
            }

            foreach (var item in propertiesOfRoot)
            {
                CodeFirstManager.Current.Log("Syncing property " + item.Name + " on content type " + contentType.Name, this);
                var reg = _propertyModule.VerifyExistingProperty(contentType, null, item, documentClrType, ref modified);
                propertyRegister.Add(reg);
            }
        }
        /// <summary>
        /// Scan the properties on tabs
        /// </summary>
        /// <param name="propertyTab"></param>
        /// <param name="contentType"></param>
        /// <param name="tabName"></param>
        /// <returns></returns>
        private IEnumerable<string> VerifyAllPropertiesOnTab(PropertyInfo propertyTab, IContentTypeBase contentType, TabRegistration registration, Type documentType, ref bool modified)
        {
            Type type = propertyTab.PropertyType;
            var properties = type.GetProperties().Where(x => x.GetCustomAttribute<ContentPropertyAttribute>() != null); //Do NOT initialise attribute here, causes initialisation exception
            var props = new List<PropertyRegistration>();
            registration._properties = props;
            registration.ClrType = type;

            if (properties.Count() > 0)
            {
                if (!contentType.PropertyGroups.Any(x => x.Name == registration.Name))
                {
                    contentType.AddPropertyGroup(registration.Name);
                }
                List<string> propertyAliases = new List<string>();
                foreach (var item in properties)
                {
                    CodeFirstManager.Current.Log("Syncing property " + item.Name + " on tab " + registration.Name + " of content type " + contentType.Name, this);
                    var propReg = _propertyModule.VerifyExistingProperty(contentType, registration, item, documentType, ref modified);
                    propertyAliases.Add(propReg.Alias);
                    props.Add(propReg);
                }
                return propertyAliases;
            }
            return new string[0];
        }