From 0c4c429b6892b73c20a00ee8d821ee4105c127bb Mon Sep 17 00:00:00 2001 From: sitereactor Date: Mon, 29 Oct 2012 14:28:16 -0100 Subject: [PATCH] Implements U4-943 to the extent its currently possible. Refactoring publishing methods in the ContentService. Adding tests for the QueryBuilder. --- src/Umbraco.Core/EventArgs.cs | 32 ++++ src/Umbraco.Core/Models/Content.cs | 4 +- src/Umbraco.Core/Models/EntityBase/Entity.cs | 2 +- .../Models/EntityBase/ICanBeDirty.cs | 1 + src/Umbraco.Core/Models/IContent.cs | 13 ++ .../Persistence/Mappers/ContentMapper.cs | 3 + .../Persistence/Mappers/ModelDtoMapper.cs | 96 ++++++------ src/Umbraco.Core/Persistence/PetaPoco.cs | 4 +- .../Persistence/Querying/ExpressionHelper.cs | 2 +- .../Persistence/Querying/Query.cs | 6 +- .../Persistence/Querying/SqlTranslator.cs | 4 + src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Persistence/Querying/QueryBuilderTests.cs | 82 +++++++++++ .../TestHelpers/BaseUsingSqlCeSyntax.cs | 20 +++ src/Umbraco.Tests/Umbraco.Tests.csproj | 5 + .../Publishing/BasePublishingStrategy.cs | 87 +++++++++++ .../Publishing/IPublishingStrategy.cs | 1 - .../Publishing/PublishingStrategy.cs | 116 ++++++++++----- src/Umbraco.Web/Services/ContentService.cs | 138 +++++++++++++----- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 20 files changed, 490 insertions(+), 128 deletions(-) create mode 100644 src/Umbraco.Core/EventArgs.cs create mode 100644 src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs create mode 100644 src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs create mode 100644 src/Umbraco.Web/Publishing/BasePublishingStrategy.cs diff --git a/src/Umbraco.Core/EventArgs.cs b/src/Umbraco.Core/EventArgs.cs new file mode 100644 index 0000000000..406cc780db --- /dev/null +++ b/src/Umbraco.Core/EventArgs.cs @@ -0,0 +1,32 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core +{ + //Publishing Events + public class PublishEventArgs : System.ComponentModel.CancelEventArgs { } + public class UnPublishEventArgs : System.ComponentModel.CancelEventArgs { } + public class SendToPublishEventArgs : System.ComponentModel.CancelEventArgs { } + + //Moving Content Events + public class MoveEventArgs : System.ComponentModel.CancelEventArgs { } + public class MoveToTrashEventArgs : System.ComponentModel.CancelEventArgs { } + + //Copying Content Events + public class CopyEventArgs : System.ComponentModel.CancelEventArgs + { + public int CopyTo { get; set; } + public IContent NewContent { get; set; } + } + + //Rollback Content Event + public class RollbackEventArgs : System.ComponentModel.CancelEventArgs { } + + //Content Cache Event args + public class ContentCacheEventArgs : System.ComponentModel.CancelEventArgs { } + public class RefreshContentEventArgs : System.ComponentModel.CancelEventArgs { } + + //Generel eventArgs + public class DeleteEventArgs : System.ComponentModel.CancelEventArgs { } + public class SaveEventArgs : System.ComponentModel.CancelEventArgs { } + public class NewEventArgs : System.ComponentModel.CancelEventArgs { } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 8ec7cf5e5e..e70078a857 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -225,7 +225,7 @@ namespace Umbraco.Core.Models /// Changes the Published state of the content object /// /// Boolean indicating whether content is published (true) or unpublished (false) - internal void ChangePublishedState(bool isPublished) + public void ChangePublishedState(bool isPublished) { Published = isPublished; //NOTE Should this be checked against the Expire/Release dates? @@ -237,7 +237,7 @@ namespace Umbraco.Core.Models /// /// Boolean indicating whether content is trashed (true) or not trashed (false) /// - internal void ChangeTrashedState(bool isTrashed, int parentId = -1) + public void ChangeTrashedState(bool isTrashed, int parentId = -1) { Trashed = isTrashed; diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index 109cf08779..059c0f6d77 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -136,7 +136,7 @@ namespace Umbraco.Core.Models.EntityBase /// /// Resets dirty properties by clearing the dictionary used to track changes. /// - internal void ResetDirtyProperties() + public void ResetDirtyProperties() { _propertyChangedInfo.Clear(); } diff --git a/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs b/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs index 34dcc2d830..2f5a6725e1 100644 --- a/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs +++ b/src/Umbraco.Core/Models/EntityBase/ICanBeDirty.cs @@ -7,5 +7,6 @@ { bool IsDirty(); bool IsPropertyDirty(string propName); + void ResetDirtyProperties(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index d6aafe009f..461680aa50 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -52,5 +52,18 @@ namespace Umbraco.Core.Models /// New ContentType for this content /// Boolean indicating whether to clear PropertyTypes upon change void ChangeContentType(IContentType contentType, bool clearProperties); + + /// + /// Changes the Published state of the content object + /// + /// Boolean indicating whether content is published (true) or unpublished (false) + void ChangePublishedState(bool isPublished); + + /// + /// Changes the Trashed state of the content object + /// + /// Boolean indicating whether content is trashed (true) or not trashed (false) + /// + void ChangeTrashedState(bool isTrashed, int parentId = -1); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs index df0115312c..f6fe2f31e3 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs @@ -52,6 +52,9 @@ namespace Umbraco.Core.Persistence.Mappers internal override string Map(string propertyName) { + if (!PropertyInfoCache.ContainsKey(propertyName)) + return string.Empty; + var dtoTypeProperty = PropertyInfoCache[propertyName]; return base.GetColumnName(dtoTypeProperty.Type, dtoTypeProperty.PropertyInfo); diff --git a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs index d03ef82b22..cc5b8dc5de 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ModelDtoMapper.cs @@ -12,29 +12,29 @@ namespace Umbraco.Core.Persistence.Mappers public void GetTableInfo(Type t, TableInfo ti) { } - public bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn) + public bool MapPropertyToColumn(Type t, PropertyInfo pi, ref string columnName, ref bool resultColumn) { - if (pi.DeclaringType == typeof(Content) || pi.DeclaringType == typeof(IContent)) + if (t == typeof(Content) || t == typeof(IContent)) { var mappedName = ContentMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(Models.Media) || pi.DeclaringType == typeof(IMedia)) + if (t == typeof(Models.Media) || t == typeof(IMedia)) { var mappedName = MediaMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(ContentType) || pi.DeclaringType == typeof(IContentType) || pi.DeclaringType == typeof(IMediaType)) + if (t == typeof(ContentType) || t == typeof(IContentType) || t == typeof(IMediaType)) { var mappedName = ContentTypeMapper.Instance.Map(pi.Name); if (!string.IsNullOrEmpty(mappedName)) @@ -44,47 +44,47 @@ namespace Umbraco.Core.Persistence.Mappers return true; } - if (pi.DeclaringType == typeof(DataTypeDefinition) || pi.DeclaringType == typeof(IDataTypeDefinition)) + if (t == typeof(DataTypeDefinition) || t == typeof(IDataTypeDefinition)) { var mappedName = DataTypeDefinitionMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(DictionaryItem) || pi.DeclaringType == typeof(IDictionaryItem)) + if (t == typeof(DictionaryItem) || t == typeof(IDictionaryItem)) { var mappedName = DictionaryMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(DictionaryTranslation) || pi.DeclaringType == typeof(IDictionaryTranslation)) + if (t == typeof(DictionaryTranslation) || t == typeof(IDictionaryTranslation)) { var mappedName = DictionaryTranslationMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(Language) || pi.DeclaringType == typeof(ILanguage)) + if (t == typeof(Language) || t == typeof(ILanguage)) { var mappedName = LanguageMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(Relation)) + if (t == typeof(Relation)) { var mappedName = RelationMapper.Instance.Map(pi.Name); if (!string.IsNullOrEmpty(mappedName)) @@ -94,33 +94,33 @@ namespace Umbraco.Core.Persistence.Mappers return true; } - if (pi.DeclaringType == typeof(RelationType)) + if (t == typeof(RelationType)) { var mappedName = RelationTypeMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(PropertyType)) + if (t == typeof(PropertyType)) { var mappedName = PropertyTypeMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } - if (pi.DeclaringType == typeof(PropertyGroup)) + if (t == typeof(PropertyGroup)) { var mappedName = PropertyGroupMapper.Instance.Map(pi.Name); - if (mappedName == string.Empty) - return false; - - columnName = mappedName; + if (!string.IsNullOrEmpty(mappedName)) + { + columnName = mappedName; + } return true; } diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 13bf593dcb..ca18d8f80d 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -118,7 +118,7 @@ namespace Umbraco.Core.Persistence public interface IMapper { void GetTableInfo(Type t, TableInfo ti); - bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn); + bool MapPropertyToColumn(Type t, PropertyInfo pi, ref string columnName, ref bool resultColumn); Func GetFromDbConverter(PropertyInfo pi, Type SourceType); Func GetToDbConverter(Type SourceType); } @@ -1802,7 +1802,7 @@ namespace Umbraco.Core.Persistence if (pc.ColumnName == null) { pc.ColumnName = pi.Name; - if (Database.Mapper != null && !Database.Mapper.MapPropertyToColumn(pi, ref pc.ColumnName, ref pc.ResultColumn)) + if (Database.Mapper != null && !Database.Mapper.MapPropertyToColumn(t, pi, ref pc.ColumnName, ref pc.ResultColumn)) continue; } diff --git a/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs index 4a97dc571c..e1dfa3c9e7 100644 --- a/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ExpressionHelper.cs @@ -265,7 +265,7 @@ namespace Umbraco.Core.Persistence.Querying case "ToLower": return string.Format("lower({0})", r); case "StartsWith": - return string.Format("upper({0}) starting with {1} ", r, args[0].ToString().ToUpper()); + return string.Format("upper({0}) like '{1}%'", r, RemoveQuote(args[0].ToString().ToUpper())); case "EndsWith": return string.Format("upper({0}) like '%{1}'", r, RemoveQuote(args[0].ToString()).ToUpper()); case "Contains": diff --git a/src/Umbraco.Core/Persistence/Querying/Query.cs b/src/Umbraco.Core/Persistence/Querying/Query.cs index 6d2bd5c769..8c098884c4 100644 --- a/src/Umbraco.Core/Persistence/Querying/Query.cs +++ b/src/Umbraco.Core/Persistence/Querying/Query.cs @@ -4,6 +4,10 @@ using System.Linq.Expressions; namespace Umbraco.Core.Persistence.Querying { + /// + /// Represents the Query Builder for building LINQ translatable queries + /// + /// public class Query : IQuery { private readonly ExpressionHelper _expresionist = new ExpressionHelper(); @@ -32,7 +36,7 @@ namespace Umbraco.Core.Persistence.Querying } return this; } - + public List WhereClauses() { return _wheres; diff --git a/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs b/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs index 1e08b08b1e..a99be90cd3 100644 --- a/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs +++ b/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs @@ -2,6 +2,10 @@ namespace Umbraco.Core.Persistence.Querying { + /// + /// Represents the Sql Translator for translating a object to Sql + /// + /// public class SqlTranslator { private readonly Sql _sql; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e671c7064a..3ac230829e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -70,6 +70,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs new file mode 100644 index 0000000000..905b6a08ef --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs @@ -0,0 +1,82 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Querying +{ + [TestFixture] + public class QueryBuilderTests : BaseUsingSqlCeSyntax + { + [Test] + public void Can_Build_StartsWith_Query_For_IContent() + { + // Arrange + var sql = new Sql(); + sql.Select("*"); + sql.From("umbracoNode"); + + var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); + + // Act + var translator = new SqlTranslator(sql, query); + var result = translator.Translate(); + var strResult = result.SQL; + + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (upper([umbracoNode].[path]) like '-1%')"; + + // Assert + Assert.That(strResult, Is.Not.Empty); + Assert.That(strResult, Is.EqualTo(expectedResult)); + Console.WriteLine(strResult); + } + + [Test] + public void Can_Build_ParentId_Query_For_IContent() + { + // Arrange + var sql = new Sql(); + sql.Select("*"); + sql.From("umbracoNode"); + + var query = Query.Builder.Where(x => x.ParentId == -1); + + // Act + var translator = new SqlTranslator(sql, query); + var result = translator.Translate(); + var strResult = result.SQL; + + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (([umbracoNode].[parentID]=-1))"; + + // Assert + Assert.That(strResult, Is.Not.Empty); + Assert.That(strResult, Is.EqualTo(expectedResult)); + Console.WriteLine(strResult); + } + + [Test] + public void Can_Build_ContentTypeAlias_Query_For_IContentType() + { + // Arrange + var sql = new Sql(); + sql.Select("*"); + sql.From("umbracoNode"); + + var query = Query.Builder.Where(x => x.Alias == "umbTextpage"); + + // Act + var translator = new SqlTranslator(sql, query); + var result = translator.Translate(); + var strResult = result.SQL; + + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (([cmsContentType].[alias]='umbTextpage'))"; + + // Assert + Assert.That(strResult, Is.Not.Empty); + Assert.That(strResult, Is.EqualTo(expectedResult)); + Console.WriteLine(strResult); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs new file mode 100644 index 0000000000..903c15ec1f --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Tests.TestHelpers +{ + [TestFixture] + public abstract class BaseUsingSqlCeSyntax + { + [SetUp] + public virtual void Initialize() + { + SyntaxConfig.SqlSyntaxProvider = SqlCeSyntaxProvider.Instance; + + SetUp(); + } + + public virtual void SetUp() + {} + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 048cd5a5ac..ce68116365 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -109,6 +109,7 @@ + @@ -144,6 +145,7 @@ + @@ -261,6 +263,9 @@ + + + xcopy "$(ProjectDir)"..\..\lib\SQLCE4\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /D diff --git a/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs b/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs new file mode 100644 index 0000000000..87843b01ca --- /dev/null +++ b/src/Umbraco.Web/Publishing/BasePublishingStrategy.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core; + +namespace Umbraco.Web.Publishing +{ + public abstract class BasePublishingStrategy : IPublishingStrategy + { + public abstract bool Publish(IContent content, int userId); + public abstract bool PublishWithChildren(IEnumerable children, int userId); + public abstract bool UnPublish(IContent content, int userId); + + /// + /// The publish event handler + /// + public delegate void PublishEventHandler(IContent sender, PublishEventArgs e); + + /// + /// The unpublish event handler + /// + public delegate void UnPublishEventHandler(IContent sender, UnPublishEventArgs e); + + /// + /// Occurs before publish + /// + public static event PublishEventHandler BeforePublish; + + /// + /// Raises the event + /// + /// + /// The instance containing the event data. + protected virtual void FireBeforePublish(IContent content, PublishEventArgs e) + { + if (BeforePublish != null) + BeforePublish(content, e); + } + + /// + /// Occurs after publish + /// + public static event PublishEventHandler AfterPublish; + + /// + /// Raises the event + /// + /// + /// The instance containing the event data. + protected virtual void FireAfterPublish(IContent content, PublishEventArgs e) + { + if (AfterPublish != null) + AfterPublish(content, e); + } + + /// + /// Occurs before unpublish + /// + public static event UnPublishEventHandler BeforeUnPublish; + + /// + /// Raises the event + /// + /// + /// The instance containing the event data. + protected virtual void FireBeforeUnPublish(IContent content, UnPublishEventArgs e) + { + if (BeforeUnPublish != null) + BeforeUnPublish(content, e); + } + + /// + /// Occurs after unpublish + /// + public static event UnPublishEventHandler AfterUnPublish; + + /// + /// Raises the event + /// + /// + /// The instance containing the event data. + protected virtual void FireAfterUnPublish(IContent content, UnPublishEventArgs e) + { + if (AfterUnPublish != null) + AfterUnPublish(content, e); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Publishing/IPublishingStrategy.cs b/src/Umbraco.Web/Publishing/IPublishingStrategy.cs index 0d61b8602b..fc85c94150 100644 --- a/src/Umbraco.Web/Publishing/IPublishingStrategy.cs +++ b/src/Umbraco.Web/Publishing/IPublishingStrategy.cs @@ -7,7 +7,6 @@ namespace Umbraco.Web.Publishing { bool Publish(IContent content, int userId); bool PublishWithChildren(IEnumerable children, int userId); - void PublishWithSubs(IContent content, int userId); bool UnPublish(IContent content, int userId); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Publishing/PublishingStrategy.cs b/src/Umbraco.Web/Publishing/PublishingStrategy.cs index 0f0cb80a7c..b4184af061 100644 --- a/src/Umbraco.Web/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Web/Publishing/PublishingStrategy.cs @@ -1,67 +1,109 @@ using System.Collections.Generic; -using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; -using umbraco.BusinessLogic; -using umbraco.cms.businesslogic.web; namespace Umbraco.Web.Publishing { /// /// Currently acts as an interconnection between the new public api and the legacy api for publishing /// - internal class PublishingStrategy : IPublishingStrategy + internal class PublishingStrategy : BasePublishingStrategy { internal PublishingStrategy() { } - public bool Publish(IContent content, int userId) + /// + /// Publishes a single piece of content + /// + /// to publish + /// Id of the user issueing the publish + /// True if the content was published, otherwise false + public override bool Publish(IContent content, int userId) { //Fire BeforePublish event - /*PublishEventArgs e = new PublishEventArgs(); - FireBeforePublish(e);*/ - - //Create new (unpublished) version - Guid is returned - //Log Publish - //Update all cmsDocument entries with Id to newest=0 - //Insert new version in cmsDocument table - //Set Release and Expire date on newly created version + var e = new PublishEventArgs(); + FireBeforePublish(content, e); - // Update xml in db using the new document (has correct version date) - //newDoc.XmlGenerate(new XmlDocument()); + if (!e.Cancel) + { + content.ChangePublishedState(true); - //Fire AfterPublish event - //FireAfterPublish(e); + //Fire AfterPublish event + FireAfterPublish(content, e); - //Updating the cache is not done in the Document-Publish methods, so this part should be added - //global::umbraco.library.UpdateDocumentCache(doc.Id); + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' has been published.", + content.Name, content.Id)); - int contentId = content.Id; + //NOTE: Ideally the xml cache should be refreshed here - as part of the publishing - var doc = new Document(contentId, true); - var user = new User(userId); - - return doc.PublishWithResult(user); + return true; + } + + return false; } - public bool PublishWithChildren(IEnumerable children, int userId) + /// + /// Publishes a list of content + /// + /// List of to publish + /// Id of the user issueing the publish + /// True if the content was published, otherwise false + public override bool PublishWithChildren(IEnumerable children, int userId) { - int contentId = children.Last().Id; - var doc = new Document(contentId, true); - var user = new User(userId); - return doc.PublishWithChildrenWithResult(user); + //Fire BeforePublish event + var e = new PublishEventArgs(); + + if (e.Cancel) + return false; + + foreach (var content in children) + { + FireBeforePublish(content, e); + + content.ChangePublishedState(true); + + //Fire AfterPublish event + FireAfterPublish(content, e); + + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' has been published.", + content.Name, content.Id)); + } + + //NOTE: Ideally the xml cache should be refreshed here - as part of the publishing + + return true; } - public void PublishWithSubs(IContent content, int userId) + /// + /// Unpublishes a single piece of content + /// + /// to unpublish + /// Id of the user issueing the unpublish + /// True is the content was unpublished, otherwise false + public override bool UnPublish(IContent content, int userId) { - int contentId = content.Id; - var doc = new Document(contentId, true); - var user = new User(userId); - doc.PublishWithSubs(user); - } + var e = new UnPublishEventArgs(); + FireBeforeUnPublish(content, e); + + if (!e.Cancel) + { + content.ChangePublishedState(false); + + FireAfterUnPublish(content, e); + + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' has been unpublished.", + content.Name, content.Id)); + + //NOTE: Ideally the xml cache should be refreshed here - as part of the unpublishing + + return true; + } - public bool UnPublish(IContent content, int userId) - { return false; } } diff --git a/src/Umbraco.Web/Services/ContentService.cs b/src/Umbraco.Web/Services/ContentService.cs index 7992e560bb..305000bc1d 100644 --- a/src/Umbraco.Web/Services/ContentService.cs +++ b/src/Umbraco.Web/Services/ContentService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; @@ -204,27 +205,34 @@ namespace Umbraco.Web.Services var list = new List(); - //Consider creating a Path query instead of recursive method + //Consider creating a Path query instead of recursive method: //var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); + var rootContent = GetRootContent(); foreach (var content in rootContent) { - list.AddRange(GetChildrenDeep(content.Id)); - } - - foreach (var item in list) - { - //Only publish valid content - Might need to change the flat list as it could pose problems for children of invalid content - if(item.IsValid()) + if(content.IsValid()) { - ((Content)item).ChangePublishedState(true); - repository.AddOrUpdate(item); + list.AddRange(GetChildrenDeep(content.Id)); } } - unitOfWork.Commit(); + //Publish and then update the database with new status + var published = _publishingStrategy.PublishWithChildren(list, userId); + if (published) + { + foreach (var item in list) + { + repository.AddOrUpdate(item); + } - return _publishingStrategy.PublishWithChildren(list, userId); + unitOfWork.Commit(); + + //TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class. + global::umbraco.library.RefreshContent(); + } + + return published; } /// @@ -249,25 +257,48 @@ namespace Umbraco.Web.Services var unitOfWork = _provider.GetUnitOfWork(); var repository = RepositoryResolver.ResolveByType(unitOfWork); - //Consider creating a Path query instead of recursive method + //Check if parent is published - if parent isn't published this Content cannot be published + var parent = GetById(content.ParentId); + if (!parent.Published) + { + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' could not be published because its parent is not published.", + content.Name, content.Id)); + return false; + } + + //Content contains invalid property values and can therefore not be published - fire event? + if (!content.IsValid()) + { + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.", + content.Name, content.Id)); + return false; + } + + //Consider creating a Path query instead of recursive method: //var query = Query.Builder.Where(x => x.Path.StartsWith(content.Path)); + var list = GetChildrenDeep(content.Id); list.Add(content); - foreach (var item in list) + //Publish and then update the database with new status + var published = _publishingStrategy.PublishWithChildren(list, userId); + if (published) { - //TODO Test and correct this so children of unpublished content isn't published. - //Only publish valid content - Might need to change the flat list as it could pose problems for children of invalid content - if (item.IsValid()) + foreach (var item in list) { - ((Content) item).ChangePublishedState(true); repository.AddOrUpdate(item); } + + unitOfWork.Commit(); + + //TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class. + //TODO Need to investigate if it will also update the cache for children of the Content object + global::umbraco.library.UpdateDocumentCache(content.Id); } - unitOfWork.Commit(); - - return _publishingStrategy.PublishWithChildren(list, userId); + return published; } /// @@ -281,26 +312,42 @@ namespace Umbraco.Web.Services var unitOfWork = _provider.GetUnitOfWork(); var repository = RepositoryResolver.ResolveByType(unitOfWork); - ((Content)content).ChangePublishedState(false); - repository.AddOrUpdate(content); - unitOfWork.Commit(); + var unpublished = _publishingStrategy.UnPublish(content, userId); - return _publishingStrategy.UnPublish(content, userId); + if (unpublished) + { + repository.AddOrUpdate(content); + unitOfWork.Commit(); + + //TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content class. + global::umbraco.library.UnPublishSingleNode(content.Id); + } + + return unpublished; } /// /// Gets a flat list of decendents of content from parent id /// - /// - /// + /// + /// Only contains valid objects, which means + /// that everything in the returned list can be published. + /// If an invalid object is found it will not + /// be added to the list neither will its children. + /// + /// Id of the parent to retrieve children from + /// A list of valid that can be published private List GetChildrenDeep(int parentId) { var list = new List(); var children = GetChildren(parentId); foreach (var child in children) { - list.Add(child); - list.AddRange(GetChildrenDeep(child.Id)); + if (child.IsValid()) + { + list.Add(child); + list.AddRange(GetChildrenDeep(child.Id)); + } } return list; } @@ -316,16 +363,37 @@ namespace Umbraco.Web.Services var unitOfWork = _provider.GetUnitOfWork(); var repository = RepositoryResolver.ResolveByType(unitOfWork); - //NOTE: Should also check if parent is published - if parent isn't published this Content cannot be published + //Check if parent is published - if parent isn't published this Content cannot be published + var parent = GetById(content.ParentId); + if(!parent.Published) + { + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' could not be published because its parent is not published.", + content.Name, content.Id)); + return false; + } + //Content contains invalid property values and can therefore not be published - fire event? if (!content.IsValid()) - return false;//Content contains invalid property values and can therefore not be published + { + LogHelper.Info( + string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.", + content.Name, content.Id)); + return false; + } - ((Content)content).ChangePublishedState(true); - repository.AddOrUpdate(content); - unitOfWork.Commit(); + //Publish and then update the database with new status + bool published = _publishingStrategy.Publish(content, userId); + if (published) + { + repository.AddOrUpdate(content); + unitOfWork.Commit(); - return _publishingStrategy.Publish(content, userId); + //TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class. + global::umbraco.library.UpdateDocumentCache(content.Id); + } + + return published; } /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 39b9dda9d4..528e295273 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -306,6 +306,7 @@ +