diff --git a/src/Umbraco.Core/Models/ContentXmlEntity.cs b/src/Umbraco.Core/Models/ContentXmlEntity.cs index 6ba9427073..f137b3b418 100644 --- a/src/Umbraco.Core/Models/ContentXmlEntity.cs +++ b/src/Umbraco.Core/Models/ContentXmlEntity.cs @@ -22,6 +22,12 @@ namespace Umbraco.Core.Models Content = content; } + public ContentXmlEntity(TContent content) + { + if (content == null) throw new ArgumentNullException("content"); + Content = content; + } + public XElement Xml { get { return _xml(Content); } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index fc88ee5780..dc5077293f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -575,6 +575,15 @@ namespace Umbraco.Core.Persistence.Repositories _contentXmlRepository.AddOrUpdate(new ContentXmlEntity(contentExists, content, xml)); } + /// + /// Used to remove the content xml for a content item + /// + /// + public void DeleteContentXml(IContent content) + { + _contentXmlRepository.Delete(new ContentXmlEntity(content)); + } + /// /// Adds/updates preview xml /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs index 93cf67ba49..da076506a6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs @@ -55,12 +55,14 @@ namespace Umbraco.Core.Persistence.Repositories { get { throw new NotImplementedException(); } } + + #endregion protected override void PersistDeletedItem(ContentXmlEntity entity) { - throw new NotImplementedException(); + //Remove 'published' xml from the cmsContentXml table for the unpublished content + Database.Delete("WHERE nodeId = @Id", new { Id = entity.Id }); } - #endregion protected override void PersistNewItem(ContentXmlEntity entity) { diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 9c39f6bed3..e30ca6e6f6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -46,6 +46,12 @@ namespace Umbraco.Core.Persistence.Repositories /// void AddOrUpdateContentXml(IContent content, Func xml); + /// + /// Used to remove the content xml for a content item + /// + /// + void DeleteContentXml(IContent content); + /// /// Used to add/update preview xml for the content item /// diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 77837595aa..49e59550ee 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -211,6 +211,8 @@ namespace Umbraco.Core.Services content.CreatorId = userId; content.WriterId = userId; repository.AddOrUpdate(content); + //Generate a new preview + repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); uow.Commit(); } @@ -261,6 +263,8 @@ namespace Umbraco.Core.Services content.CreatorId = userId; content.WriterId = userId; repository.AddOrUpdate(content); + //Generate a new preview + repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); uow.Commit(); } @@ -785,21 +789,23 @@ namespace Umbraco.Core.Services /// Optional boolean indicating whether or not to raise events. public void Save(IEnumerable contents, int userId = 0, bool raiseEvents = true) { + var asArray = contents.ToArray(); + if (raiseEvents) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(contents), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) return; } using (new WriteLock(Locker)) { - var containsNew = contents.Any(x => x.HasIdentity == false); + var containsNew = asArray.Any(x => x.HasIdentity == false); var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentRepository(uow)) { if (containsNew) { - foreach (var content in contents) + foreach (var content in asArray) { content.WriterId = userId; @@ -808,22 +814,26 @@ namespace Umbraco.Core.Services content.ChangePublishedState(PublishedState.Saved); repository.AddOrUpdate(content); - uow.Commit(); + //add or update preview + repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); } } else { - foreach (var content in contents) + foreach (var content in asArray) { content.WriterId = userId; repository.AddOrUpdate(content); - } - uow.Commit(); + //add or update preview + repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); + } } + + uow.Commit(); } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(contents, false), this); + Saved.RaiseEvent(new SaveEventArgs(asArray, false), this); Audit.Add(AuditTypes.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, -1); } @@ -1140,6 +1150,8 @@ namespace Umbraco.Core.Services copy.WriterId = userId; repository.AddOrUpdate(copy); + //add or update a preview + repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, c)); uow.Commit(); //Special case for the Upload DataType @@ -1176,10 +1188,13 @@ namespace Umbraco.Core.Services if (isUpdated) { repository.AddOrUpdate(copy); + //add or update a preview + repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, c)); uow.Commit(); } } + //TODO: Move this to the repository layer in a single transaction! //Special case for the Tags DataType var tagsDataTypeId = new Guid(Constants.PropertyEditors.Tags); if (content.Properties.Any(x => x.PropertyType.DataTypeId == tagsDataTypeId)) @@ -1193,6 +1208,7 @@ namespace Umbraco.Core.Services } //NOTE This 'Relation' part should eventually be delegated to a RelationService + //TODO: This should be party of a single commit if (relateToOriginal) { IRelationType relationType = null; @@ -1217,14 +1233,15 @@ namespace Umbraco.Core.Services var children = GetChildren(content.Id); foreach (var child in children) { + //TODO: This shouldn't recurse back to this method, it should be done in a private method + // that doesn't have a nested lock and so we can perform the entire operation in one commit. Copy(child, copy.Id, relateToOriginal, userId); } Copied.RaiseEvent(new CopyEventArgs(content, copy, false, parentId), this); Audit.Add(AuditTypes.Copy, "Copy Content performed by user", content.WriterId, content.Id); - - + //TODO: Don't think we need this here because cache should be cleared by the event listeners // and the correct ICacheRefreshers!? RuntimeCacheProvider.Current.Clear(); @@ -1284,6 +1301,8 @@ namespace Umbraco.Core.Services content.ChangePublishedState(PublishedState.Unpublished); repository.AddOrUpdate(content); + //add or update a preview + repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); uow.Commit(); } @@ -1348,8 +1367,7 @@ namespace Umbraco.Core.Services shouldBeSaved.Add(content); repository.AddOrUpdate(content); - - //Generate a new preview + //add or update a preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, c)); } @@ -1475,6 +1493,8 @@ namespace Umbraco.Core.Services //TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine Save(content, false, userId); + //TODO: This shouldn't be here! This needs to be part of the repository logic but in order to fix this we need to + // change how this method calls "Save" as it needs to save using an internal method using (var uow = _uowProvider.GetUnitOfWork()) { var xml = _entitySerializer.Serialize(this, _dataTypeService, content); @@ -1655,21 +1675,15 @@ namespace Umbraco.Core.Services { item.Result.ContentItem.WriterId = userId; repository.AddOrUpdate(item.Result.ContentItem); + //add or update a preview + repository.AddOrUpdatePreviewXml(item.Result.ContentItem, c => _entitySerializer.Serialize(this, _dataTypeService, c)); + //add or update the published xml + repository.AddOrUpdateContentXml(item.Result.ContentItem, c => _entitySerializer.Serialize(this, _dataTypeService, c)); updated.Add(item.Result.ContentItem); } uow.Commit(); - foreach (var c in updated) - { - var xml = _entitySerializer.Serialize(this, _dataTypeService, c); - var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) }; - var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = c.Id }) != - null; - var r = exists - ? uow.Database.Update(poco) - : Convert.ToInt32(uow.Database.Insert(poco)); - } } //Save xml to db and call following method to fire event: _publishingStrategy.PublishingFinalized(updated, false); @@ -1700,9 +1714,7 @@ namespace Umbraco.Core.Services { content.WriterId = userId; repository.AddOrUpdate(content); - - //Remove 'published' xml from the cmsContentXml table for the unpublished content - uow.Database.Delete("WHERE nodeId = @Id", new { Id = content.Id }); + repository.DeleteContentXml(content); uow.Commit(); } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index fe78f719ec..86d34374e3 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -284,6 +284,13 @@ namespace Umbraco.Tests.Services var content = contentService.GetById(NodeDto.NodeIdSeed + 1); bool published = contentService.Publish(content, 0); + var provider = new PetaPocoUnitOfWorkProvider(); + var uow = provider.GetUnitOfWork(); + using (RepositoryResolver.Current.ResolveByType(uow)) + { + Assert.IsTrue(uow.Database.Exists(content.Id)); + } + // Act bool unpublished = contentService.UnPublish(content, 0); @@ -291,6 +298,12 @@ namespace Umbraco.Tests.Services Assert.That(published, Is.True); Assert.That(unpublished, Is.True); Assert.That(content.Published, Is.False); + + uow = provider.GetUnitOfWork(); + using (RepositoryResolver.Current.ResolveByType(uow)) + { + Assert.IsFalse(uow.Database.Exists(content.Id)); + } } ///