diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 964e771850..776714b839 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -4,7 +4,6 @@ using System.Web; using System.Web.Caching; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 2410c1c995..2e5e7f8383 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -102,10 +102,10 @@ namespace Umbraco.Core.Models if(Trashed) return ContentStatus.Trashed; - if(ExpireDate.HasValue && DateTime.UtcNow > ExpireDate.Value) + if(ExpireDate.HasValue && ExpireDate.Value > DateTime.MinValue && DateTime.UtcNow > ExpireDate.Value) return ContentStatus.Expired; - if(ReleaseDate.HasValue && ReleaseDate.Value > DateTime.UtcNow) + if(ReleaseDate.HasValue && ReleaseDate.Value > DateTime.MinValue && ReleaseDate.Value > DateTime.UtcNow) return ContentStatus.AwaitingRelease; if(Published) @@ -156,11 +156,12 @@ namespace Umbraco.Core.Models get { return _releaseDate; } set { - if(value.HasValue && value.Value > DateTime.UtcNow && Published) + //Thought this type of check would be clever, but it only seems to cause problems + /*if(value.HasValue && value.Value > DateTime.UtcNow && Published) _published = false; if (value.HasValue && value.Value < DateTime.UtcNow && !Published) - _published = true; + _published = true;*/ _releaseDate = value; OnPropertyChanged(ReleaseDateSelector); @@ -176,8 +177,9 @@ namespace Umbraco.Core.Models get { return _expireDate; } set { - if(value.HasValue && DateTime.UtcNow > value.Value && Published) - _published = false; + //Thought this type of check would be clever, but it only seems to cause problems + /*if(value.HasValue && DateTime.UtcNow > value.Value && Published) + _published = false;*/ _expireDate = value; OnPropertyChanged(ExpireDateSelector); @@ -311,10 +313,10 @@ namespace Umbraco.Core.Models public override bool IsPropertyDirty(string propertyName) { bool existsInEntity = base.IsPropertyDirty(propertyName); + if (existsInEntity) + return true; - bool anyDirtyProperties = Properties.Any(x => x.IsPropertyDirty(propertyName)); - - return existsInEntity || anyDirtyProperties; + return Properties.Any(x => x.IsPropertyDirty(propertyName)); } /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 23fb4694e2..10151455e7 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -12,6 +12,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Services; namespace Umbraco.Core.Models { @@ -369,6 +370,18 @@ namespace Umbraco.Core.Models } } + /// + /// Checks whether an item has any published versions + /// + /// + /// True if the content has any published versiom otherwise False + public static bool HasPublishedVersion(this IContent content) + { + if (content.HasIdentity == false) + return false; + + return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id); + } /// /// Creates the xml representation for the object diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 13bd19cec6..9d747e4c55 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -7,7 +7,7 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models { /// - /// Represents the contnet type that a object is based on + /// Represents the content type that a object is based on /// [Serializable] [DataContract(IsReference = true)] @@ -81,6 +81,24 @@ namespace Umbraco.Core.Models } } + /// + /// Removes a template from the list of allowed templates + /// + /// to remove + /// True if template was removed, otherwise False + public bool RemoveTemplate(ITemplate template) + { + if (DefaultTemplateId == template.Id) + DefaultTemplateId = default(int); + + var templates = AllowedTemplates.ToList(); + var remove = templates.FirstOrDefault(x => x.Id == template.Id); + var result = templates.Remove(remove); + AllowedTemplates = templates; + + return result; + } + /// /// Indicates whether a specific property on the current entity is dirty. /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index d44b132832..6b01f5b27a 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -149,7 +149,7 @@ namespace Umbraco.Core.Models get { return _alias; } set { - _alias = value; + _alias = value.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); OnPropertyChanged(AliasSelector); } } diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 2d88a56cc0..6583184d76 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Umbraco.Core.Models { diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs index 5e89279755..3e61b98510 100644 --- a/src/Umbraco.Core/Models/IContentType.cs +++ b/src/Umbraco.Core/Models/IContentType.cs @@ -22,5 +22,12 @@ namespace Umbraco.Core.Models /// /// Default void SetDefaultTemplate(ITemplate template); + + /// + /// Removes a template from the list of allowed templates + /// + /// to remove + /// True if template was removed, otherwise False + bool RemoveTemplate(ITemplate template); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index f125c6eaf3..4b50d835fe 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -75,7 +75,7 @@ namespace Umbraco.Core.Models.Rdbms return Text; } - return null; + return string.Empty; } } } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 93d137d1b1..789646df2d 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -54,11 +54,11 @@ namespace Umbraco.Core.Persistence.Factories if (property.HasIdentity) dto.Id = property.Id; - if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer && property.Value != null) + if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false) { dto.Integer = int.Parse(property.Value.ToString()); } - else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null) + else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false) { dto.Date = DateTime.Parse(property.Value.ToString()); } diff --git a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs index c44512a6fb..acfffc8971 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs @@ -2,6 +2,8 @@ using System.Reflection; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Mappers { @@ -15,6 +17,21 @@ namespace Umbraco.Core.Persistence.Mappers public bool MapPropertyToColumn(Type t, PropertyInfo pi, ref string columnName, ref bool resultColumn) { + if (t == typeof (PropertyDataDto)) + { + var tableNameAttribute = t.FirstAttribute(); + var columnAttribute = pi.FirstAttribute(); + + if (tableNameAttribute != null && columnAttribute != null && string.IsNullOrEmpty(columnAttribute.Name) == false) + { + columnName = string.Format("{0}.{1}", + SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName( + tableNameAttribute.Value), + SyntaxConfig.SqlSyntaxProvider.GetQuotedColumnName(columnAttribute.Name)); + } + return true; + } + if (t == typeof(Content) || t == typeof(IContent)) { var mappedName = ContentMapper.Instance.Map(pi.Name); @@ -125,6 +142,16 @@ namespace Umbraco.Core.Persistence.Mappers return true; } + if (t == typeof(Property)) + { + var mappedName = PropertyMapper.Instance.Map(pi.Name); + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } + return true; + } + if (t == typeof(PropertyType)) { var mappedName = PropertyTypeMapper.Instance.Map(pi.Name); @@ -155,6 +182,19 @@ namespace Umbraco.Core.Persistence.Mappers public Func GetToDbConverter(Type sourceType) { + //We need this check to ensure that PetaPoco doesn't try to insert an invalid date from a nullable DateTime property + if (sourceType == typeof (DateTime)) + { + return datetimeVal => + { + var datetime = datetimeVal as DateTime?; + if(datetime.HasValue && datetime.Value > DateTime.MinValue) + return datetime.Value; + + return null; + }; + } + return null; } } diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyMapper.cs new file mode 100644 index 0000000000..795e5a9216 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Mappers/PropertyMapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Mappers +{ + internal sealed class PropertyMapper : BaseMapper + { + private static readonly ConcurrentDictionary PropertyInfoCache = new ConcurrentDictionary(); + + internal static PropertyMapper Instance = new PropertyMapper(); + + private PropertyMapper() + { + BuildMap(); + } + + #region Overrides of BaseMapper + + internal override void BuildMap() + { + CacheMap(src => src.Id, dto => dto.Id); + CacheMap(src => src.Version, dto => dto.VersionId); + CacheMap(src => src.PropertyTypeId, dto => dto.PropertyTypeId); + } + + internal override string Map(string propertyName) + { + if (!PropertyInfoCache.ContainsKey(propertyName)) + return string.Empty; + + var dtoTypeProperty = PropertyInfoCache[propertyName]; + + return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); + } + + internal override void CacheMap(Expression> sourceMember, Expression> destinationMember) + { + var property = base.ResolveMapping(sourceMember, destinationMember); + PropertyInfoCache.AddOrUpdate(property.SourcePropertyName, property, (x, y) => property); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index e825bf78a9..ae0eb39ec9 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -13,6 +13,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security; +using System.Security.Permissions; using System.Text; using System.Configuration; using System.Data.Common; @@ -936,7 +938,7 @@ namespace Umbraco.Core.Persistence // Instance data used by the Multipoco factory delegate - essentially a list of the nested poco factories to call - class MultiPocoFactory + public class MultiPocoFactory { public MultiPocoFactory(IEnumerable dels) @@ -2324,6 +2326,7 @@ namespace Umbraco.Core.Persistence public SqlJoinClause InnerJoin(string table) { return Join("INNER JOIN ", table); } public SqlJoinClause LeftJoin(string table) { return Join("LEFT JOIN ", table); } + public SqlJoinClause LeftOuterJoin(string table) { return Join("LEFT OUTER JOIN ", table); } public SqlJoinClause RightJoin(string table) { return Join("RIGHT JOIN ", table); } public class SqlJoinClause diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs index 8243e47de5..3e8d562d07 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs @@ -129,6 +129,11 @@ namespace Umbraco.Core.Persistence return SyntaxConfig.SqlSyntaxProvider.DoesTableExist(db, tableName); } + public static bool TableExist(this UmbracoDatabase db, string tableName) + { + return SyntaxConfig.SqlSyntaxProvider.DoesTableExist(db, tableName); + } + public static void CreateDatabaseSchema(this Database db) { NewTable += PetaPocoExtensions_NewTable; diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs index 71e6d0c1bd..f38392fcc4 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs @@ -86,6 +86,15 @@ namespace Umbraco.Core.Persistence return sql.LeftJoin(SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName)); } + public static Sql.SqlJoinClause LeftOuterJoin(this Sql sql) + { + var type = typeof(T); + var tableNameAttribute = type.FirstAttribute(); + string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value; + + return sql.LeftOuterJoin(SyntaxConfig.SqlSyntaxProvider.GetQuotedTableName(tableName)); + } + public static Sql.SqlJoinClause RightJoin(this Sql sql) { var type = typeof (T); diff --git a/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs index 72e785e8b3..48d9731149 100644 --- a/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Querying IList updateFields = new List(); IList insertFields = new List(); - private string sep = string.Empty; + private string sep = " "; private bool useFieldName = false; private Database.PocoData pd; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 90c80f2f63..84bf8cb115 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -260,7 +260,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IContent entity) { - //A new version should only be created if published state has changed + //A new version should only be created if published state (or language) has changed bool shouldCreateNewVersion = ((ICanBeDirty)entity).IsPropertyDirty("Published") || ((ICanBeDirty)entity).IsPropertyDirty("Language"); if (shouldCreateNewVersion) { @@ -298,7 +298,7 @@ namespace Umbraco.Core.Persistence.Repositories } //If Published state has changed then previous versions should have their publish state reset - if (shouldCreateNewVersion && entity.Published) + if (((ICanBeDirty)entity).IsPropertyDirty("Published") && entity.Published) { var publishedDocs = Database.Fetch("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true }); foreach (var doc in publishedDocs) @@ -374,7 +374,7 @@ namespace Umbraco.Core.Persistence.Repositories { var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); var uploadFieldId = new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"); - //Loop through properties to check if the content contains media that should be deleted + //Loop through properties to check if the content contains images/files that should be deleted foreach (var property in entity.Properties) { if (property.PropertyType.DataTypeControlId == uploadFieldId && diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index bb73c7c7d3..299b2e2e0a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -91,11 +91,11 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); - var documentTypeDtos = Database.Fetch(sql); + var dtos = Database.Fetch(sql); - foreach (var dto in documentTypeDtos) + foreach (var dto in dtos.DistinctBy(x => x.ContentTypeDto.NodeId)) { - yield return Get(dto.ContentTypeNodeId); + yield return Get(dto.ContentTypeDto.NodeId); } } @@ -138,6 +138,7 @@ namespace Umbraco.Core.Persistence.Repositories string.Format("DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id"), string.Format("DELETE FROM cmsTagRelationship WHERE nodeId = @Id"), string.Format("DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id"), + string.Format("DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id"), string.Format("DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id"), string.Format("DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id"), string.Format("DELETE FROM cmsPropertyType WHERE contentTypeId = @Id"), @@ -185,6 +186,13 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date ((ContentType)entity).UpdatingEntity(); + //Look up parent to get and set the correct Path if ParentId has changed + if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) + { + var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); + entity.Path = string.Concat(parent.Path, ",", entity.Id); + } + var factory = new ContentTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index ff42b191a7..83e57db965 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -153,6 +153,13 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date and Version Guid ((DataTypeDefinition)entity).UpdatingEntity(); + //Look up parent to get and set the correct Path if ParentId has changed + if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) + { + var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); + entity.Path = string.Concat(parent.Path, ",", entity.Id); + } + var factory = new DataTypeDefinitionFactory(NodeObjectTypeId); //Look up DataTypeDefinition entry to get Primary for updating the DTO var dataTypeDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 70726b7671..d63360031d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; @@ -234,6 +236,13 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date ((Models.Media)entity).UpdatingEntity(); + //Look up parent to get and set the correct Path if ParentId has changed + if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) + { + var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); + entity.Path = string.Concat(parent.Path, ",", entity.Id); + } + var factory = new MediaFactory(NodeObjectTypeId, entity.Id); //Look up Content entry to get Primary for updating the DTO var contentDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); @@ -287,6 +296,36 @@ namespace Umbraco.Core.Persistence.Repositories ((ICanBeDirty)entity).ResetDirtyProperties(); } + protected override void PersistDeletedItem(IMedia entity) + { + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + var uploadFieldId = new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"); + //Loop through properties to check if the media item contains images/file that should be deleted + foreach (var property in entity.Properties) + { + if (property.PropertyType.DataTypeControlId == uploadFieldId && + string.IsNullOrEmpty(property.Value.ToString()) == false + && fs.FileExists(IOHelper.MapPath(property.Value.ToString()))) + { + var relativeFilePath = fs.GetRelativePath(property.Value.ToString()); + var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath); + + // don't want to delete the media folder if not using directories. + if (UmbracoSettings.UploadAllowDirectories && parentDirectory != fs.GetRelativePath("/")) + { + //issue U4-771: if there is a parent directory the recursive parameter should be true + fs.DeleteDirectory(parentDirectory, String.IsNullOrEmpty(parentDirectory) == false); + } + else + { + fs.DeleteFile(relativeFilePath, true); + } + } + } + + base.PersistDeletedItem(entity); + } + #endregion private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType) diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 51705e1adf..17035d725d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -124,6 +124,7 @@ namespace Umbraco.Core.Persistence.Repositories string.Format("DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id"), string.Format("DELETE FROM cmsTagRelationship WHERE nodeId = @Id"), string.Format("DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id"), + string.Format("DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id"), string.Format("DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id"), string.Format("DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id"), string.Format("DELETE FROM cmsPropertyType WHERE contentTypeId = @Id"), @@ -160,6 +161,13 @@ namespace Umbraco.Core.Persistence.Repositories //Updates Modified date ((MediaType)entity).UpdatingEntity(); + //Look up parent to get and set the correct Path if ParentId has changed + if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) + { + var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId }); + entity.Path = string.Concat(parent.Path, ",", entity.Id); + } + var factory = new MediaTypeFactory(NodeObjectTypeId); var dto = factory.BuildDto(entity); diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index ec42e6dee2..3b2d8e753f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -125,7 +125,7 @@ namespace Umbraco.Core.Persistence.Repositories { entity.AddingEntity(); - var factory = new RelationFactory(null); + var factory = new RelationFactory(entity.RelationType); var dto = factory.BuildDto(entity); var id = Convert.ToInt32(Database.Insert(dto)); @@ -138,7 +138,7 @@ namespace Umbraco.Core.Persistence.Repositories { entity.UpdatingEntity(); - var factory = new RelationFactory(null); + var factory = new RelationFactory(entity.RelationType); var dto = factory.BuildDto(entity); Database.Update(dto); diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 47d2b60c95..88565f29e9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -226,6 +226,13 @@ namespace Umbraco.Core.Persistence.Repositories } } + //Look up parent to get and set the correct Path if ParentId has changed + if (((ICanBeDirty)entity).IsPropertyDirty("ParentId")) + { + var parent = Database.First("WHERE id = @ParentId", new { ParentId = ((Template)entity).ParentId }); + entity.Path = string.Concat(parent.Path, ",", entity.Id); + } + //Get TemplateDto from db to get the Primary key of the entity var templateDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); //Save updated entity to db diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index c05f9e4a60..2c428fab52 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -1,6 +1,8 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -18,7 +20,9 @@ using System.Runtime.InteropServices; // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("130a6b5c-50e7-4df3-a0dd-e9e7eb0b7c5c")] -//[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)] +//[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)] +//[assembly: AllowPartiallyTrustedCallers] +//[assembly: SecurityTransparent] [assembly: InternalsVisibleTo("umbraco")] [assembly: InternalsVisibleTo("Umbraco.Tests")] diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index af61a4891f..61cdd4bf45 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; @@ -354,6 +353,24 @@ namespace Umbraco.Core.Services } } + /// + /// Checks if the passed in can be published based on the anscestors publish state. + /// + /// to check if anscestors are published + /// True if the Content can be published, otherwise False + public bool IsPublishable(IContent content) + { + //If the passed in content has yet to be saved we "fallback" to checking the Parent + //because if the Parent is publishable then the current content can be Saved and Published + if (content.HasIdentity == false) + { + IContent parent = GetById(content.ParentId); + return IsPublishable(parent, true); + } + + return IsPublishable(content, false); + } + /// /// Re-Publishes all Content /// @@ -442,11 +459,11 @@ namespace Umbraco.Core.Services //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published - if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) + if (content.ParentId != -1 && content.ParentId != -20 && IsPublishable(content) == false) { LogHelper.Info( string.Format( - "Content '{0}' with Id '{1}' could not be published because its parent is not published.", + "Content '{0}' with Id '{1}' could not be published because its parent or one of its ancestors is not published.", content.Name, content.Id)); return false; } @@ -518,17 +535,7 @@ namespace Umbraco.Core.Services { //TODO Refactor this so omitCacheRefresh isn't exposed in the public method, but only in an internal one as its purely there for legacy reasons. - //Look for children and unpublish them if any exists, otherwise just unpublish the passed in Content. - var children = GetChildrenDeep(content.Id); - var hasChildren = children.Any(); - - if (hasChildren) - children.Add(content); - - var unpublished = hasChildren - ? _publishingStrategy.UnPublish(children, userId) - : _publishingStrategy.UnPublish(content, userId); - + var unpublished = _publishingStrategy.UnPublish(content, userId); if (unpublished) { var uow = _uowProvider.GetUnitOfWork(); @@ -536,24 +543,8 @@ namespace Umbraco.Core.Services { repository.AddOrUpdate(content); - if (hasChildren) - { - foreach (var child in children) - { - SetWriter(child, userId); - repository.AddOrUpdate(child); - } - } - - //Remove 'published' xml from the cmsContentXml table for the unpublished content and its (possible) children + //Remove 'published' xml from the cmsContentXml table for the unpublished content uow.Database.Delete("WHERE nodeId = @Id", new {Id = content.Id}); - if (hasChildren) - { - foreach (var child in children) - { - uow.Database.Delete("WHERE nodeId = @Id", new {Id = child.Id}); - } - } uow.Commit(); } @@ -581,7 +572,7 @@ namespace Umbraco.Core.Services return false; //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published - if (content.ParentId != -1 && content.ParentId != -20 && HasPublishedVersion(content.ParentId) == false) + if (content.ParentId != -1 && content.ParentId != -20 && IsPublishable(content) == false) { LogHelper.Info( string.Format( @@ -616,22 +607,30 @@ namespace Umbraco.Core.Services { var xml = content.ToXml(); var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToString(SaveOptions.None) }; - var exists = - uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = content.Id }) != - null; + var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new {Id = content.Id}) != null; int result = exists ? uow.Database.Update(poco) : Convert.ToInt32(uow.Database.Insert(poco)); } } + Saved.RaiseEvent(new SaveEventArgs(content, false), this); + //Save xml to db and call following method to fire event through PublishingStrategy to update cache if (omitCacheRefresh == false) _publishingStrategy.PublishingFinalized(content); + + //We need to check if children and their publish state to ensure that we republish content that was previously published + if (HasChildren(content.Id)) + { + var children = GetChildrenDeep(content.Id); + var shouldBeRepublished = children.Where(child => HasPublishedVersion(child.Id)); - Saved.RaiseEvent(new SaveEventArgs(content, false), this); + if (omitCacheRefresh == false) + _publishingStrategy.PublishingFinalized(shouldBeRepublished, false); + } - Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); + Audit.Add(AuditTypes.Publish, "Save and Publish performed by user", userId == -1 ? 0 : userId, content.Id); return published; } @@ -687,7 +686,6 @@ namespace Umbraco.Core.Services { foreach (var content in contents) { - SetWriter(content, userId); //Only change the publish state if the "previous" version was actually published @@ -978,10 +976,13 @@ namespace Umbraco.Core.Services /// Optional Id of the User copying the Content /// The newly created object public IContent Copy(IContent content, int parentId, bool relateToOriginal, int userId = -1) - { + { var copy = ((Content)content).Clone(); copy.ParentId = parentId; - copy.Name = copy.Name + " (1)"; + + // A copy should never be set to published + // automatically even if the original was + this.UnPublish(copy); if (Copying.IsRaisedEventCancelled(new CopyEventArgs(content, copy, parentId), this)) return null; @@ -990,7 +991,7 @@ namespace Umbraco.Core.Services using (var repository = _repositoryFactory.CreateContentRepository(uow)) { SetWriter(content, userId); - + repository.AddOrUpdate(copy); uow.Commit(); @@ -1101,7 +1102,6 @@ namespace Umbraco.Core.Services /// The newly created object public IContent Rollback(int id, Guid versionId, int userId = -1) { - var content = GetByVersion(versionId); if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this)) @@ -1148,7 +1148,7 @@ namespace Umbraco.Core.Services /// /// Id of the parent to retrieve children from /// A list of valid that can be published - private List GetChildrenDeep(int parentId) + private IEnumerable GetChildrenDeep(int parentId) { var list = new List(); var children = GetChildren(parentId); @@ -1163,6 +1163,40 @@ namespace Umbraco.Core.Services return list; } + /// + /// Checks if the passed in can be published based on the anscestors publish state. + /// + /// + /// Check current is only used when falling back to checking the Parent of non-saved content, as + /// non-saved content doesn't have a valid path yet. + /// + /// to check if anscestors are published + /// Boolean indicating whether the passed in content should also be checked for published versions + /// True if the Content can be published, otherwise False + private bool IsPublishable(IContent content, bool checkCurrent) + { + var ids = content.Path.Split(',').Select(int.Parse).ToList(); + foreach (var id in ids) + { + //If Id equals that of the recycle bin we return false because nothing in the bin can be published + if (id == -20) + return false; + + //We don't check the System Root, so just continue + if (id == -1) continue; + + //If the current id equals that of the passed in content and if current shouldn't be checked we skip it. + if (checkCurrent == false && id == content.Id) continue; + + //Check if the content for the current id is published - escape the loop if we encounter content that isn't published + var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.GetById(id).Published; + if (hasPublishedVersion == false) + return false; + } + + return true; + } + /// /// Updates a content object with the User (id), who created the content. /// diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 5ecf8b05f8..bae66f95a5 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -109,6 +109,21 @@ namespace Umbraco.Core.Services } } + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the content type has any children otherwise False + public bool HasChildren(int id) + { + using (var repository = _repositoryFactory.CreateContentTypeRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + int count = repository.Count(query); + return count > 0; + } + } + /// /// Saves a single object /// @@ -278,6 +293,21 @@ namespace Umbraco.Core.Services } } + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the media type has any children otherwise False + public bool MediaTypeHasChildren(int id) + { + using (var repository = _repositoryFactory.CreateMediaTypeRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + int count = repository.Count(query); + return count > 0; + } + } + /// /// Saves a single object /// diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 11d21dbd9f..4130da307a 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -213,7 +213,7 @@ namespace Umbraco.Core.Services /// /// Alias of the template /// A object - public ITemplate GetTemplateByAlias(string alias) + public ITemplate GetTemplate(string alias) { using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) { @@ -221,6 +221,19 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a object by its alias + /// + /// Id of the template + /// A object + public ITemplate GetTemplate(int id) + { + using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + /// /// Saves a /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 4836f25736..4e4a400b51 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -270,5 +270,12 @@ namespace Umbraco.Core.Services /// Optional Id of the User copying the Content /// The newly created object IContent Copy(IContent content, int parentId, bool relateToOriginal, int userId = -1); + + /// + /// Checks if the passed in can be published based on the anscestors publish state. + /// + /// to check if anscestors are published + /// True if the Content can be published, otherwise False + bool IsPublishable(IContent content); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index d51eb3a784..f9ee765c9b 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -136,5 +136,19 @@ namespace Umbraco.Core.Services /// /// The DTD as a string string GetContentTypesDtd(); + + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the content type has any children otherwise False + bool HasChildren(int id); + + /// + /// Checks whether an item has any children + /// + /// Id of the + /// True if the media type has any children otherwise False + bool MediaTypeHasChildren(int id); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 58839f7673..8793987ca6 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -87,7 +87,14 @@ namespace Umbraco.Core.Services /// /// Alias of the template /// A object - ITemplate GetTemplateByAlias(string alias); + ITemplate GetTemplate(string alias); + + /// + /// Gets a object by its alias + /// + /// Id of the template + /// A object + ITemplate GetTemplate(int id); /// /// Saves a diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 81351de603..e1b8fbb05c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -247,6 +247,7 @@ + diff --git a/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs b/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs index 4b1a00d7ca..eb1d9ee8fd 100644 --- a/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs +++ b/src/Umbraco.Tests/CodeFirst/Definitions/ContentTypeDefinitionFactory.cs @@ -318,7 +318,7 @@ namespace Umbraco.Tests.CodeFirst.Definitions ? templateName.Replace(".cshtml", "").Replace(".vbhtml", "") : templateName.Replace(".masterpage", ""); - var template = ApplicationContext.Current.Services.FileService.GetTemplateByAlias(@alias); + var template = ApplicationContext.Current.Services.FileService.GetTemplate(@alias); if(template == null) { var name = engine == RenderingEngine.Mvc diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs index f5a7cf11ca..37a57a6bf6 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Persistence.Querying .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") .InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]") .InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]") - .Where("nodeObjectType='c66ba18e-eaf3-4cff-8a22-41b16d66a972'"); + .Where("nodeObjectType = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'"); var sql = new Sql(); sql.Select("*") @@ -49,8 +49,8 @@ namespace Umbraco.Tests.Persistence.Querying .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") .InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]") .InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]") - .Where("nodeObjectType='c66ba18e-eaf3-4cff-8a22-41b16d66a972'") - .Where("id=1050"); + .Where("nodeObjectType = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'") + .Where("id = 1050"); var sql = new Sql(); sql.Select("*") @@ -81,9 +81,9 @@ namespace Umbraco.Tests.Persistence.Querying .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") .InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]") .InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]") - .Where("nodeObjectType='c66ba18e-eaf3-4cff-8a22-41b16d66a972'") - .Where("id=1050") - .Where("VersionId='2b543516-a944-4ee6-88c6-8813da7aaa07'") + .Where("nodeObjectType = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'") + .Where("id = 1050") + .Where("VersionId = '2b543516-a944-4ee6-88c6-8813da7aaa07'") .OrderBy("[cmsContentVersion].[VersionDate] DESC"); var sql = new Sql(); diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs index d9fca798e7..6a72713323 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs @@ -21,8 +21,8 @@ namespace Umbraco.Tests.Persistence.Querying .On("[cmsContentType].[nodeId] = [cmsDocumentType].[contentTypeNodeId]") .InnerJoin("[umbracoNode]") .On("[cmsContentType].[nodeId] = [umbracoNode].[id]") - .Where("nodeObjectType='a2cb7800-f571-4787-9638-bc48539a0efb'") - .Where("IsDefault='True'"); + .Where("nodeObjectType = 'a2cb7800-f571-4787-9638-bc48539a0efb'") + .Where("IsDefault = 'True'"); var sql = new Sql(); sql.Select("*") @@ -51,9 +51,9 @@ namespace Umbraco.Tests.Persistence.Querying .On("[cmsContentType].[nodeId] = [cmsDocumentType].[contentTypeNodeId]") .InnerJoin("[umbracoNode]") .On("[cmsContentType].[nodeId] = [umbracoNode].[id]") - .Where("nodeObjectType='a2cb7800-f571-4787-9638-bc48539a0efb'") - .Where("IsDefault='True'") - .Where("id=1050"); + .Where("nodeObjectType = 'a2cb7800-f571-4787-9638-bc48539a0efb'") + .Where("IsDefault = 'True'") + .Where("id = 1050"); var sql = new Sql(); sql.Select("*") diff --git a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs index 0e57c019bb..8037e347aa 100644 --- a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Persistence.Querying public void Can_Use_Where_Predicate() { var expected = new Sql(); - expected.Select("*").From("[cmsContent]").Where("nodeId=1045"); + expected.Select("*").From("[cmsContent]").Where("nodeId = 1045"); var sql = new Sql(); sql.Select("*").From().Where(x => x.NodeId == 1045); @@ -90,8 +90,8 @@ namespace Umbraco.Tests.Persistence.Querying var expected = new Sql(); expected.Select("*") .From("[cmsContent]") - .Where("nodeId=1045") - .Where("contentType=1050"); + .Where("nodeId = 1045") + .Where("contentType = 1050"); var sql = new Sql(); sql.Select("*") diff --git a/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs index 2fc46144c1..46bc9099ac 100644 --- a/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs @@ -48,7 +48,7 @@ namespace Umbraco.Tests.Persistence.Querying var result = translator.Translate(); var strResult = result.SQL; - string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([umbracoNode].[parentID]=-1)"; + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([umbracoNode].[parentID] = -1)"; // Assert Assert.That(strResult, Is.Not.Empty); @@ -71,7 +71,7 @@ namespace Umbraco.Tests.Persistence.Querying var result = translator.Translate(); var strResult = result.SQL; - string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([cmsContentType].[alias]='umbTextpage')"; + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([cmsContentType].[alias] = 'umbTextpage')"; // Assert Assert.That(strResult, Is.Not.Empty); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 39befcf58a..49e29d2324 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -308,7 +308,15 @@ namespace Umbraco.Tests.Services Assert.That(content.Published, Is.False); } - [Test] + /// + /// This test is ignored because the way children are handled when + /// parent is unpublished is treated differently now then from when this test + /// was written. + /// The correct case is now that Root is UnPublished removing the children + /// from cache, but still having them "Published" in the "background". + /// Once the Parent is Published the Children should re-appear as published. + /// + [Test, NUnit.Framework.Ignore] public void Can_UnPublish_Root_Content_And_Verify_Children_Is_UnPublished() { // Arrange @@ -707,7 +715,27 @@ namespace Umbraco.Tests.Services Assert.That(c2.Value.ParentId > 0, Is.True); } - private IEnumerable CreateContentHierarchy() + [Test] + public void Can_Verify_Content_Has_Published_Version() + { + // Arrange + var contentService = ServiceContext.ContentService; + var content = contentService.GetById(1046); + bool published = contentService.PublishWithChildren(content, 0); + var homepage = contentService.GetById(1046); + homepage.Name = "Homepage"; + ServiceContext.ContentService.Save(homepage); + + // Act + bool hasPublishedVersion = ServiceContext.ContentService.HasPublishedVersion(1046); + + // Assert + Assert.That(published, Is.True); + Assert.That(homepage.Published, Is.False); + Assert.That(hasPublishedVersion, Is.True); + } + + private IEnumerable CreateContentHierarchy() { var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); var root = ServiceContext.ContentService.GetById(1046); diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index f7fdad504a..d6edcff895 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -10,14 +10,14 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - + - - - - + + + + @@ -26,8 +26,8 @@ NOTES: - - + + @@ -41,20 +41,12 @@ NOTES: --> - + - +