diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 5b7da056b7..5bbc607b59 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -173,8 +173,7 @@ namespace Umbraco.Core return new ServiceContext( new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), new PetaPocoUnitOfWorkProvider(scopeProvider), - new FileUnitOfWorkProvider(scopeProvider), - new PublishingStrategy(msgFactory, ProfilingLogger.Logger), + new FileUnitOfWorkProvider(scopeProvider), ApplicationCache, ProfilingLogger.Logger, msgFactory); diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs index f2c295adb5..10f32143c7 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Logging; +using System; +using Umbraco.Core.Logging; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork @@ -8,6 +9,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// public class FileUnitOfWorkProvider : ScopeUnitOfWorkProvider { + [Obsolete("Use the ctor specifying a IScopeProvider instead")] public FileUnitOfWorkProvider() : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) { diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs index e5a76cc94e..5a6a1a2888 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs @@ -4,6 +4,8 @@ namespace Umbraco.Core.Persistence.UnitOfWork { public interface IScopeUnitOfWork : IDatabaseUnitOfWork { + EventMessages Messages { get; } + IEventManager EventManager { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs index 8bae95e5dd..1578e0e06a 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs @@ -161,6 +161,11 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return Scope.Events; } } + public EventMessages Messages + { + get { return Scope.Messages; } + } + protected Queue Operations { get { return _operations; } diff --git a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs index 63f32c41c5..6c08822a71 100644 --- a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs @@ -1,11 +1,12 @@ +using System; using System.Collections.Generic; +using System.ComponentModel; using Umbraco.Core.Models; namespace Umbraco.Core.Publishing { - /// - /// Abstract class for the implementation of an , which provides the events used for publishing/unpublishing. - /// + [Obsolete("This class is not intended to be used and will be removed in future versions, see IPublishingStrategy instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public abstract class BasePublishingStrategy : IPublishingStrategy { diff --git a/src/Umbraco.Core/Publishing/IPublishingStrategy.cs b/src/Umbraco.Core/Publishing/IPublishingStrategy.cs index 0408409488..93be145779 100644 --- a/src/Umbraco.Core/Publishing/IPublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/IPublishingStrategy.cs @@ -1,9 +1,80 @@ using System.Collections.Generic; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Publishing { /// + /// TODO: This is a compatibility hack, we want to get rid of IPublishingStrategy all together or just have it as an internal + /// helper class but we can't just remove it now, we also cannot just change it, so the current IPublishingStrategy one will simply not be used + /// in our own code, if for some odd reason someone else is using it, then fine it will continue to work with hacks but won't be used by us. + /// + internal interface IPublishingStrategy2 + { + /// + /// Publishes a single piece of Content + /// + /// + /// to publish + /// Id of the User issueing the publish operation + /// True if the publish operation was successfull and not cancelled, otherwise false + Attempt Publish(IScopeUnitOfWork uow, IContent content, int userId); + + /// + /// Publishes a list of Content + /// + /// + /// An enumerable list of + /// Id of the User issueing the publish operation + /// + /// True if the publish operation was successfull and not cancelled, otherwise false + IEnumerable> PublishWithChildren(IScopeUnitOfWork uow, IEnumerable content, int userId, bool includeUnpublishedDocuments); + + /// + /// Unpublishes a single piece of Content + /// + /// + /// to unpublish + /// Id of the User issueing the unpublish operation + /// True if the unpublish operation was successfull and not cancelled, otherwise false + Attempt UnPublish(IScopeUnitOfWork uow, IContent content, int userId); + + /// + /// Call to fire event that updating the published content has finalized. + /// + /// + /// This seperation of the OnPublished event is done to ensure that the Content + /// has been properly updated (committed unit of work) and xml saved in the db. + /// + /// + /// thats being published + void PublishingFinalized(IScopeUnitOfWork uow, IContent content); + + /// + /// Call to fire event that updating the published content has finalized. + /// + /// + /// An enumerable list of thats being published + /// Boolean indicating whether its all content that is republished + void PublishingFinalized(IScopeUnitOfWork uow, IEnumerable content, bool isAllRepublished); + + /// + /// Call to fire event that updating the unpublished content has finalized. + /// + /// + /// thats being unpublished + void UnPublishingFinalized(IScopeUnitOfWork uow, IContent content); + + /// + /// Call to fire event that updating the unpublished content has finalized. + /// + /// + /// An enumerable list of thats being unpublished + void UnPublishingFinalized(IScopeUnitOfWork uow, IEnumerable content); + } + + /// + /// TODO: This should be obsoleted but if we did that then the Publish/Unpublish events on the content service would show that the param is obsoleted /// Defines the Publishing Strategy /// public interface IPublishingStrategy diff --git a/src/Umbraco.Core/Publishing/PublishingStrategy.cs b/src/Umbraco.Core/Publishing/PublishingStrategy.cs index c6d4f19baf..4d8627ac5d 100644 --- a/src/Umbraco.Core/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/PublishingStrategy.cs @@ -1,42 +1,57 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; namespace Umbraco.Core.Publishing { - //TODO: Do we need this anymore?? + //TODO: Do we need this anymore?? - get rid of it! + /// /// Currently acts as an interconnection between the new public api and the legacy api for publishing /// - public class PublishingStrategy : BasePublishingStrategy + [EditorBrowsable(EditorBrowsableState.Never)] + public class PublishingStrategy : BasePublishingStrategy, IPublishingStrategy2 { - private readonly IEventMessagesFactory _eventMessagesFactory; + private readonly IScopeProvider _scopeProvider; private readonly ILogger _logger; + [Obsolete("This class is not intended to be used, it will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] public PublishingStrategy(IEventMessagesFactory eventMessagesFactory, ILogger logger) { - if (eventMessagesFactory == null) throw new ArgumentNullException("eventMessagesFactory"); if (logger == null) throw new ArgumentNullException("logger"); - _eventMessagesFactory = eventMessagesFactory; + _scopeProvider = new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger)); + _logger = logger; + } + + public PublishingStrategy(ILogger logger) + { + if (logger == null) throw new ArgumentNullException("logger"); _logger = logger; } /// /// Publishes a single piece of Content /// + /// /// to publish /// Id of the User issueing the publish operation - internal Attempt PublishInternal(IContent content, int userId) + Attempt IPublishingStrategy2.Publish(IScopeUnitOfWork uow, IContent content, int userId) { - var evtMsgs = _eventMessagesFactory.Get(); + + var evtMsgs = uow.Messages; if (Publishing.IsRaisedEventCancelled( - new PublishEventArgs(content, evtMsgs), this)) + new PublishEventArgs(content, evtMsgs), this, uow.EventManager)) { _logger.Info( string.Format("Content '{0}' with Id '{1}' will not be published, the event was cancelled.", content.Name, content.Id)); @@ -87,12 +102,17 @@ namespace Umbraco.Core.Publishing /// True if the publish operation was successfull and not cancelled, otherwise false public override bool Publish(IContent content, int userId) { - return PublishInternal(content, userId).Success; + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + uow.Commit(); + return ((IPublishingStrategy2)this).Publish(uow, content, userId).Success; + } } /// /// Publishes a list of content items /// + /// /// /// /// @@ -120,8 +140,8 @@ namespace Umbraco.Core.Publishing /// the user definitely wants to publish it even if it has never been published before. /// /// - internal IEnumerable> PublishWithChildrenInternal( - IEnumerable content, int userId, bool includeUnpublishedDocuments = true) + IEnumerable> IPublishingStrategy2.PublishWithChildren(IScopeUnitOfWork uow, + IEnumerable content, int userId, bool includeUnpublishedDocuments) { var statuses = new List>(); @@ -136,7 +156,7 @@ namespace Umbraco.Core.Publishing // Because we're grouping I think this will execute all the queries anyways so need to fetch it all first. var fetchedContent = content.ToArray(); - var evtMsgs = _eventMessagesFactory.Get(); + var evtMsgs = uow.Messages; //We're going to populate the statuses with all content that is already published because below we are only going to iterate over // content that is not published. We'll set the status to "AlreadyPublished" @@ -181,7 +201,7 @@ namespace Umbraco.Core.Publishing //Fire Publishing event if (Publishing.IsRaisedEventCancelled( - new PublishEventArgs(item, evtMsgs), this)) + new PublishEventArgs(item, evtMsgs), this, uow.EventManager)) { //the publishing has been cancelled. _logger.Info( @@ -280,13 +300,13 @@ namespace Umbraco.Core.Publishing // any document that fails to publish... var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id); - if (hasPublishedVersion && !includeUnpublishedDocuments) + if (hasPublishedVersion && includeUnpublishedDocuments == false) { //it has a published version but our flag tells us to not include un-published documents and therefore we should // not be forcing decendant/child documents to be published if their parent fails. parentsIdsCancelled.Add(content.Id); } - else if (!hasPublishedVersion) + else if (hasPublishedVersion == false) { //it doesn't have a published version so we certainly cannot publish it's children. parentsIdsCancelled.Add(content.Id); @@ -301,15 +321,21 @@ namespace Umbraco.Core.Publishing /// True if the publish operation was successfull and not cancelled, otherwise false public override bool PublishWithChildren(IEnumerable content, int userId) { - var result = PublishWithChildrenInternal(content, userId); + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + var result = ((IPublishingStrategy2)this).PublishWithChildren(uow, content, userId, true); - //NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)... - // ... if one item couldn't be published it wouldn't be correct to return false. - // in retrospect it should have returned a list of with Ids and Publish Status - // come to think of it ... the cache would still be updated for a failed item or at least tried updated. - // It would call the Published event for the entire list, but if the Published property isn't set to True it - // wouldn't actually update the cache for that item. But not really ideal nevertheless... - return true; + uow.Commit(); + + //NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)... + // ... if one item couldn't be published it wouldn't be correct to return false. + // in retrospect it should have returned a list of with Ids and Publish Status + // come to think of it ... the cache would still be updated for a failed item or at least tried updated. + // It would call the Published event for the entire list, but if the Published property isn't set to True it + // wouldn't actually update the cache for that item. But not really ideal nevertheless... + return true; + } + } /// @@ -320,21 +346,26 @@ namespace Umbraco.Core.Publishing /// True if the unpublish operation was successfull and not cancelled, otherwise false public override bool UnPublish(IContent content, int userId) { - return UnPublishInternal(content, userId).Success; + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + uow.Commit(); + return ((IPublishingStrategy2)this).UnPublish(uow, content, userId).Success; + } } /// /// Unpublishes a list of Content /// + /// /// An enumerable list of /// Id of the User issueing the unpublish operation /// A list of publish statuses - private IEnumerable> UnPublishInternal(IEnumerable content, int userId) + private IEnumerable> UnPublishInternal(IScopeUnitOfWork uow, IEnumerable content, int userId) { - return content.Select(x => UnPublishInternal(x, userId)); + return content.Select(x => ((IPublishingStrategy2)this).UnPublish(uow, x, userId)); } - private Attempt UnPublishInternal(IContent content, int userId) + Attempt IPublishingStrategy2.UnPublish(IScopeUnitOfWork uow, IContent content, int userId) { // content should (is assumed to ) be the newest version, which may not be published // don't know how to test this, so it's not verified @@ -342,11 +373,11 @@ namespace Umbraco.Core.Publishing // if published != newest, then the published flags need to be reseted by whoever is calling that method // at the moment it's done by the content service - var evtMsgs = _eventMessagesFactory.Get(); + var evtMsgs = uow.Messages; //Fire UnPublishing event if (UnPublishing.IsRaisedEventCancelled( - new PublishEventArgs(content, evtMsgs), this)) + new PublishEventArgs(content, evtMsgs), this, uow.EventManager)) { _logger.Info( string.Format("Content '{0}' with Id '{1}' will not be unpublished, the event was cancelled.", content.Name, content.Id)); @@ -383,15 +414,21 @@ namespace Umbraco.Core.Publishing /// True if the unpublish operation was successfull and not cancelled, otherwise false public override bool UnPublish(IEnumerable content, int userId) { - var result = UnPublishInternal(content, userId); + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + var result = UnPublishInternal(uow, content, userId); + uow.Commit(); - //NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)... - // ... if one item couldn't be published it wouldn't be correct to return false. - // in retrospect it should have returned a list of with Ids and Publish Status - // come to think of it ... the cache would still be updated for a failed item or at least tried updated. - // It would call the Published event for the entire list, but if the Published property isn't set to True it - // wouldn't actually update the cache for that item. But not really ideal nevertheless... - return true; + //NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)... + // ... if one item couldn't be published it wouldn't be correct to return false. + // in retrospect it should have returned a list of with Ids and Publish Status + // come to think of it ... the cache would still be updated for a failed item or at least tried updated. + // It would call the Published event for the entire list, but if the Published property isn't set to True it + // wouldn't actually update the cache for that item. But not really ideal nevertheless... + return true; + } + + } /// @@ -404,9 +441,12 @@ namespace Umbraco.Core.Publishing /// thats being published public override void PublishingFinalized(IContent content) { - var evtMsgs = _eventMessagesFactory.Get(); - Published.RaiseEvent( - new PublishEventArgs(content, false, false, evtMsgs), this); + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + ((IPublishingStrategy2) this).PublishingFinalized(uow, content); + uow.Commit(); + } + } /// @@ -416,10 +456,11 @@ namespace Umbraco.Core.Publishing /// Boolean indicating whether its all content that is republished public override void PublishingFinalized(IEnumerable content, bool isAllRepublished) { - var evtMsgs = _eventMessagesFactory.Get(); - Published.RaiseEvent( - new PublishEventArgs(content, false, isAllRepublished, evtMsgs), this); - + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + ((IPublishingStrategy2)this).PublishingFinalized(uow, content, isAllRepublished); + uow.Commit(); + } } /// @@ -428,9 +469,11 @@ namespace Umbraco.Core.Publishing /// thats being unpublished public override void UnPublishingFinalized(IContent content) { - var evtMsgs = _eventMessagesFactory.Get(); - UnPublished.RaiseEvent( - new PublishEventArgs(content, false, false, evtMsgs), this); + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + ((IPublishingStrategy2)this).UnPublishingFinalized(uow, content); + uow.Commit(); + } } /// @@ -439,31 +482,68 @@ namespace Umbraco.Core.Publishing /// An enumerable list of thats being unpublished public override void UnPublishingFinalized(IEnumerable content) { - var evtMsgs = _eventMessagesFactory.Get(); - UnPublished.RaiseEvent( - new PublishEventArgs(content, false, false, evtMsgs), this); + using (var uow = new ScopeUnitOfWork(_scopeProvider)) + { + ((IPublishingStrategy2)this).UnPublishingFinalized(uow, content); + uow.Commit(); + } } /// /// Occurs before publish /// + [Obsolete("Use events on the ContentService")] + [EditorBrowsable(EditorBrowsableState.Never)] public static event TypedEventHandler> Publishing; /// /// Occurs after publish /// + [Obsolete("Use events on the ContentService")] + [EditorBrowsable(EditorBrowsableState.Never)] public static event TypedEventHandler> Published; /// /// Occurs before unpublish /// + [Obsolete("Use events on the ContentService")] + [EditorBrowsable(EditorBrowsableState.Never)] public static event TypedEventHandler> UnPublishing; /// /// Occurs after unpublish /// - public static event TypedEventHandler> UnPublished; + [Obsolete("Use events on the ContentService")] + [EditorBrowsable(EditorBrowsableState.Never)] + public static event TypedEventHandler> UnPublished; + + void IPublishingStrategy2.PublishingFinalized(IScopeUnitOfWork uow, IContent content) + { + var evtMsgs = uow.Messages; + Published.RaiseEvent( + new PublishEventArgs(content, false, false, evtMsgs), this, uow.EventManager); + } + void IPublishingStrategy2.PublishingFinalized(IScopeUnitOfWork uow, IEnumerable content, bool isAllRepublished) + { + var evtMsgs = uow.Messages; + Published.RaiseEvent( + new PublishEventArgs(content, false, isAllRepublished, evtMsgs), this, uow.EventManager); + } + void IPublishingStrategy2.UnPublishingFinalized(IScopeUnitOfWork uow, IContent content) + { + var evtMsgs = uow.Messages; + UnPublished.RaiseEvent( + new PublishEventArgs(content, false, false, evtMsgs), this, uow.EventManager); + } + + void IPublishingStrategy2.UnPublishingFinalized(IScopeUnitOfWork uow, IEnumerable content) + { + var evtMsgs = uow.Messages; + UnPublished.RaiseEvent( + new PublishEventArgs(content, false, false, evtMsgs), this, uow.EventManager); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index 26e8b7cb49..f7b26470e6 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Scoping /// /// Gets the scope event messages. /// - IList Messages { get; } + EventMessages Messages { get; } /// /// Gets the event manager diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 4f8c705f28..f39fc1a2af 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Scoping private UmbracoDatabase _database; private IEventManager _eventManager; - private IList _messages; + private EventMessages _messages; public NoScope(ScopeProvider scopeProvider) { @@ -60,16 +60,16 @@ namespace Umbraco.Core.Scoping } /// - public IList Messages + public EventMessages Messages { get { EnsureNotDisposed(); - return _messages ?? (_messages = new List()); + return _messages ?? (_messages = new EventMessages()); } } - public IList MessagesOrNull + public EventMessages MessagesOrNull { get { diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index f0e58f1f9c..9b857659d4 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Scoping private IsolatedRuntimeCache _isolatedRuntimeCache; private UmbracoDatabase _database; - private IList _messages; + private EventMessages _messages; private IDictionary> _exitActions; private IEventManager _eventManager; @@ -166,13 +166,13 @@ namespace Umbraco.Core.Scoping //} /// - public IList Messages + public EventMessages Messages { get { EnsureNotDisposed(); if (ParentScope != null) return ParentScope.Messages; - return _messages ?? (_messages = new List()); + return _messages ?? (_messages = new EventMessages()); } } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index ac128f567d..92895c5fdd 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -31,7 +31,7 @@ namespace Umbraco.Core.Services /// public class ContentService : ScopeRepositoryService, IContentService, IContentServiceOperations { - private readonly IPublishingStrategy _publishingStrategy; + private readonly IPublishingStrategy2 _publishingStrategy; private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer(); private readonly IDataTypeService _dataTypeService; private readonly IUserService _userService; @@ -44,16 +44,14 @@ namespace Umbraco.Core.Services IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, - IEventMessagesFactory eventMessagesFactory, - IPublishingStrategy publishingStrategy, + IEventMessagesFactory eventMessagesFactory, IDataTypeService dataTypeService, IUserService userService) : base(provider, repositoryFactory, logger, eventMessagesFactory) { - if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy"); if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); if (userService == null) throw new ArgumentNullException("userService"); - _publishingStrategy = publishingStrategy; + _publishingStrategy = new PublishingStrategy(logger); _dataTypeService = dataTypeService; _userService = userService; } @@ -1715,7 +1713,7 @@ namespace Umbraco.Core.Services if (content.Published) { //TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable! - var published = _publishingStrategy.Publish(content, userId); + var published = _publishingStrategy.Publish(uow, content, userId).Success; shouldBePublished.Add(content); } else @@ -1736,13 +1734,14 @@ namespace Umbraco.Core.Services if (raiseEvents) Saved.RaiseEvent(new SaveEventArgs(asArray, false), this, uow.EventManager); - } - if (shouldBePublished.Any()) - { - //TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable! - _publishingStrategy.PublishingFinalized(shouldBePublished, false); + if (shouldBePublished.Any()) + { + //TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable! + _publishingStrategy.PublishingFinalized(uow, shouldBePublished, false); + } } + } Audit(AuditType.Sort, "Sorting content performed by user", userId, 0); @@ -1973,17 +1972,18 @@ namespace Umbraco.Core.Services list.Add(content); //include parent item list.AddRange(GetDescendants(content)); - var internalStrategy = (PublishingStrategy)_publishingStrategy; + var internalStrategy = _publishingStrategy; + + var uow = UowProvider.GetUnitOfWork(); //Publish and then update the database with new status - var publishedOutcome = internalStrategy.PublishWithChildrenInternal(list, userId, includeUnpublished).ToArray(); + var publishedOutcome = internalStrategy.PublishWithChildren(uow, list, userId, includeUnpublished).ToArray(); var published = publishedOutcome .Where(x => x.Success || x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished) // ensure proper order (for events) - cannot publish a child before its parent! .OrderBy(x => x.Result.ContentItem.Level) .ThenBy(x => x.Result.ContentItem.SortOrder); - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { //NOTE The Publish with subpages-dialog was used more as a republish-type-thing, so we'll have to include PublishStatusType.SuccessAlreadyPublished @@ -2001,9 +2001,10 @@ namespace Umbraco.Core.Services uow.Commit(); + //Save xml to db and call following method to fire event: + _publishingStrategy.PublishingFinalized(uow, updated, false); } - //Save xml to db and call following method to fire event: - _publishingStrategy.PublishingFinalized(updated, false); + Audit(AuditType.Publish, "Publish with Children performed by user", userId, content.Id); @@ -2033,10 +2034,15 @@ namespace Umbraco.Core.Services return Attempt.Succeed(new UnPublishStatus(content, UnPublishedStatusType.SuccessAlreadyUnPublished, evtMsgs)); // already unpublished } - var unpublished = _publishingStrategy.UnPublish(content, userId); - if (unpublished == false) return Attempt.Fail(new UnPublishStatus(content, UnPublishedStatusType.FailedCancelledByEvent, evtMsgs)); - var uow = UowProvider.GetUnitOfWork(); + + var unpublished = _publishingStrategy.UnPublish(uow, content, userId); + if (unpublished == false) + { + uow.Commit(); + return Attempt.Fail(new UnPublishStatus(content, UnPublishedStatusType.FailedCancelledByEvent, evtMsgs)); + } + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { content.WriterId = userId; @@ -2047,10 +2053,12 @@ namespace Umbraco.Core.Services repository.DeleteContentXml(content); uow.Commit(); + + //Delete xml from db? and call following method to fire event through PublishingStrategy to update cache + if (omitCacheRefresh == false) + _publishingStrategy.UnPublishingFinalized(uow, content); } - //Delete xml from db? and call following method to fire event through PublishingStrategy to update cache - if (omitCacheRefresh == false) - _publishingStrategy.UnPublishingFinalized(content); + Audit(AuditType.UnPublish, "UnPublish performed by user", userId, content.Id); @@ -2094,20 +2102,21 @@ namespace Umbraco.Core.Services //set the invalid properties (if there are any) publishStatus.InvalidProperties = ((ContentBase)content).LastInvalidProperties; } + + var uow = UowProvider.GetUnitOfWork(); + //if we're still successful, then publish using the strategy if (publishStatus.StatusType == PublishStatusType.Success) - { - var internalStrategy = (PublishingStrategy)_publishingStrategy; + { //Publish and then update the database with new status - var publishResult = internalStrategy.PublishInternal(content, userId); + var publishResult = _publishingStrategy.Publish(uow, content, userId); //set the status type to the publish result publishStatus.StatusType = publishResult.Result.StatusType; } //we are successfully published if our publishStatus is still Successful bool published = publishStatus.StatusType == PublishStatusType.Success; - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { if (published == false) @@ -2136,20 +2145,20 @@ namespace Umbraco.Core.Services if (raiseEvents) Saved.RaiseEvent(new SaveEventArgs(content, false, evtMsgs), this, uow.EventManager); - } - //Save xml to db and call following method to fire event through PublishingStrategy to update cache - if (published) - { - _publishingStrategy.PublishingFinalized(content); - } + //Save xml to db and call following method to fire event through PublishingStrategy to update cache + if (published) + { + _publishingStrategy.PublishingFinalized(uow, content); + } - //We need to check if children and their publish state to ensure that we 'republish' content that was previously published - if (published && previouslyPublished == false && HasChildren(content.Id)) - { - var descendants = GetPublishedDescendants(content); + //We need to check if children and their publish state to ensure that we 'republish' content that was previously published + if (published && previouslyPublished == false && HasChildren(content.Id)) + { + var descendants = GetPublishedDescendants(content); - _publishingStrategy.PublishingFinalized(descendants, false); + _publishingStrategy.PublishingFinalized(uow, descendants, false); + } } Audit(AuditType.Publish, "Save and Publish performed by user", userId, content.Id); diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 46b637b24d..b4f8214f99 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using Umbraco.Core.Logging; using System.IO; using System.Linq; @@ -149,13 +150,26 @@ namespace Umbraco.Core.Services if (redirectUrlService != null) _redirectUrlService = new Lazy(() => redirectUrlService); } + [Obsolete("The IPublishingStrategy parameter is no longer required and not used")] + [EditorBrowsable(EditorBrowsableState.Never)] + public ServiceContext( + RepositoryFactory repositoryFactory, + IScopeUnitOfWorkProvider dbUnitOfWorkProvider, + IScopeUnitOfWorkProvider fileUnitOfWorkProvider, + IPublishingStrategy publishingStrategy, + CacheHelper cache, + ILogger logger, + IEventMessagesFactory eventMessagesFactory) + : this(repositoryFactory, dbUnitOfWorkProvider, fileUnitOfWorkProvider, cache, logger, eventMessagesFactory) + { + } + /// /// Creates a service context with a RepositoryFactory which is used to construct Services /// /// /// /// - /// /// /// /// @@ -163,7 +177,6 @@ namespace Umbraco.Core.Services RepositoryFactory repositoryFactory, IScopeUnitOfWorkProvider dbUnitOfWorkProvider, IScopeUnitOfWorkProvider fileUnitOfWorkProvider, - BasePublishingStrategy publishingStrategy, CacheHelper cache, ILogger logger, IEventMessagesFactory eventMessagesFactory) @@ -171,14 +184,13 @@ namespace Umbraco.Core.Services if (repositoryFactory == null) throw new ArgumentNullException("repositoryFactory"); if (dbUnitOfWorkProvider == null) throw new ArgumentNullException("dbUnitOfWorkProvider"); if (fileUnitOfWorkProvider == null) throw new ArgumentNullException("fileUnitOfWorkProvider"); - if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy"); if (cache == null) throw new ArgumentNullException("cache"); if (logger == null) throw new ArgumentNullException("logger"); if (eventMessagesFactory == null) throw new ArgumentNullException("eventMessagesFactory"); EventMessagesFactory = eventMessagesFactory; - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, cache, + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, cache, repositoryFactory, logger, eventMessagesFactory); } @@ -189,7 +201,6 @@ namespace Umbraco.Core.Services private void BuildServiceCache( IScopeUnitOfWorkProvider dbUnitOfWorkProvider, IScopeUnitOfWorkProvider fileUnitOfWorkProvider, - BasePublishingStrategy publishingStrategy, CacheHelper cache, RepositoryFactory repositoryFactory, ILogger logger, @@ -268,7 +279,7 @@ namespace Umbraco.Core.Services _memberService = new Lazy(() => new MemberService(provider, repositoryFactory, logger, eventMessagesFactory, _memberGroupService.Value, _dataTypeService.Value)); if (_contentService == null) - _contentService = new Lazy(() => new ContentService(provider, repositoryFactory, logger, eventMessagesFactory, publishingStrategy, _dataTypeService.Value, _userService.Value)); + _contentService = new Lazy(() => new ContentService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value)); if (_mediaService == null) _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory, logger, eventMessagesFactory, _dataTypeService.Value, _userService.Value)); diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index ba79fcbc69..29d9e972ab 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Persistence //assign the db context dbContext, //assign the service context - new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), new PublishingStrategy(evtMsgs, _logger), cacheHelper, _logger, evtMsgs), + new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), cacheHelper, _logger, evtMsgs), cacheHelper, new ProfilingLogger(_logger, Mock.Of())) { diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 63ba1afb81..9d8bb4104d 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -15,6 +15,8 @@ using Umbraco.Tests.TestHelpers.Entities; using umbraco.editorControls.tinyMCE3; using umbraco.interfaces; using System.Linq; +using Moq; +using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Tests.Publishing { @@ -27,6 +29,10 @@ namespace Umbraco.Tests.Publishing { base.Initialize(); + var unitOfWorkMock = new Mock(); + unitOfWorkMock.Setup(x => x.Messages).Returns(() => new EventMessages()); + unitOfWorkMock.Setup(x => x.EventManager).Returns(() => new NoScopedEventManager()); + _unitOfWork = unitOfWorkMock.Object; //LegacyUmbracoSettings.SettingsFilePath = IOHelper.MapPath(SystemDirectories.Config + Path.DirectorySeparatorChar, false); } @@ -36,10 +42,13 @@ namespace Umbraco.Tests.Publishing base.TearDown(); //ensure event handler is gone - PublishingStrategy.Publishing -= PublishingStrategyPublishing; + PublishingStrategy.Publishing -= PublishingStrategyPublishing; + + } private IContent _homePage; + private IScopeUnitOfWork _unitOfWork; /// /// in these tests we have a heirarchy of @@ -64,11 +73,11 @@ namespace Umbraco.Tests.Publishing ServiceContext.ContentTypeService.GetContentType("umbTextpage"), "Sub Sub Sub", mandatorContent.Id); ServiceContext.ContentService.Save(subContent, 0); - var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger); + IPublishingStrategy2 strategy = new PublishingStrategy(Logger); //publish root and nodes at it's children level var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] { _homePage }); - var result = strategy.PublishWithChildrenInternal(listToPublish, 0, true); + var result = strategy.PublishWithChildren(_unitOfWork, listToPublish, 0, true); Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success)); Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Result.ContentItem.Id) @@ -91,14 +100,14 @@ namespace Umbraco.Tests.Publishing { CreateTestData(); - var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger); + IPublishingStrategy2 strategy = new PublishingStrategy( Logger); PublishingStrategy.Publishing +=PublishingStrategyPublishing; //publish root and nodes at it's children level var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] {_homePage}); - var result = strategy.PublishWithChildrenInternal(listToPublish, 0); + var result = strategy.PublishWithChildren(_unitOfWork, listToPublish, 0, true); Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success)); Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Result.ContentItem.Id) @@ -118,22 +127,22 @@ namespace Umbraco.Tests.Publishing { CreateTestData(); - var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger); + IPublishingStrategy2 strategy = new PublishingStrategy(Logger); //publish root and nodes at it's children level - var result1 = strategy.Publish(_homePage, 0); + var result1 = strategy.Publish(_unitOfWork, _homePage, 0); Assert.IsTrue(result1); Assert.IsTrue(_homePage.Published); foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id)) { - var r = strategy.Publish(c, 0); + var r = strategy.Publish(_unitOfWork, c, 0); Assert.IsTrue(r); Assert.IsTrue(c.Published); } //ok, all are published except the deepest descendant, we will pass in a flag to not include it to //be published - var result = strategy.PublishWithChildrenInternal( + var result = strategy.PublishWithChildren(_unitOfWork, ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage}), 0, false); //all of them will be SuccessAlreadyPublished unless the unpublished one gets included, in that case //we'll have a 'Success' result which we don't want. @@ -145,17 +154,17 @@ namespace Umbraco.Tests.Publishing { CreateTestData(); - var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger); + IPublishingStrategy2 strategy = new PublishingStrategy(Logger); //publish root and nodes at it's children level - var result1 = strategy.Publish(_homePage, 0); + var result1 = strategy.Publish(_unitOfWork, _homePage, 0); Assert.IsTrue(result1); Assert.IsTrue(_homePage.Published); //NOTE (MCH) This isn't persisted, so not really a good test as it will look like the result should be something else. foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id)) { - var r = strategy.Publish(c, 0); + var r = strategy.Publish(_unitOfWork, c, 0); Assert.IsTrue(r); Assert.IsTrue(c.Published); } @@ -168,7 +177,7 @@ namespace Umbraco.Tests.Publishing //with 'SuccessAlreadyPublished' var descendants = ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage}); - var result = strategy.PublishWithChildrenInternal(descendants, 0, true); + var result = strategy.PublishWithChildren(_unitOfWork, descendants, 0, true); Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.Success)); Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished)); diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 65137968ec..0b531e3b88 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -103,7 +103,7 @@ namespace Umbraco.Tests.TestHelpers //assign the db context new DatabaseContext(scopeProvider, Logger, SqlSyntax, GetDbProviderName()), //assign the service context - new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), + new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), CacheHelper, Logger, evtMsgs), CacheHelper, ProfilingLogger) { diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 3960aa9cee..b5545e8f00 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -169,7 +169,7 @@ namespace Umbraco.Tests.TestHelpers //assign the db context new DatabaseContext(scopeProvider, Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), //assign the service context - new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), + new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), CacheHelper, Logger, evtMsgs), CacheHelper, ProfilingLogger) { diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 9d6bd6e168..b0ac28dfe5 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -97,7 +97,6 @@ namespace Umbraco.Web new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(scopeProvider), - new PublishingStrategy(evtMsgs, ProfilingLogger.Logger), ApplicationCache, ProfilingLogger.Logger, evtMsgs);