From aa25990681768d3f8b201eccf9c42fb815da7505 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 1 Dec 2017 19:29:54 +0100 Subject: [PATCH] Integer version ID, cleanup --- .../Events/DeleteRevisionsEventArgs.cs | 17 +- src/Umbraco.Core/Models/Content.cs | 31 +- src/Umbraco.Core/Models/ContentBase.cs | 13 +- .../Models/ContentPreviewEntity.cs | 23 - src/Umbraco.Core/Models/ContentXmlEntity.cs | 65 --- src/Umbraco.Core/Models/IContent.cs | 29 +- src/Umbraco.Core/Models/IContentBase.cs | 13 +- src/Umbraco.Core/Models/Property.cs | 6 + .../PublishedContent/IPublishedContent.cs | 1 - .../PublishedContentWrapped.cs | 2 - .../Models/Rdbms/ContentVersionDto.cs | 5 - .../Factories/ContentBaseFactory.cs | 16 +- .../Persistence/Mappers/ContentMapper.cs | 3 +- .../Persistence/Mappers/MediaMapper.cs | 6 +- .../Persistence/Mappers/MemberMapper.cs | 1 - .../TargetVersionEight/VariantsMigration.cs | 3 + .../Repositories/ContentRepository.cs | 43 +- .../Interfaces/IRepositoryVersionable.cs | 6 +- .../Repositories/MediaRepository.cs | 21 +- .../Repositories/MemberRepository.cs | 21 +- .../Repositories/VersionableRepositoryBase.cs | 20 +- src/Umbraco.Core/Services/ContentService.cs | 530 ++++++++---------- .../Services/EntityXmlSerializer.cs | 1 - src/Umbraco.Core/Services/IContentService.cs | 24 +- src/Umbraco.Core/Services/IMediaService.cs | 4 +- src/Umbraco.Core/Services/MediaService.cs | 4 +- .../Services/PublishResultType.cs | 7 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 - src/Umbraco.Examine/UmbracoContentIndexer.cs | 1 - .../XmlPublishedContentInitBenchmarks.cs | 8 +- .../Integration/ContentEventsTests.cs | 10 +- src/Umbraco.Tests/Models/ContentTests.cs | 9 +- src/Umbraco.Tests/Models/MediaXmlTest.cs | 1 - src/Umbraco.Tests/Models/MemberTests.cs | 6 +- src/Umbraco.Tests/Models/VariationTests.cs | 6 +- .../Persistence/Mappers/ContentMapperTest.cs | 35 +- .../Persistence/Mappers/MediaMapperTest.cs | 31 +- .../NPocoTests/PetaPocoCachesTest.cs | 2 +- .../Repositories/ContentRepositoryTest.cs | 46 +- .../Repositories/MediaRepositoryTest.cs | 2 +- .../Published/NestedContentTests.cs | 1 - .../PublishedContent/PublishedRouterTests.cs | 2 - .../Publishing/PublishingStrategyTests.cs | 150 ----- .../Services/ContentServiceTests.cs | 156 ++++-- .../Models/PublishedContentBase.cs | 1 - .../NuCache/DataSource/Database.cs | 2 - .../NuCache/PublishedContent.cs | 1 - .../PublishedCache/NuCache/PublishedMember.cs | 1 - .../PublishedCache/PublishedMember.cs | 2 - .../XmlPublishedCache/PublishedMediaCache.cs | 2 - .../XmlPublishedCache/XmlPublishedContent.cs | 19 +- .../XmlPublishedCache/XmlStore.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 2 +- .../WebServices/BulkPublishController.cs | 5 +- .../PackageActions/publishRootDocument.cs | 3 +- .../umbraco.presentation/library.cs | 2 - src/Umbraco.Web/umbraco.presentation/page.cs | 13 +- 57 files changed, 561 insertions(+), 877 deletions(-) delete mode 100644 src/Umbraco.Core/Models/ContentPreviewEntity.cs delete mode 100644 src/Umbraco.Core/Models/ContentXmlEntity.cs diff --git a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs index a4d50d9aaf..a93f08ebb8 100644 --- a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs +++ b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs @@ -4,7 +4,7 @@ namespace Umbraco.Core.Events { public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable { - public DeleteRevisionsEventArgs(int id, bool canCancel, Guid specificVersion = default(Guid), bool deletePriorVersions = false, DateTime dateToRetain = default(DateTime)) + public DeleteRevisionsEventArgs(int id, bool canCancel, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, canCancel) { DeletePriorVersions = deletePriorVersions; @@ -12,7 +12,7 @@ namespace Umbraco.Core.Events DateToRetain = dateToRetain; } - public DeleteRevisionsEventArgs(int id, Guid specificVersion = default(Guid), bool deletePriorVersions = false, DateTime dateToRetain = default(DateTime)) + public DeleteRevisionsEventArgs(int id, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id) { DeletePriorVersions = deletePriorVersions; @@ -20,17 +20,14 @@ namespace Umbraco.Core.Events DateToRetain = dateToRetain; } - public bool DeletePriorVersions { get; private set; } - public Guid SpecificVersion { get; private set; } - public DateTime DateToRetain { get; private set; } + public bool DeletePriorVersions { get; } + public int SpecificVersion { get; } + public DateTime DateToRetain { get; } /// /// Returns true if we are deleting a specific revision /// - public bool IsDeletingSpecificRevision - { - get { return SpecificVersion != default(Guid); } - } + public bool IsDeletingSpecificRevision => SpecificVersion != default; public bool Equals(DeleteRevisionsEventArgs other) { @@ -43,7 +40,7 @@ namespace Umbraco.Core.Events { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (obj.GetType() != GetType()) return false; return Equals((DeleteRevisionsEventArgs) obj); } diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 4a680eabc1..fdfd962759 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -46,6 +46,7 @@ namespace Umbraco.Core.Models { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); _publishedState = PublishedState.Unpublished; + PublishedVersionId = 0; } /// @@ -70,6 +71,7 @@ namespace Umbraco.Core.Models { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); _publishedState = PublishedState.Unpublished; + PublishedVersionId = 0; } // ReSharper disable once ClassNeverInstantiated.Local @@ -223,40 +225,46 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public string PublishName { get; internal set; } + [IgnoreDataMember] + public int PublishedVersionId { get; internal set; } + [DataMember] public bool Blueprint { get; internal set; } /// - public virtual void PublishAllValues() + public virtual bool PublishAllValues() { if (ValidateAll().Any()) - throw new InvalidOperationException("Values are not valid."); + return false; foreach (var property in Properties) property.PublishAllValues(); _publishedState = PublishedState.Publishing; + return true; } /// - public virtual void PublishValues(int? languageId = null, string segment = null) + public virtual bool PublishValues(int? languageId = null, string segment = null) { if (Validate(languageId, segment).Any()) - throw new InvalidOperationException("Values are not valid."); + return false; foreach (var property in Properties) property.PublishValue(languageId, segment); _publishedState = PublishedState.Publishing; + return true; } /// - public virtual void PublishCultureValues(int? languageId = null) + public virtual bool PublishCultureValues(int? languageId = null) { if (ValidateCulture(languageId).Any()) - throw new InvalidOperationException("Values are not valid."); + return false; foreach (var property in Properties) property.PublishCultureValues(languageId); _publishedState = PublishedState.Publishing; + return true; } /// @@ -286,7 +294,7 @@ namespace Umbraco.Core.Models private bool IsCopyFromSelf(IContent other) { // copying from the same Id and VersionPk - return Id == other.Id && VersionPk == ((Content) other).VersionPk; + return Id == other.Id && VersionId == other.VersionId; } /// @@ -329,7 +337,7 @@ namespace Umbraco.Core.Models if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); - var published = VersionPk > PublishedVersionPk; + var published = VersionId > PublishedVersionId; // note: use property.SetValue(), don't assign pvalue.EditValue, else change tracking fails @@ -341,7 +349,7 @@ namespace Umbraco.Core.Models foreach (var pvalue in property.Values) if (pvalue.LanguageId == languageId && pvalue.Segment == segment) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + property.SetValue(null, pvalue.LanguageId, pvalue.Segment); } // copy other properties @@ -362,7 +370,7 @@ namespace Umbraco.Core.Models if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); - var published = VersionPk > PublishedVersionPk; + var published = VersionId > PublishedVersionId; // note: use property.SetValue(), don't assign pvalue.EditValue, else change tracking fails @@ -438,8 +446,7 @@ namespace Umbraco.Core.Models { var clone = (Content)DeepClone(); clone.Key = Guid.Empty; - clone.Version = Guid.NewGuid(); - clone.VersionPk = clone.PublishedVersionPk = 0; + clone.VersionId = clone.PublishedVersionId = 0; clone.ResetIdentity(); foreach (var property in clone.Properties) diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index f7d643e9a3..1d728ede8b 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -70,7 +70,7 @@ namespace Umbraco.Core.Models // initially, all new instances have Id = 0; // no identity - Version = Guid.NewGuid(); // a new unique version id + VersionId = 0; // no versions _name = name; _contentTypeId = contentType.Id; @@ -201,17 +201,8 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _writerId, Ps.Value.WriterSelector); } - /// - /// Gets or sets the identifier of the version. - /// - [DataMember] - public Guid Version { get; internal set; } - [IgnoreDataMember] - internal int VersionPk { get; set; } - - [IgnoreDataMember] - internal int PublishedVersionPk { get; set; } + public int VersionId { get; internal set; } /// /// Integer Id of the default ContentType diff --git a/src/Umbraco.Core/Models/ContentPreviewEntity.cs b/src/Umbraco.Core/Models/ContentPreviewEntity.cs deleted file mode 100644 index 0ae4f4d3f6..0000000000 --- a/src/Umbraco.Core/Models/ContentPreviewEntity.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Xml.Linq; - -namespace Umbraco.Core.Models -{ - /// - /// Used content repository in order to add an entity to the persisted collection to be saved - /// in a single transaction during saving an entity - /// - internal class ContentPreviewEntity : ContentXmlEntity - where TContent : IContentBase - { - public ContentPreviewEntity(TContent content, Func xml) - : base(content, xml) - { - } - - public Guid Version - { - get { return Content.Version; } - } - } -} diff --git a/src/Umbraco.Core/Models/ContentXmlEntity.cs b/src/Umbraco.Core/Models/ContentXmlEntity.cs deleted file mode 100644 index 3dc9e93528..0000000000 --- a/src/Umbraco.Core/Models/ContentXmlEntity.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Xml.Linq; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Models -{ - /// - /// Used in content/media/member repositories in order to add this type of entity to the persisted collection to be saved - /// in a single transaction during saving an entity - /// - internal class ContentXmlEntity : IAggregateRoot // fixme kill? - where TContent : IContentBase - { - private readonly Func _xml; - - public ContentXmlEntity(TContent content, Func xml) - { - if (content == null) throw new ArgumentNullException("content"); - _xml = xml; - Content = content; - } - - public ContentXmlEntity(TContent content) - { - if (content == null) throw new ArgumentNullException("content"); - Content = content; - } - - public XElement Xml - { - get { return _xml(Content); } - } - - public TContent Content { get; private set; } - - public int Id - { - get { return Content.Id; } - set { throw new NotSupportedException(); } - } - - public Guid Key { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public DateTime? DeletedDate { get; set; } - - /// - /// Special case, always return false, this will cause the repositories managing - /// this object to always do an 'insert' but these are special repositories that - /// do an InsertOrUpdate on insert since the data for this needs to be managed this way - /// - public bool HasIdentity - { - get { return false; } - } - - public object DeepClone() - { - var clone = (ContentXmlEntity)MemberwiseClone(); - //Automatically deep clone ref properties that are IDeepCloneable - DeepCloneHelper.DeepCloneRefProperties(this, clone); - return clone; - } - } -} diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index d19bc1425c..06735023aa 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -23,6 +23,11 @@ namespace Umbraco.Core.Models /// bool Edited { get; } + /// + /// Gets the published version identifier. + /// + int PublishedVersionId { get; } + /// /// Gets a value indicating whether the content item is a blueprint. /// @@ -100,20 +105,32 @@ namespace Umbraco.Core.Models /// /// Publishes all values. /// - /// The document must then be published via the content service. - void PublishAllValues(); + /// A value indicating whether the values could be published. + /// + /// The document must then be published via the content service. + /// Values are not published if they are not valie. + /// + bool PublishAllValues(); /// /// Publishes values. /// - /// The document must then be published via the content service. - void PublishValues(int? languageId = null, string segment = null); + /// A value indicating whether the values could be published. + /// + /// The document must then be published via the content service. + /// Values are not published if they are not valie. + /// + bool PublishValues(int? languageId = null, string segment = null); /// /// Publishes the culture/any values. /// - /// The document must then be published via the content service. - void PublishCultureValues(int? languageId = null); + /// A value indicating whether the values could be published. + /// + /// The document must then be published via the content service. + /// Values are not published if they are not valie. + /// + bool PublishCultureValues(int? languageId = null); /// /// Clears all published values. diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index ba0b86ea40..66242386a9 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models @@ -15,16 +14,16 @@ namespace Umbraco.Core.Models /// int ContentTypeId { get; } - /// - /// Gets the Guid identifier of the content's version. - /// - Guid Version { get; } - /// /// Gets the identifier of the writer. /// int WriterId { get; set; } + /// + /// Gets the version identifier. + /// + int VersionId { get; } + /// /// List of properties, which make up all the data available for this Content object /// diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 4492ff507d..f6173db0d6 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -146,6 +146,8 @@ namespace Umbraco.Core.Models private object GetPropertyValue(PropertyValue pvalue, bool published) { + if (pvalue == null) return null; + return _propertyType.IsPublishing ? (published ? pvalue.PublishedValue : pvalue.EditedValue) : pvalue.EditedValue; @@ -250,6 +252,8 @@ namespace Umbraco.Core.Models private void PublishPropertyValue(PropertyValue pvalue) { + if (pvalue == null) return; + if (!_propertyType.IsPublishing) throw new NotSupportedException("Property type does not support publishing."); var origValue = pvalue.PublishedValue; @@ -259,6 +263,8 @@ namespace Umbraco.Core.Models private void ClearPublishedPropertyValue(PropertyValue pvalue) { + if (pvalue == null) return; + if (!_propertyType.IsPublishing) throw new NotSupportedException("Property type does not support publishing."); var origValue = pvalue.PublishedValue; diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index 6b65b7b421..f114d94116 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -35,7 +35,6 @@ namespace Umbraco.Core.Models.PublishedContent string Path { get; } DateTime CreateDate { get; } DateTime UpdateDate { get; } - Guid Version { get; } int Level { get; } string Url { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index da3add40e5..a30726012e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -76,8 +76,6 @@ namespace Umbraco.Core.Models.PublishedContent public virtual DateTime UpdateDate => _content.UpdateDate; - public virtual Guid Version => _content.Version; - public virtual int Level => _content.Level; public virtual string Url => _content.Url; diff --git a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs index f7de760472..eee9fea977 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs @@ -18,13 +18,8 @@ namespace Umbraco.Core.Models.Rdbms [Column("nodeId")] [ForeignKey(typeof(ContentDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeIdVersionId", ForColumns = "nodeId, versionId")] public int NodeId { get; set; } - [Column("versionId")] - [Index(IndexTypes.UniqueNonClustered)] - public Guid VersionId { get; set; } - [Column("versionDate")] [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime VersionDate { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs index b49c8fe50e..5859f61714 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentBaseFactory.cs @@ -25,8 +25,7 @@ namespace Umbraco.Core.Persistence.Factories content.Id = dto.NodeId; content.Key = nodeDto.UniqueId; - content.Version = contentVersionDto.VersionId; - content.VersionPk = contentVersionDto.Id; + content.VersionId = contentVersionDto.Id; content.Name = contentVersionDto.Text; content.NodeName = contentVersionDto.Text; @@ -51,7 +50,7 @@ namespace Umbraco.Core.Persistence.Factories //if (dto.Published) if (publishedVersionDto != null) { - content.PublishedVersionPk = publishedVersionDto.Id; + content.PublishedVersionId = publishedVersionDto.Id; content.PublishDate = publishedVersionDto.ContentVersionDto.VersionDate; content.PublishName = publishedVersionDto.ContentVersionDto.Text; content.PublisherId = publishedVersionDto.ContentVersionDto.UserId; @@ -85,8 +84,7 @@ namespace Umbraco.Core.Persistence.Factories content.Id = dto.NodeId; content.Key = nodeDto.UniqueId; - content.Version = contentVersionDto.VersionId; - content.VersionPk = contentVersionDto.Id; + content.VersionId = contentVersionDto.Id; // fixme missing names? @@ -127,8 +125,7 @@ namespace Umbraco.Core.Persistence.Factories content.Id = dto.NodeId; content.Key = nodeDto.UniqueId; - content.Version = contentVersionDto.VersionId; - content.VersionPk = contentVersionDto.Id; + content.VersionId = contentVersionDto.Id; // fixme missing names? @@ -248,9 +245,8 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new ContentVersionDto { - Id = entity.VersionPk, + Id = entity.VersionId, NodeId = entity.Id, - VersionId = entity.Version, VersionDate = entity.UpdateDate, UserId = entity.WriterId, Current = true, // always building the current one @@ -268,7 +264,7 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new DocumentVersionDto { - Id = entity.VersionPk, + Id = entity.VersionId, TemplateId = entity.Template?.Id, Published = false, // always building the current, unpublished one diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs index ca273fff68..1d5b07dcd9 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs @@ -23,6 +23,8 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Id, dto => dto.NodeId); CacheMap(src => src.Key, dto => dto.UniqueId); + CacheMap(src => src.VersionId, dto => dto.Id); + CacheMap(src => src.NodeName, dto => dto.Text); CacheMap(src => src.Name, dto => dto.Text); @@ -37,7 +39,6 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); CacheMap(src => src.UpdateDate, dto => dto.VersionDate); - CacheMap(src => src.Version, dto => dto.VersionId); CacheMap(src => src.ExpireDate, dto => dto.ExpiresDate); CacheMap(src => src.ReleaseDate, dto => dto.ReleaseDate); CacheMap(src => src.Published, dto => dto.Published); diff --git a/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs index f3945a688f..111bb980ed 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MediaMapper.cs @@ -21,6 +21,10 @@ namespace Umbraco.Core.Persistence.Mappers if (PropertyInfoCache.IsEmpty == false) return; CacheMap(src => src.Id, dto => dto.NodeId); + CacheMap(src => src.Key, dto => dto.UniqueId); + + CacheMap(src => src.VersionId, dto => dto.Id); + CacheMap(src => src.CreateDate, dto => dto.CreateDate); CacheMap(src => src.Level, dto => dto.Level); CacheMap(src => src.ParentId, dto => dto.ParentId); @@ -28,11 +32,9 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.SortOrder, dto => dto.SortOrder); CacheMap(src => src.Name, dto => dto.Text); CacheMap(src => src.Trashed, dto => dto.Trashed); - CacheMap(src => src.Key, dto => dto.UniqueId); CacheMap(src => src.CreatorId, dto => dto.UserId); CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); CacheMap(src => src.UpdateDate, dto => dto.VersionDate); - CacheMap(src => src.Version, dto => dto.VersionId); } } } diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs index 3d87e99e47..7b5b7d064b 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberMapper.cs @@ -32,7 +32,6 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.ContentTypeId, dto => dto.ContentTypeId); CacheMap(src => src.ContentTypeAlias, dto => dto.Alias); CacheMap(src => src.UpdateDate, dto => dto.VersionDate); - CacheMap(src => src.Version, dto => dto.VersionId); CacheMap(src => src.Email, dto => dto.Email); CacheMap(src => src.Username, dto => dto.LoginName); diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs index 1ff5cc8749..b48ab249dd 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs @@ -282,6 +282,9 @@ WHERE v1.propertyTypeId=v2.propertyTypeId AND v1.languageId=v2.languageId AND v1 return string.Empty; }); + // drop more columns + Delete.Column("versionId").FromTable(PreTables.ContentVersion); + // rename tables Rename.Table(PreTables.ContentVersion).To(Constants.DatabaseSchema.Tables.ContentVersion); Rename.Table(PreTables.Document).To(Constants.DatabaseSchema.Tables.Document); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 1cd799836c..5d966a8009 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -202,24 +202,24 @@ namespace Umbraco.Core.Persistence.Repositories return MapDtosToContent(Database.Fetch(sql), true); } - public override IContent GetVersion(Guid versionId) + public override IContent GetVersion(int versionId) { var sql = GetBaseQuery(QueryType.Single, false) - .Where(x => x.VersionId == versionId); + .Where(x => x.Id == versionId); var dto = Database.Fetch(sql).FirstOrDefault(); return dto == null ? null : MapDtoToContent(dto); } - protected override void PerformDeleteVersion(int id, Guid versionId) + protected override void PerformDeleteVersion(int id, int versionId) { // raise event first else potential FK issues OnUowRemovingVersion(new UnitOfWorkVersionEventArgs(UnitOfWork, id, versionId)); // fixme - syntax + ... - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE ContentId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE versionId = @versionId", new { versionId }); + Database.Delete("WHERE id = @versionId", new { versionId }); + Database.Delete("WHERE id = @versionId", new { versionId }); } #endregion @@ -288,11 +288,11 @@ namespace Umbraco.Core.Persistence.Repositories contentVersionDto.NodeId = nodeDto.NodeId; contentVersionDto.Current = !publishing; Database.Insert(contentVersionDto); - content.VersionPk = contentVersionDto.Id; + content.VersionId = contentVersionDto.Id; // persist the document version dto var documentVersionDto = dto.DocumentVersionDto; - documentVersionDto.Id = content.VersionPk; + documentVersionDto.Id = content.VersionId; if (publishing) documentVersionDto.Published = true; Database.Insert(documentVersionDto); @@ -300,20 +300,19 @@ namespace Umbraco.Core.Persistence.Repositories // and again in case we're publishing immediately if (publishing) { - content.PublishedVersionPk = content.VersionPk; - content.Version = contentVersionDto.VersionId = Guid.NewGuid(); + content.PublishedVersionId = content.VersionId; contentVersionDto.Id = 0; contentVersionDto.Current = true; Database.Insert(contentVersionDto); - content.VersionPk = contentVersionDto.Id; + content.VersionId = contentVersionDto.Id; - documentVersionDto.Id = content.VersionPk; + documentVersionDto.Id = content.VersionId; documentVersionDto.Published = false; Database.Insert(documentVersionDto); } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionPk, content.PublishedVersionPk, entity.Properties, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, out var edited); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -377,7 +376,7 @@ namespace Umbraco.Core.Persistence.Repositories // whatever we do, we must check that we are saving the current version // fixme maybe we can just fetch Current (bool) - var version = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.Id == content.VersionPk)).FirstOrDefault(); + var version = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.Id == content.VersionId)).FirstOrDefault(); if (version == null || !version.Current ) throw new InvalidOperationException("Cannot save a non-current version."); @@ -386,10 +385,10 @@ namespace Umbraco.Core.Persistence.Repositories var publishing = content.PublishedState == PublishedState.Publishing; // check if we need to create a new version - if (publishing && content.PublishedVersionPk > 0) + if (publishing && content.PublishedVersionId > 0) { // published version is not published anymore - Database.Execute(Sql().Update(u => u.Set(x => x.Published, false)).Where(x => x.Id == content.PublishedVersionPk)); + Database.Execute(Sql().Update(u => u.Set(x => x.Published, false)).Where(x => x.Id == content.PublishedVersionId)); } // ensure unique name on the same level @@ -433,24 +432,23 @@ namespace Umbraco.Core.Persistence.Repositories // and, if publishing, insert new content & document version dtos if (publishing) { - content.PublishedVersionPk = content.VersionPk; + content.PublishedVersionId = content.VersionId; contentVersionDto.Id = 0; // want a new id contentVersionDto.Current = true; // current version - content.Version = contentVersionDto.VersionId = Guid.NewGuid(); // for that new guid Database.Insert(contentVersionDto); - content.VersionPk = documentVersionDto.Id = contentVersionDto.Id; // get the new id + content.VersionId = documentVersionDto.Id = contentVersionDto.Id; // get the new id documentVersionDto.Published = false; // non-published version Database.Insert(documentVersionDto); } // replace the property data - var pdataToDelete = publishing ? new[] { content.VersionPk, content.PublishedVersionPk } : new[] { content.VersionPk }; + var pdataToDelete = publishing ? new[] { content.VersionId, content.PublishedVersionId } : new[] { content.VersionId }; var deletePropertyDataSql = SqlContext.Sql().Delete().WhereIn(x => x.VersionId, pdataToDelete); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionPk, publishing ? content.PublishedVersionPk : 0, entity.Properties, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, out var edited); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -762,13 +760,12 @@ namespace Umbraco.Core.Persistence.Repositories for (var i = 0; i < dtos.Count; i++) { var dto = dtos[i]; - var versionGuid = dto.DocumentVersionDto.ContentVersionDto.VersionId; if (withCache) { // if the cache contains the (proper version of the) item, use it var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - if (cached != null && cached.Version == versionGuid) + if (cached != null && cached.VersionId == dto.DocumentVersionDto.ContentVersionDto.Id) { content[i] = (Content) cached; continue; diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs index 29901dbe19..f8ba8ce4fe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs @@ -39,19 +39,19 @@ namespace Umbraco.Core.Persistence.Repositories /// /// /// The maximum number of rows to return - IEnumerable GetVersionIds(int id, int topRows); + IEnumerable GetVersionIds(int id, int topRows); /// /// Gets a specific version of an entity. /// /// The identifier of the version. - TEntity GetVersion(Guid versionId); + TEntity GetVersion(int versionId); /// /// Deletes a specific version of an entity. /// /// The identifier of the version to delete. - void DeleteVersion(Guid versionId); + void DeleteVersion(int versionId); /// /// Deletes all versions of an entity, older than a date. diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 0ec65e9028..728d069095 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -167,10 +167,10 @@ namespace Umbraco.Core.Persistence.Repositories return MapDtosToContent(Database.Fetch(sql), true); } - public override IMedia GetVersion(Guid versionId) + public override IMedia GetVersion(int versionId) { var sql = GetBaseQuery(QueryType.Single) - .Where(x => x.VersionId == versionId); + .Where(x => x.Id == versionId); var dto = Database.Fetch(sql).FirstOrDefault(); return dto == null ? null : MapDtoToContent(dto); @@ -215,13 +215,13 @@ namespace Umbraco.Core.Persistence.Repositories return nodeId ?? -1; } - protected override void PerformDeleteVersion(int id, Guid versionId) + protected override void PerformDeleteVersion(int id, int versionId) { // raise event first else potential FK issues OnUowRemovingVersion(new UnitOfWorkVersionEventArgs(UnitOfWork, id, versionId)); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE versionId = @versionId", new { versionId }); + Database.Delete("WHERE versionId = @versionId", new { versionId }); } #endregion @@ -292,10 +292,10 @@ namespace Umbraco.Core.Persistence.Repositories contentVersionDto.NodeId = nodeDto.NodeId; contentVersionDto.Current = true; Database.Insert(contentVersionDto); - media.VersionPk = contentVersionDto.Id; + media.VersionId = contentVersionDto.Id; // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionPk, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -348,9 +348,9 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(contentVersionDto); // replace the property data - var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionPk); + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionPk, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -498,13 +498,12 @@ namespace Umbraco.Core.Persistence.Repositories for (var i = 0; i < dtos.Count; i++) { var dto = dtos[i]; - var versionGuid = dto.ContentVersionDto.VersionId; if (withCache) { // if the cache contains the (proper version of the) item, use it var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - if (cached != null && cached.Version == versionGuid) + if (cached != null && cached.VersionId == dto.ContentVersionDto.Id) { content[i] = (Models.Media) cached; // fixme should we just cache Media not IMedia? continue; diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 246080dd16..877113ea5b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -205,22 +205,22 @@ namespace Umbraco.Core.Persistence.Repositories return MapDtosToContent(Database.Fetch(sql), true); } - public override IMember GetVersion(Guid versionId) + public override IMember GetVersion(int versionId) { var sql = GetBaseQuery(QueryType.Single) - .Where(x => x.VersionId == versionId); + .Where(x => x.Id == versionId); var dto = Database.Fetch(sql).FirstOrDefault(); return dto == null ? null : MapDtoToContent(dto); } - protected override void PerformDeleteVersion(int id, Guid versionId) + protected override void PerformDeleteVersion(int id, int versionId) { // raise event first else potential FK issues OnUowRemovingVersion(new UnitOfWorkVersionEventArgs(UnitOfWork, id, versionId)); - Database.Delete("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); - Database.Delete("WHERE ContentId = @Id AND VersionId = @VersionId", new { Id = id, VersionId = versionId }); + Database.Delete("WHERE versionId = @VersionId", new { versionId }); + Database.Delete("WHERE versionId = @VersionId", new { versionId }); } #endregion @@ -289,7 +289,7 @@ namespace Umbraco.Core.Persistence.Repositories contentVersionDto.NodeId = nodeDto.NodeId; contentVersionDto.Current = true; Database.Insert(contentVersionDto); - member.VersionPk = contentVersionDto.Id; + member.VersionId = contentVersionDto.Id; // persist the member dto dto.NodeId = nodeDto.NodeId; @@ -306,7 +306,7 @@ namespace Umbraco.Core.Persistence.Repositories Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionPk, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -369,9 +369,9 @@ namespace Umbraco.Core.Persistence.Repositories Database.Update(dto, changedCols); // replace the property data - var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionPk); + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionPk, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -548,13 +548,12 @@ namespace Umbraco.Core.Persistence.Repositories for (var i = 0; i < dtos.Count; i++) { var dto = dtos[i]; - var versionGuid = dto.ContentVersionDto.VersionId; if (withCache) { // if the cache contains the (proper version of the) item, use it var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); - if (cached != null && cached.Version == dto.ContentVersionDto.VersionId) + if (cached != null && cached.VersionId == dto.ContentVersionDto.Id) { content[i] = (Member) cached; // fixme should we just cache Content not IContent? continue; diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 550b26867e..a8b24bbc46 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -42,32 +42,32 @@ namespace Umbraco.Core.Persistence.Repositories #region Versions // gets a specific version - public abstract TEntity GetVersion(Guid versionId); + public abstract TEntity GetVersion(int versionId); // gets all versions, current first public abstract IEnumerable GetAllVersions(int nodeId); // gets all version ids, current first - public virtual IEnumerable GetVersionIds(int nodeId, int maxRows) + public virtual IEnumerable GetVersionIds(int nodeId, int maxRows) { var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersionIds", tsql => - tsql.Select(x => x.VersionId) + tsql.Select(x => x.Id) .From() .Where(x => x.NodeId == SqlTemplate.Arg("nodeId")) .OrderByDescending(x => x.Current) // current '1' comes before others '0' .AndByDescending(x => x.VersionDate) // most recent first ); - return Database.Fetch(SqlSyntax.SelectTop(template.Sql(nodeId), maxRows)); + return Database.Fetch(SqlSyntax.SelectTop(template.Sql(nodeId), maxRows)); } // deletes a specific version - public virtual void DeleteVersion(Guid versionId) + public virtual void DeleteVersion(int versionId) { // fixme test object node type? // get the version we want to delete var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersion", tsql => - tsql.Select().From().Where(x => x.VersionId == SqlTemplate.Arg("versionId")) + tsql.Select().From().Where(x => x.Id == SqlTemplate.Arg("versionId")) ); var versionDto = Database.Fetch(template.Sql(new { versionId })).FirstOrDefault(); @@ -93,11 +93,11 @@ namespace Umbraco.Core.Persistence.Repositories ); var versionDtos = Database.Fetch(template.Sql(new { nodeId, versionDate })); foreach (var versionDto in versionDtos) - PerformDeleteVersion(versionDto.NodeId, versionDto.VersionId); + PerformDeleteVersion(versionDto.NodeId, versionDto.Id); } // actually deletes a version - protected abstract void PerformDeleteVersion(int id, Guid versionId); + protected abstract void PerformDeleteVersion(int id, int versionId); #endregion @@ -552,7 +552,7 @@ namespace Umbraco.Core.Persistence.Repositories public class UnitOfWorkVersionEventArgs : EventArgs { - public UnitOfWorkVersionEventArgs(IScopeUnitOfWork unitOfWork, int entityId, Guid versionId) + public UnitOfWorkVersionEventArgs(IScopeUnitOfWork unitOfWork, int entityId, int versionId) { UnitOfWork = unitOfWork; EntityId = entityId; @@ -561,7 +561,7 @@ namespace Umbraco.Core.Persistence.Repositories public IScopeUnitOfWork UnitOfWork { get; } public int EntityId { get; } - public Guid VersionId { get; } + public int VersionId { get; } } public static event TypedEventHandler UowRefreshedEntity; diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 0038739e6f..1e56ded70f 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -486,7 +486,7 @@ namespace Umbraco.Core.Services /// /// Id of the version to retrieve /// An item - public IContent GetVersion(Guid versionId) + public IContent GetVersion(int versionId) { using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { @@ -517,7 +517,7 @@ namespace Umbraco.Core.Services /// /// The maximum number of rows to return /// - public IEnumerable GetVersionIds(int id, int maxRows) + public IEnumerable GetVersionIds(int id, int maxRows) { using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { @@ -1052,7 +1052,81 @@ namespace Umbraco.Core.Services /// public PublishResult SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true) { - return SaveAndPublishDo(content, userId, raiseEvents); + var evtMsgs = EventMessagesFactory.Get(); + PublishResult result; + + if (((Content) content).PublishedState != PublishedState.Publishing && content.Published) + { + // already published, and values haven't changed - i.e. not changing anything + // nothing to do + // fixme - unless we *want* to bump dates? + return new PublishResult(PublishResultType.SuccessAlready, evtMsgs, content); + } + + using (var uow = UowProvider.CreateUnitOfWork()) + { + var saveEventArgs = new SaveEventArgs(content, evtMsgs); + if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving")) + { + uow.Complete(); + return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); + } + + var isNew = content.IsNewEntity(); + var changeType = isNew ? TreeChangeTypes.RefreshBranch : TreeChangeTypes.RefreshNode; + var previouslyPublished = content.HasIdentity && content.Published; + + uow.WriteLock(Constants.Locks.ContentTree); + var repository = uow.CreateRepository(); + + // ensure that the document can be published, and publish + // handling events, business rules, etc + result = StrategyCanPublish(uow, content, userId, /*checkPath:*/ true, evtMsgs); + if (result.Success) + result = StrategyPublish(uow, content, /*canPublish:*/ true, userId, evtMsgs); + + // save - always, even if not publishing (this is SaveAndPublish) + if (content.HasIdentity == false) + content.CreatorId = userId; + content.WriterId = userId; + + repository.AddOrUpdate(content); + + if (raiseEvents) // always + { + saveEventArgs.CanCancel = false; + uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved"); + } + + if (result.Success == false) + { + uow.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); + return result; + } + + if (isNew == false && previouslyPublished == false) + changeType = TreeChangeTypes.RefreshBranch; // whole branch + + // invalidate the node/branch + uow.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); + + uow.Events.Dispatch(Published, this, new PublishEventArgs(content, false, false), "Published"); + + // if was not published and now is... descendants that were 'published' (but + // had an unpublished ancestor) are 're-published' ie not explicitely published + // but back as 'published' nevertheless + if (isNew == false && previouslyPublished == false && HasChildren(content.Id)) + { + var descendants = GetPublishedDescendantsLocked(uow, repository, content).ToArray(); + uow.Events.Dispatch(Published, this, new PublishEventArgs(descendants, false, false), "Published"); + } + + Audit(uow, AuditType.Publish, "Save and Publish performed by user", userId, content.Id); + + uow.Complete(); + } + + return result; } /// @@ -1066,7 +1140,7 @@ namespace Umbraco.Core.Services var repository = uow.CreateRepository(); var newest = GetById(content.Id); // ensure we have the newest version - if (content.Version != newest.Version) // but use the original object if it's already the newest version + if (content.VersionId != newest.VersionId) // but use the original object if it's already the newest version content = newest; if (content.Published == false) { @@ -1076,9 +1150,9 @@ namespace Umbraco.Core.Services // strategy // fixme should we still complete the uow? don't want to rollback here! - var attempt = StrategyCanUnPublish(uow, content, userId, evtMsgs); + var attempt = StrategyCanUnpublish(uow, content, userId, evtMsgs); if (attempt.Success == false) return attempt; // causes rollback - attempt = StrategyUnPublish(uow, content, true, userId, evtMsgs); + attempt = StrategyUnpublish(uow, content, true, userId, evtMsgs); if (attempt.Success == false) return attempt; // causes rollback content.WriterId = userId; @@ -1140,9 +1214,104 @@ namespace Umbraco.Core.Services } /// - public IEnumerable PublishWithChildren(IContent content, int userId, bool includeUnpublished) + public IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0) { - return PublishWithChildrenDo(content, userId, includeUnpublished); + return SaveAndPublishBranch(content, force, new[] { (languageId, segment) }, userId); + } + + /// + public IEnumerable SaveAndPublishBranch(IContent document, bool force, ValueTuple[] variations, int userId = 0) + { + var evtMsgs = EventMessagesFactory.Get(); + var results = new List(); + var publishedDocuments = new List(); + + using (var uow = UowProvider.CreateUnitOfWork()) + { + uow.WriteLock(Constants.Locks.ContentTree); + var repository = uow.CreateRepository(); + + // fixme events?! + + if (!document.HasIdentity) + throw new InvalidOperationException("Do not branch-publish a new document."); + + var publishedState = ((Content) document).PublishedState; + if (publishedState == PublishedState.Publishing) + throw new InvalidOperationException("Do not publish values when publishing branches."); + + // deal with the branch root - if it fails, abort + var result = SaveAndPublishBranchOne(document, repository, uow, variations, true, publishedDocuments, evtMsgs, userId); + results.Add(result); + if (!result.Success) return results; + + // deal with descendants + // if one fails, abort its branch + var exclude = new HashSet(); + foreach (var d in GetDescendants(document)) + { + // if parent is excluded, exclude document and ignore + // if not forcing, and not publishing, exclude document and ignore + if (exclude.Contains(d.ParentId) || !force && !d.Published) + { + exclude.Add(d.Id); + continue; + } + + // no need to check path here, + // 1. because we know the parent is path-published (we just published it) + // 2. because it would not work as nothing's been written out to the db until the uow completes + result = SaveAndPublishBranchOne(d, repository, uow, variations, false, publishedDocuments, evtMsgs, userId); + results.Add(result); + if (result.Success) continue; + + // abort branch + exclude.Add(d.Id); + } + + uow.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); + uow.Events.Dispatch(Published, this, new PublishEventArgs(publishedDocuments, false, false), "Published"); + Audit(uow, AuditType.Publish, "SaveAndPublishBranch performed by user", userId, document.Id); + + uow.Complete(); + } + + return results; + } + + private PublishResult SaveAndPublishBranchOne(IContent document, + IContentRepository repository, IScopeUnitOfWork uow, + ValueTuple[] variations, bool checkPath, + List publishedDocuments, + EventMessages evtMsgs, int userId) + { + bool IsEditingContent(IContent content, int? languageId, string segment) + => content.Properties.Any(x => x.Values.Where(y => y.LanguageId == languageId && y.Segment == segment).Any(y => y.EditedValue != y.PublishedValue)); + + // if already published, and values haven't changed - i.e. not changing anything + // nothing to do - fixme - unless we *want* to bump dates? + if (document.Published && variations.All(x => !IsEditingContent(document, x.Item1, x.Item2))) + return new PublishResult(PublishResultType.SuccessAlready, evtMsgs, document); + + // publish & check if values are valid + if (!variations.All(x => document.PublishValues(x.Item1, x.Item2))) + return new PublishResult(PublishResultType.FailedContentInvalid, evtMsgs, document); + + // check if we can publish + var result = StrategyCanPublish(uow, document, userId, checkPath, evtMsgs); + if (!result.Success) + return result; + + // publish - should be successful + var publishResult = StrategyPublish(uow, document, /*canPublish:*/ true, userId, evtMsgs); + if (!publishResult.Success) + throw new Exception("oops: failed to publish."); + + // save + document.WriterId = userId; + repository.AddOrUpdate(document); + publishedDocuments.Add(document); + return publishResult; } #endregion @@ -1258,7 +1427,7 @@ namespace Umbraco.Core.Services /// Id of the version to delete /// Boolean indicating whether to delete versions prior to the versionId /// Optional Id of the User deleting versions of a Content object - public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0) + public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = 0) { using (var uow = UowProvider.CreateUnitOfWork()) { @@ -1278,7 +1447,7 @@ namespace Umbraco.Core.Services uow.WriteLock(Constants.Locks.ContentTree); var repository = uow.CreateRepository(); var c = repository.Get(id); - if (c.Version != versionId) // don't delete the current version + if (c.VersionId != versionId) // don't delete the current version repository.DeleteVersion(versionId); uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId)); @@ -1669,7 +1838,7 @@ namespace Umbraco.Core.Services /// Id of the version to rollback to /// Optional Id of the User issueing the rollback of the Content /// The newly created object - public IContent Rollback(int id, Guid versionId, int userId = 0) + public IContent Rollback(int id, int versionId, int userId = 0) { using (var uow = UowProvider.CreateUnitOfWork()) { @@ -1694,7 +1863,7 @@ namespace Umbraco.Core.Services // in that last case, we want to copy the published values // what-if there's no 'published' version for now? // fixme WE DON'T WANT TO DO THIS HERE! - var copyPublished = ((Content) origContent).VersionPk > ((Content) origContent).PublishedVersionPk; + var copyPublished = origContent.VersionId > origContent.PublishedVersionId; //((Content) currContent).CopyAllValues(origContent, copyPublished); ((Content) currContent).CopyAllValues(origContent); currContent.WriterId = userId; @@ -1848,145 +2017,6 @@ namespace Umbraco.Core.Services repo.AddOrUpdate(new AuditItem(objectId, message, type, userId)); } - /// - /// Publishes a object and all its children - /// - /// The to publish along with its children - /// Optional Id of the User issueing the publishing - /// If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published - /// - /// A list of publish statues. If the parent document is not valid or cannot be published because it's parent(s) is not published - /// then the list will only contain one status item, otherwise it will contain status items for it and all of it's descendants that - /// are to be published. - /// - private IEnumerable PublishWithChildrenDo(IContent content, int userId = 0, bool includeUnpublished = false) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - - var evtMsgs = EventMessagesFactory.Get(); - var publishedItems = new List(); // this is for events - PublishResult[] attempts; - - using (var uow = UowProvider.CreateUnitOfWork()) - { - uow.WriteLock(Constants.Locks.ContentTree); - var repository = uow.CreateRepository(); - - // fail fast + use in alreadyChecked below to avoid duplicate checks - var attempt = StrategyCanPublish(uow, content, userId, /*checkPath:*/ true, evtMsgs); - if (attempt.Success == false) - return new[] { attempt }; // causes rollback - - var contents = new List { content }; //include parent item - contents.AddRange(GetDescendants(content)); - - // publish using the strategy - for descendants, - // - published w/out changes: nothing to do - // - published w/changes: publish those changes - // - unpublished: publish if includeUnpublished, otherwise ignore - var alreadyChecked = new[] { content }; - attempts = StrategyPublishWithChildren(uow, contents, alreadyChecked, userId, evtMsgs, includeUnpublished).ToArray(); - - foreach (var status in attempts.Where(x => x.Success)) - { - // save them all, even those that are .Success because of (.StatusType == PublishStatusType.SuccessAlreadyPublished) - // so we bump the date etc - var publishedItem = status.Content; - publishedItem.WriterId = userId; - repository.AddOrUpdate(publishedItem); - publishedItems.Add(publishedItem); - } - - uow.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); - uow.Events.Dispatch(Published, this, new PublishEventArgs(publishedItems, false, false), "Published"); - Audit(uow, AuditType.Publish, "Publish with Children performed by user", userId, content.Id); - - uow.Complete(); - } - - return attempts; - } - - /// - /// Saves and Publishes a single object - /// - /// The to save and publish - /// Optional Id of the User issueing the publishing - /// Optional boolean indicating whether or not to raise save events. - /// True if publishing succeeded, otherwise False - private PublishResult SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true) - { - var evtMsgs = EventMessagesFactory.Get(); - PublishResult status; - - using (var uow = UowProvider.CreateUnitOfWork()) - { - var saveEventArgs = new SaveEventArgs(content, evtMsgs); - if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving")) - { - uow.Complete(); - return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); - } - - var isNew = content.IsNewEntity(); - var changeType = isNew ? TreeChangeTypes.RefreshBranch : TreeChangeTypes.RefreshNode; - var previouslyPublished = content.HasIdentity && content.Published; - - uow.WriteLock(Constants.Locks.ContentTree); - var repository = uow.CreateRepository(); - - status = StrategyCanPublish(uow, content, userId, /*checkPath:*/ true, evtMsgs); - if (status.Success) - { - // strategy handles events, and various business rules eg release & expire - // dates, trashed status... - status = StrategyPublish(uow, content, true, userId, evtMsgs); - } - - // save - always, even if not publishing (this is SaveAndPublish) - if (content.HasIdentity == false) - content.CreatorId = userId; - content.WriterId = userId; - - repository.AddOrUpdate(content); - - if (raiseEvents) // always - { - saveEventArgs.CanCancel = false; - uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved"); - } - - if (status.Success == false) - { - uow.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - return status; - } - - if (isNew == false && previouslyPublished == false) - changeType = TreeChangeTypes.RefreshBranch; // whole branch - - // invalidate the node/branch - uow.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - - uow.Events.Dispatch(Published, this, new PublishEventArgs(content, false, false), "Published"); - - // if was not published and now is... descendants that were 'published' (but - // had an unpublished ancestor) are 're-published' ie not explicitely published - // but back as 'published' nevertheless - if (isNew == false && previouslyPublished == false && HasChildren(content.Id)) - { - var descendants = GetPublishedDescendantsLocked(uow, repository, content).ToArray(); - uow.Events.Dispatch(Published, this, new PublishEventArgs(descendants, false, false), "Published"); - } - - Audit(uow, AuditType.Publish, "Save and Publish performed by user", userId, content.Id); - - uow.Complete(); - } - - return status; - } - #endregion #region Event Handlers @@ -2135,213 +2165,115 @@ namespace Umbraco.Core.Services #region Publishing Strategies - // prob. want to find nicer names? - + // ensures that a document can be published internal PublishResult StrategyCanPublish(IScopeUnitOfWork uow, IContent content, int userId, bool checkPath, EventMessages evtMsgs) { + // raise Publishing event if (uow.Events.DispatchCancelable(Publishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' will not be published, the event was cancelled."); + Logger.Info($"Document \"'{content.Name}\" (id={content.Id}) cannot be published: publishing was cancelled."); return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); } - // check if the content is valid - var invalidProperties = content.Validate(); - if (invalidProperties.Any()) + // ensure that the document has published values + // either because it is 'publishing' or because it already has a published version + if (((Content) content).PublishedState != PublishedState.Publishing && content.PublishedVersionId == 0) { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' could not be published because of invalid properties."); - return new PublishResult(PublishResultType.FailedContentInvalid, evtMsgs, content) - { - InvalidProperties = invalidProperties - }; + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be published: document does not have published values."); + return new PublishResult(PublishResultType.FailedNoPublishedValues, evtMsgs, content); } - // check if the Content is Expired - if (content.Status == ContentStatus.Expired) + // ensure that the document status is correct + switch (content.Status) { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' has expired and could not be published."); - return new PublishResult(PublishResultType.FailedHasExpired, evtMsgs, content); + case ContentStatus.Expired: + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be published: document has expired."); + return new PublishResult(PublishResultType.FailedHasExpired, evtMsgs, content); + + case ContentStatus.AwaitingRelease: + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be published: document is awaiting release."); + return new PublishResult(PublishResultType.FailedAwaitingRelease, evtMsgs, content); + + case ContentStatus.Trashed: + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be published: document is trashed."); + return new PublishResult(PublishResultType.FailedIsTrashed, evtMsgs, content); } - // check if the Content is Awaiting Release - if (content.Status == ContentStatus.AwaitingRelease) - { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' is awaiting release and could not be published."); - return new PublishResult(PublishResultType.FailedAwaitingRelease, evtMsgs, content); - } - - // check if the Content is Trashed - if (content.Status == ContentStatus.Trashed) - { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' is trashed and could not be published."); - return new PublishResult(PublishResultType.FailedIsTrashed, evtMsgs, content); - } + if (!checkPath) return new PublishResult(evtMsgs, content); // check if the content can be path-published - if (checkPath) + // root content can be published + // else check ancestors - we know we are not trashed + var pathIsOk = content.ParentId == Constants.System.Root || IsPathPublished(GetParent(content)); + if (pathIsOk == false) { - // root content can be published - var pathIsOk = content.ParentId == Constants.System.Root; - - // else check ancestors - we know we are not trashed - if (pathIsOk == false) - pathIsOk = IsPathPublished(content.Parent()); - - if (pathIsOk == false) - { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' could not be published because its parent is not published."); - return new PublishResult(PublishResultType.FailedPathNotPublished, evtMsgs, content); - } + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be published: parent is not published."); + return new PublishResult(PublishResultType.FailedPathNotPublished, evtMsgs, content); } return new PublishResult(evtMsgs, content); } - internal PublishResult StrategyPublish(IScopeUnitOfWork uow, IContent content, bool alreadyCheckedCanPublish, int userId, EventMessages evtMsgs) + // publishes a document + internal PublishResult StrategyPublish(IScopeUnitOfWork uow, IContent content, bool canPublish, int userId, EventMessages evtMsgs) { // note: when used at top-level, StrategyCanPublish with checkPath=true should have run already // and alreadyCheckedCanPublish should be true, so not checking again. when used at nested level, - // there is no need to check the path again. so, checkPath=false in StrategyCanPublish below is ok + // there is no need to check the path again. so, checkPath=false in StrategyCanPublish below - var attempt = alreadyCheckedCanPublish + var result = canPublish ? new PublishResult(evtMsgs, content) // already know we can : StrategyCanPublish(uow, content, userId, /*checkPath:*/ false, evtMsgs); // else check - if (attempt.Success == false) - return attempt; + + if (result.Success == false) + return result; // change state to publishing ((Content) content).PublishedState = PublishedState.Publishing; - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' has been published."); - - return attempt; + Logger.Info($"Content \"{content.Name}\" (id={content.Id}) has been published."); + return result; } - /// - /// Publishes a list of content items - /// - /// Contents, ordered by level ASC - /// Contents for which we've already checked CanPublish - /// - /// - /// Indicates whether to publish content that is completely unpublished (has no published - /// version). If false, will only publish already published content with changes. Also impacts what happens if publishing - /// fails (see remarks). - /// - /// - /// Navigate content & descendants top-down and for each, - /// - if it is published - /// - and unchanged, do nothing - /// - else (has changes), publish those changes - /// - if it is not published - /// - and at top-level, publish - /// - or includeUnpublished is true, publish - /// - else do nothing & skip the underlying branch - /// - /// When publishing fails - /// - if content has no published version, skip the underlying branch - /// - else (has published version), - /// - if includeUnpublished is true, process the underlying branch - /// - else, do not process the underlying branch - /// - internal IEnumerable StrategyPublishWithChildren(IScopeUnitOfWork uow, IEnumerable contents, IEnumerable alreadyChecked, int userId, EventMessages evtMsgs, bool includeUnpublished = true) + // ensures that a document can be unpublished + internal PublishResult StrategyCanUnpublish(IScopeUnitOfWork uow, IContent content, int userId, EventMessages evtMsgs) { - var statuses = new List(); - var alreadyCheckedA = (alreadyChecked ?? Enumerable.Empty()).ToArray(); - - // list of ids that we exclude because they could not be published - var excude = new List(); - - var topLevel = -1; - foreach (var content in contents) - { - // initialize - content is ordered by level ASC - if (topLevel < 0) - topLevel = content.Level; - - if (excude.Contains(content.ParentId)) - { - // parent is excluded, so exclude content too - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' will not be published because it's parent's publishing action failed or was cancelled."); - excude.Add(content.Id); - // status has been reported for an ancestor and that one is excluded => no status - continue; - } - - if (content.Published && content.Level > topLevel) // topLevel we DO want to (re)publish - { - // newest is published already - statuses.Add(new PublishResult(PublishResultType.SuccessAlready, evtMsgs, content)); - continue; - } - - if (content.Level == topLevel || includeUnpublished) - { - // content has no published version, and we want to publish it, either - // because it is top-level or because we include unpublished. - // if publishing fails, and because content does not have a published - // version at all, ensure we do not process its descendants - var r = StrategyPublish(uow, content, alreadyCheckedA.Contains(content), userId, evtMsgs); - if (r.Success == false) - excude.Add(content.Id); - - statuses.Add(r); - continue; - } - - // content has no published version, and we don't want to publish it - excude.Add(content.Id); // ignore everything below it - // content is not even considered, really => no status - } - - return statuses; - } - - internal PublishResult StrategyCanUnPublish(IScopeUnitOfWork uow, IContent content, int userId, EventMessages evtMsgs) - { - // fire UnPublishing event + // raise UnPublishing event if (uow.Events.DispatchCancelable(UnPublishing, this, new PublishEventArgs(content, evtMsgs))) { - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' will not be unpublished, the event was cancelled."); + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) cannot be unpublished: unpublishing was cancelled."); return new PublishResult(PublishResultType.FailedCancelledByEvent, evtMsgs, content); } return new PublishResult(evtMsgs, content); } - internal PublishResult StrategyUnPublish(IScopeUnitOfWork uow, IContent content, bool alreadyCheckedCanUnPublish, int userId, EventMessages evtMsgs) + // unpublishes a document + internal PublishResult StrategyUnpublish(IScopeUnitOfWork uow, IContent content, bool canUnpublish, int userId, EventMessages evtMsgs) { - // 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 - - var attempt = alreadyCheckedCanUnPublish + var attempt = canUnpublish ? new PublishResult(evtMsgs, content) // already know we can - : StrategyCanUnPublish(uow, content, userId, evtMsgs); + : StrategyCanUnpublish(uow, content, userId, evtMsgs); // else check + if (attempt.Success == false) return attempt; - // if Content has a release date set to before now, it should be removed so it doesn't interrupt an unpublish + // if the document has a release date set to before now, + // it should be removed so it doesn't interrupt an unpublish // otherwise it would remain released == published if (content.ReleaseDate.HasValue && content.ReleaseDate.Value <= DateTime.Now) { content.ReleaseDate = null; - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' had its release date removed, because it was unpublished."); + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) had its release date removed, because it was unpublished."); } - // version is published or unpublished, but content is published // change state to unpublishing ((Content) content).PublishedState = PublishedState.Unpublishing; - Logger.Info($"Content '{content.Name}' with Id '{content.Id}' has been unpublished."); - + Logger.Info($"Document \"{content.Name}\" (id={content.Id}) has been unpublished."); return attempt; } - internal IEnumerable StrategyUnPublish(IScopeUnitOfWork uow, IEnumerable content, int userId, EventMessages evtMsgs) - { - return content.Select(x => StrategyUnPublish(uow, x, false, userId, evtMsgs)); - } - #endregion #region Content Types diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 990fc6e7b6..a7e44e77e2 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -94,7 +94,6 @@ namespace Umbraco.Core.Services //xml.Add(new XAttribute("creatorID", media.CreatorId)); xml.Add(new XAttribute("writerName", media.GetWriterProfile(userService).Name)); xml.Add(new XAttribute("writerID", media.WriterId)); - xml.Add(new XAttribute("version", media.Version)); //xml.Add(new XAttribute("template", 0)); // no template for media diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 46e12784b2..4cf2bf8569 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -129,12 +128,12 @@ namespace Umbraco.Core.Services /// Gets top versions of a document. /// /// Versions are ordered with current first, then most recent first. - IEnumerable GetVersionIds(int id, int topRows); + IEnumerable GetVersionIds(int id, int topRows); /// /// Gets a version of a document. /// - IContent GetVersion(Guid versionId); + IContent GetVersion(int versionId); /// /// Gets root-level documents. @@ -285,7 +284,7 @@ namespace Umbraco.Core.Services /// /// Deletes a version of a document. /// - void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0); + void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = 0); #endregion @@ -337,16 +336,21 @@ namespace Umbraco.Core.Services /// Property values must first be published at document level. PublishResult SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true); + /// + /// Saves and publishes a document branch. + /// + IEnumerable SaveAndPublishBranch(IContent content, bool force, ValueTuple[] variations, int userId = 0); + + /// + /// Saves and publishes a document branch. + /// + IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0); + /// /// Unpublishes a document. /// PublishResult Unpublish(IContent content, int userId = 0); - /// - /// Publishes a document and all its children. - /// - IEnumerable PublishWithChildren(IContent content, int userId = 0, bool includeUnpublished = false); - /// /// Gets a value indicating whether a document is path-publishable. /// @@ -373,7 +377,7 @@ namespace Umbraco.Core.Services /// /// Rolls a document back a previous version. /// - IContent Rollback(int id, Guid versionId, int userId = 0); + IContent Rollback(int id, int versionId, int userId = 0); #endregion diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 63cd816b16..749ade323e 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -312,7 +312,7 @@ namespace Umbraco.Core.Services /// /// Id of the version to retrieve /// An item - IMedia GetVersion(Guid versionId); + IMedia GetVersion(int versionId); /// /// Gets a collection of an objects versions by Id @@ -343,7 +343,7 @@ namespace Umbraco.Core.Services /// Id of the version to delete /// Boolean indicating whether to delete versions prior to the versionId /// Optional Id of the User deleting versions of a Content object - void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0); + void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = 0); /// /// Gets an object from the path stored in the 'umbracoFile' property. diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 6eb9d24bae..edcef9edd0 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -417,7 +417,7 @@ namespace Umbraco.Core.Services /// /// Id of the version to retrieve /// An item - public IMedia GetVersion(Guid versionId) + public IMedia GetVersion(int versionId) { using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { @@ -1033,7 +1033,7 @@ namespace Umbraco.Core.Services /// Id of the version to delete /// Boolean indicating whether to delete versions prior to the versionId /// Optional Id of the User deleting versions of a Media object - public void DeleteVersion(int id, Guid versionId, bool deletePriorVersions, int userId = 0) + public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = 0) { using (var uow = UowProvider.CreateUnitOfWork()) { diff --git a/src/Umbraco.Core/Services/PublishResultType.cs b/src/Umbraco.Core/Services/PublishResultType.cs index 1ecb447b66..c46a5e1725 100644 --- a/src/Umbraco.Core/Services/PublishResultType.cs +++ b/src/Umbraco.Core/Services/PublishResultType.cs @@ -54,6 +54,11 @@ /// /// The content item could not be published because it contains invalid data (has not passed validation requirements). /// - FailedContentInvalid = Failed | 6 + FailedContentInvalid = Failed | 6, + + /// + /// The document could not be published because it does not have published values. + /// + FailedNoPublishedValues = Failed | 7 } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5521f6c969..18f8879f3d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -522,7 +522,6 @@ - @@ -531,7 +530,6 @@ - diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 91e695de41..54d1ae8471 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -328,7 +328,6 @@ namespace Umbraco.Examine {"creatorName", new object[] {c.GetCreatorProfile(UserService).Name}}, {"writerName", new object[] {c.GetWriterProfile(UserService).Name}}, {"writerID", new object[] {c.WriterId}}, - {"version", new object[] {c.Version}}, {"template", new object[] {c.Template?.Id ?? 0}} }; diff --git a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs index d59a15e299..6af5ab454a 100644 --- a/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/XmlPublishedContentInitBenchmarks.cs @@ -160,7 +160,7 @@ namespace Umbraco.Tests.Benchmarks XmlPublishedContent.InitializeNode(null, _xml10.DocumentElement, false, out id, out key, out template, out sortOrder, out name, out writerName, out urlName, out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path, - out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType, + out createDate, out updateDate, out level, out isDraft, out publishedContentType, out properties, GetPublishedContentType); } @@ -170,7 +170,7 @@ namespace Umbraco.Tests.Benchmarks XmlPublishedContent.InitializeNode(null, _xml100.DocumentElement, false, out id, out key, out template, out sortOrder, out name, out writerName, out urlName, out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path, - out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType, + out createDate, out updateDate, out level, out isDraft, out publishedContentType, out properties, GetPublishedContentType); } @@ -180,7 +180,7 @@ namespace Umbraco.Tests.Benchmarks XmlPublishedContent.InitializeNode(null, _xml1000.DocumentElement, false, out id, out key, out template, out sortOrder, out name, out writerName, out urlName, out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path, - out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType, + out createDate, out updateDate, out level, out isDraft, out publishedContentType, out properties, GetPublishedContentType); } @@ -190,7 +190,7 @@ namespace Umbraco.Tests.Benchmarks XmlPublishedContent.InitializeNode(null, _xml10000.DocumentElement, false, out id, out key, out template, out sortOrder, out name, out writerName, out urlName, out creatorName, out creatorId, out writerId, out docTypeAlias, out nodeType, out path, - out version, out createDate, out updateDate, out level, out isDraft, out publishedContentType, + out createDate, out updateDate, out level, out isDraft, out publishedContentType, out properties, GetPublishedContentType); } diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 5a0d4c086a..72552fd6c8 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -758,7 +758,7 @@ namespace Umbraco.Tests.Integration ServiceContext.ContentService.Unpublish(content1); ResetEvents(); - ServiceContext.ContentService.PublishWithChildren(content1, 0, false); + ServiceContext.ContentService.SaveAndPublishBranch(content1, false); Assert.AreEqual(6, _msgCount); Assert.AreEqual(6, _events.Count); @@ -794,7 +794,7 @@ namespace Umbraco.Tests.Integration ServiceContext.ContentService.Unpublish(content1); ResetEvents(); - ServiceContext.ContentService.PublishWithChildren(content1, 0, true); + ServiceContext.ContentService.SaveAndPublishBranch(content1, true); Assert.AreEqual(14, _msgCount); Assert.AreEqual(14, _events.Count); @@ -2152,17 +2152,17 @@ namespace Umbraco.Tests.Integration Assert.IsNotNull(content); content.PublishValues(); ServiceContext.ContentService.SaveAndPublish(content); - var v1 = content.Version; + var v1 = content.VersionId; content.Properties.First().SetValue("changed"); content.PublishValues(); ServiceContext.ContentService.SaveAndPublish(content); - var v2 = content.Version; + var v2 = content.VersionId; content.Properties.First().SetValue("again"); content.PublishValues(); ServiceContext.ContentService.SaveAndPublish(content); - var v3 = content.Version; + var v3 = content.VersionId; Console.WriteLine(v1); Console.WriteLine(v2); diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 93068b5c5a..424ae83a96 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -167,7 +167,7 @@ namespace Umbraco.Tests.Models // Assert Assert.AreNotSame(clone, content); Assert.AreNotSame(clone.Id, content.Id); - Assert.AreNotSame(clone.Version, content.Version); + Assert.AreNotSame(clone.VersionId, content.VersionId); Assert.That(clone.HasIdentity, Is.False); Assert.AreNotSame(content.Properties, clone.Properties); @@ -210,7 +210,6 @@ namespace Umbraco.Tests.Models }; content.Trashed = false; content.UpdateDate = DateTime.Now; - content.Version = Guid.NewGuid(); content.WriterId = 23; ((IUmbracoEntity)content).AdditionalData.Add("test1", 123); @@ -273,7 +272,6 @@ namespace Umbraco.Tests.Models }; content.Trashed = false; content.UpdateDate = DateTime.Now; - content.Version = Guid.NewGuid(); content.WriterId = 23; ((IUmbracoEntity)content).AdditionalData.Add("test1", 123); @@ -286,7 +284,7 @@ namespace Umbraco.Tests.Models Assert.AreNotSame(clone, content); Assert.AreEqual(clone, content); Assert.AreEqual(clone.Id, content.Id); - Assert.AreEqual(clone.Version, content.Version); + Assert.AreEqual(clone.VersionId, content.VersionId); Assert.AreEqual(((IUmbracoEntity)clone).AdditionalData, ((IUmbracoEntity)content).AdditionalData); Assert.AreNotSame(clone.ContentType, content.ContentType); Assert.AreEqual(clone.ContentType, content.ContentType); @@ -319,7 +317,7 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.Template, content.Template); Assert.AreEqual(clone.Trashed, content.Trashed); Assert.AreEqual(clone.UpdateDate, content.UpdateDate); - Assert.AreEqual(clone.Version, content.Version); + Assert.AreEqual(clone.VersionId, content.VersionId); Assert.AreEqual(clone.WriterId, content.WriterId); Assert.AreNotSame(clone.Properties, content.Properties); Assert.AreEqual(clone.Properties.Count(), content.Properties.Count()); @@ -380,7 +378,6 @@ namespace Umbraco.Tests.Models }; content.Trashed = false; content.UpdateDate = DateTime.Now; - content.Version = Guid.NewGuid(); content.WriterId = 23; ((IUmbracoEntity)content).AdditionalData.Add("test1", 123); diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index cebc9fe83d..d86b9f03ad 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -62,7 +62,6 @@ namespace Umbraco.Tests.Models Assert.AreEqual(media.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); Assert.AreEqual(media.GetCreatorProfile(ServiceContext.UserService).Name, (string)element.Attribute("writerName")); Assert.AreEqual(media.CreatorId.ToString(), (string)element.Attribute("writerID")); - Assert.AreEqual(media.Version.ToString(), (string)element.Attribute("version")); Assert.IsNull(element.Attribute("template")); Assert.AreEqual(media.Properties[Constants.Conventions.Media.File].GetValue().ToString(), element.Elements(Constants.Conventions.Media.File).Single().Value); diff --git a/src/Umbraco.Tests/Models/MemberTests.cs b/src/Umbraco.Tests/Models/MemberTests.cs index c4e106e9cb..6b4cc4b3e8 100644 --- a/src/Umbraco.Tests/Models/MemberTests.cs +++ b/src/Umbraco.Tests/Models/MemberTests.cs @@ -45,7 +45,6 @@ namespace Umbraco.Tests.Models member.SortOrder = 5; member.Trashed = false; member.UpdateDate = DateTime.Now; - member.Version = Guid.NewGuid(); ((IUmbracoEntity)member).AdditionalData.Add("test1", 123); ((IUmbracoEntity)member).AdditionalData.Add("test2", "hello"); @@ -56,7 +55,7 @@ namespace Umbraco.Tests.Models Assert.AreNotSame(clone, member); Assert.AreEqual(clone, member); Assert.AreEqual(clone.Id, member.Id); - Assert.AreEqual(clone.Version, member.Version); + Assert.AreEqual(clone.VersionId, member.VersionId); Assert.AreEqual(((IUmbracoEntity)clone).AdditionalData, ((IUmbracoEntity)member).AdditionalData); Assert.AreNotSame(clone.ContentType, member.ContentType); Assert.AreEqual(clone.ContentType, member.ContentType); @@ -90,7 +89,7 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.LastPasswordChangeDate, member.LastPasswordChangeDate); Assert.AreEqual(clone.Trashed, member.Trashed); Assert.AreEqual(clone.UpdateDate, member.UpdateDate); - Assert.AreEqual(clone.Version, member.Version); + Assert.AreEqual(clone.VersionId, member.VersionId); Assert.AreEqual(clone.PasswordQuestion, member.PasswordQuestion); Assert.AreEqual(clone.ProviderUserKey, member.ProviderUserKey); Assert.AreEqual(clone.RawPasswordAnswerValue, member.RawPasswordAnswerValue); @@ -145,7 +144,6 @@ namespace Umbraco.Tests.Models member.SortOrder = 5; member.Trashed = false; member.UpdateDate = DateTime.Now; - member.Version = Guid.NewGuid(); ((IUmbracoEntity)member).AdditionalData.Add("test1", 123); ((IUmbracoEntity)member).AdditionalData.Add("test2", "hello"); diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 51f45cd1bd..c8111d4326 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -119,7 +119,7 @@ namespace Umbraco.Tests.Models var contentType = new ContentType(-1) { Alias = "contentType" }; contentType.AddPropertyType(propertyType); - var content = new Content("content", -1, contentType) { Id = 1, VersionPk = 1 }; + var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; // can set value // and get edited value, published is null @@ -197,7 +197,7 @@ namespace Umbraco.Tests.Models Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - var other = new Content("other", -1, contentType) { Id = 2, VersionPk = 1 }; + var other = new Content("other", -1, contentType) { Id = 2, VersionId = 1 }; other.SetValue("prop", "o"); other.SetValue("prop", "o1", 1); @@ -224,7 +224,7 @@ namespace Umbraco.Tests.Models var contentType = new ContentType(-1) { Alias = "contentType" }; contentType.AddPropertyType(propertyType); - var content = new Content("content", -1, contentType) { Id = 1, VersionPk = 1 }; + var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; prop.SetValue("a"); Assert.AreEqual("a", prop.GetValue()); diff --git a/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs index 50ddfd0cac..e91b72a4fe 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs @@ -1,4 +1,6 @@ using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; @@ -10,46 +12,29 @@ namespace Umbraco.Tests.Persistence.Mappers [Test] public void Can_Map_Id_Property() { - - // Act - string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Id"); - - - // Assert - Assert.That(column, Is.EqualTo("[umbracoNode].[id]")); + var column = new ContentMapper().Map(new SqlCeSyntaxProvider(), nameof(Content.Id)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.Node}].[id]")); } [Test] public void Can_Map_Trashed_Property() { - - // Act - string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Trashed"); - - // Assert - Assert.That(column, Is.EqualTo("[umbracoNode].[trashed]")); + var column = new ContentMapper().Map(new SqlCeSyntaxProvider(), nameof(Content.Trashed)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.Node}].[trashed]")); } [Test] public void Can_Map_Published_Property() { - - // Act - string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Published"); - - // Assert - Assert.That(column, Is.EqualTo("[uDocument].[published]")); + var column = new ContentMapper().Map(new SqlCeSyntaxProvider(), nameof(Content.Published)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.Document}].[published]")); } [Test] public void Can_Map_Version_Property() { - - // Act - string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Version"); - - // Assert - Assert.That(column, Is.EqualTo("[uContentVersion].[versionId]")); + var column = new ContentMapper().Map(new SqlCeSyntaxProvider(), nameof(Content.VersionId)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.ContentVersion}].[id]")); } } } diff --git a/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs index 8183a05c8c..a2c38bee8b 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs @@ -1,4 +1,6 @@ using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; @@ -10,42 +12,29 @@ namespace Umbraco.Tests.Persistence.Mappers [Test] public void Can_Map_Id_Property() { - // Act - string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "Id"); - - // Assert - Assert.That(column, Is.EqualTo("[umbracoNode].[id]")); + var column = new MediaMapper().Map(new SqlCeSyntaxProvider(), nameof(Media.Id)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.Node}].[id]")); } [Test] public void Can_Map_Trashed_Property() { - // Act - string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "Trashed"); - - // Assert - Assert.That(column, Is.EqualTo("[umbracoNode].[trashed]")); + var column = new MediaMapper().Map(new SqlCeSyntaxProvider(), nameof(Media.Trashed)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.Node}].[trashed]")); } [Test] public void Can_Map_UpdateDate_Property() { - // Act - string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "UpdateDate"); - - // Assert - Assert.That(column, Is.EqualTo("[uContentVersion].[versionDate]")); + var column = new MediaMapper().Map(new SqlCeSyntaxProvider(), nameof(Media.UpdateDate)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.ContentVersion}].[versionDate]")); } [Test] public void Can_Map_Version_Property() { - - // Act - string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "Version"); - - // Assert - Assert.That(column, Is.EqualTo("[uContentVersion].[versionId]")); + var column = new MediaMapper().Map(new SqlCeSyntaxProvider(), nameof(Media.VersionId)); + Assert.That(column, Is.EqualTo($"[{Constants.DatabaseSchema.Tables.ContentVersion}].[id]")); } } } diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index e761c6abec..4aa67a0404 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -144,7 +144,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests Path = "-1," + id1 }); - contentService.GetVersion(Guid.NewGuid()); + contentService.GetVersion(1234); } private void CreateStuff(out int id1, out int id2, out int id3, out string alias) diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index e2dde45b46..c1f3287a3e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(unitOfWork, out var contentTypeRepository, out DataTypeDefinitionRepository _); - var versions = new List(); + var versions = new List(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); ServiceContext.FileService.SaveTemplate(hasPropertiesContentType.DefaultTemplate); // else, FK violation on contentType! @@ -137,7 +137,7 @@ namespace Umbraco.Tests.Persistence.Repositories contentTypeRepository.AddOrUpdate(hasPropertiesContentType); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the first version + versions.Add(content1.VersionId); // the first version // publish = new edit version content1.SetValue("title", "title"); @@ -145,13 +145,13 @@ namespace Umbraco.Tests.Persistence.Repositories ((Content) content1).PublishedState = PublishedState.Publishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // NEW VERSION + versions.Add(content1.VersionId); // NEW VERSION // new edit version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -162,12 +162,12 @@ namespace Umbraco.Tests.Persistence.Repositories content1.SetValue("title", "title-1"); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the same version + versions.Add(content1.VersionId); // the same version // no new version has been created Assert.AreEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -176,13 +176,13 @@ namespace Umbraco.Tests.Persistence.Repositories ((Content) content1).PublishedState = PublishedState.Unpublishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the same version + versions.Add(content1.VersionId); // the same version // no new version has been created Assert.AreEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsFalse(content1.Published); Assert.AreEqual(PublishedState.Unpublished, ((Content) content1).PublishedState); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(false, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -193,11 +193,11 @@ namespace Umbraco.Tests.Persistence.Repositories content1.SetValue("title", "title-2"); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the same version + versions.Add(content1.VersionId); // the same version // no new version has been created Assert.AreEqual(versions[versions.Count - 2], versions[versions.Count - 1]); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(false, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -207,13 +207,13 @@ namespace Umbraco.Tests.Persistence.Repositories ((Content) content1).PublishedState = PublishedState.Publishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // NEW VERSION + versions.Add(content1.VersionId); // NEW VERSION // new version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -227,11 +227,11 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the same version + versions.Add(content1.VersionId); // the same version // no new version has been created Assert.AreEqual(versions[versions.Count - 2], versions[versions.Count - 1]); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -243,13 +243,13 @@ namespace Umbraco.Tests.Persistence.Repositories ((Content) content1).PublishedState = PublishedState.Publishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // NEW VERSION + versions.Add(content1.VersionId); // NEW VERSION // a new version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); - Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); @@ -263,13 +263,13 @@ namespace Umbraco.Tests.Persistence.Repositories foreach (var v in allVersions) { var c = (Content) v; - Console.WriteLine($"{c.Id} {c.Version} {(c.Published ? "+" : "-")}pub pk={c.VersionPk} ppk={c.PublishedVersionPk} name=\"{c.Name}\" pname=\"{c.PublishName}\""); + Console.WriteLine($"{c.Id} {c.VersionId} {(c.Published ? "+" : "-")}pub pk={c.VersionId} ppk={c.PublishedVersionId} name=\"{c.Name}\" pname=\"{c.PublishName}\""); } // get older version var content = repository.GetVersion(versions[versions.Count - 4]); - Assert.IsNotNull(content.Version); - Assert.AreEqual(versions[versions.Count - 4], content.Version); + Assert.AreNotEqual(0, content.VersionId); + Assert.AreEqual(versions[versions.Count - 4], content.VersionId); Assert.AreEqual("name-4", content1.Name); Assert.AreEqual("title-4", content1.GetValue("title")); Assert.AreEqual("name-2", content.Name); @@ -280,7 +280,7 @@ namespace Umbraco.Tests.Persistence.Repositories var expVersions = versions.Distinct().Reverse().ToArray(); Assert.AreEqual(expVersions.Length, allVersions.Length); for (var i = 0; i < expVersions.Length; i++) - Assert.AreEqual(expVersions[i], allVersions[i].Version); + Assert.AreEqual(expVersions[i], allVersions[i].VersionId); } } @@ -536,7 +536,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(content.Id, updatedContent.Id); Assert.AreEqual(content.Name, updatedContent.Name); - Assert.AreEqual(content.Version, updatedContent.Version); + Assert.AreEqual(content.VersionId, updatedContent.VersionId); Assert.AreEqual(content.GetValue("title"), "Welcome to our Home page"); content.SetValue("title", "toot"); @@ -545,7 +545,7 @@ namespace Umbraco.Tests.Persistence.Repositories updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); Assert.AreEqual("toot", updatedContent.GetValue("title")); - Assert.AreEqual(content.Version, updatedContent.Version); + Assert.AreEqual(content.VersionId, updatedContent.VersionId); } } @@ -606,7 +606,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(content.UpdateDate, Is.GreaterThan(DateTime.MinValue)); Assert.AreNotEqual(0, content.ParentId); Assert.AreEqual("Text Page 2", content.Name); - Assert.AreNotEqual(Guid.Empty, content.Version); + Assert.AreNotEqual(0, content.VersionId); Assert.AreEqual(NodeDto.NodeIdSeed + 1, content.ContentTypeId); Assert.That(content.Path, Is.Not.Empty); Assert.That(content.Properties.Any(), Is.True); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 6f74dffadb..da6897412e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -233,7 +233,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(media.ParentId, Is.Not.EqualTo(0)); Assert.That(media.Name, Is.EqualTo("Test Image")); Assert.That(media.SortOrder, Is.EqualTo(0)); - Assert.That(media.Version, Is.Not.EqualTo(Guid.Empty)); + Assert.That(media.VersionId, Is.Not.EqualTo(0)); Assert.That(media.ContentTypeId, Is.EqualTo(1032)); Assert.That(media.Path, Is.Not.Empty); Assert.That(media.Properties.Any(), Is.True); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index 35b8ba9895..397b571fb2 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -271,7 +271,6 @@ namespace Umbraco.Tests.Published public override string Path { get; } public override DateTime CreateDate { get; } public override DateTime UpdateDate { get; } - public override Guid Version { get; } public override int Level { get; } public override Guid Key { get; } // ReSharper restore UnassignedGetOnlyAutoProperty diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index 6fc8cbce35..cfc0df25b7 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -79,9 +79,7 @@ namespace Umbraco.Tests.PublishedContent pc.Setup(content => content.CreateDate).Returns(DateTime.Now); pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); pc.Setup(content => content.Path).Returns("-1,1"); - pc.Setup(content => content.Version).Returns(Guid.NewGuid); pc.Setup(content => content.Parent).Returns(() => null); - pc.Setup(content => content.Version).Returns(Guid.NewGuid); pc.Setup(content => content.Properties).Returns(new Collection()); return pc; } diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 493a21855d..0b89c99412 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -22,158 +22,8 @@ namespace Umbraco.Tests.Publishing //LegacyUmbracoSettings.SettingsFilePath = IOHelper.MapPath(SystemDirectories.Config + Path.DirectorySeparatorChar, false); } - [TearDown] - public override void TearDown() - { - base.TearDown(); - - //ensure event handler is gone - ContentService.Publishing -= StrategyPublishing; - } - private IContent _homePage; - /// - /// in these tests we have a heirarchy of - /// - home - /// -- text page 1 - /// -- text page 2 - /// --- text page 3 - /// - /// For this test, none of them are published, then we bulk publish them all, however one of the nodes will fail publishing - /// because it is not valid, then it's children won't be published either because it's never been published. - /// - [Test] - public void Publishes_Many_Ignores_Invalid_Items_And_Children() - { - var testData = CreateTestData(); - //Create some other data which are descendants of Text Page 2 - var mandatorContent = MockedContent.CreateSimpleContent( - ServiceContext.ContentTypeService.Get("umbMandatory"), "Invalid Content", testData.Single(x => x.Name == "Text Page 2").Id); - mandatorContent.SetValue("author", string.Empty); - ServiceContext.ContentService.Save(mandatorContent, 0); - var subContent = MockedContent.CreateSimpleContent( - ServiceContext.ContentTypeService.Get("umbTextpage"), "Sub Sub Sub", mandatorContent.Id); - ServiceContext.ContentService.Save(subContent, 0); - - //publish root and nodes at it's children level - var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] { _homePage }); - var evtMsgs = new EventMessages(); - var uow = TestObjects.GetUnitOfWorkMock(); - var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(uow, listToPublish, null, 0, evtMsgs, true); - - Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success)); - Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Content.Id) - .ContainsAll(listToPublish.Where(x => x.Name != "Invalid Content" && x.Name != "Sub Sub Sub").Select(x => x.Id))); - } - - /// - /// in these tests we have a heirarchy of - /// - home - /// -- text page 1 - /// -- text page 2 - /// --- text page 3 - /// - /// For this test, none of them are published, then we bulk publish them all, however we cancel the publishing for - /// "text page 2". This internally will ensure that text page 3 doesn't get published either because text page 2 has - /// never been published. - /// - [Test] - public void Publishes_Many_Ignores_Cancelled_Items_And_Children() - { - CreateTestData(); - - ContentService.Publishing += StrategyPublishing; - - //publish root and nodes at it's children level - var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] {_homePage}); - var evtMsgs = new EventMessages(); - var uow = TestObjects.GetUnitOfWorkMock(); - var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(uow, listToPublish, null, 0, evtMsgs); - - Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success)); - Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Content.Id) - .ContainsAll(listToPublish.Where(x => x.Name != "Text Page 2" && x.Name != "Text Page 3").Select(x => x.Id))); - } - - static void StrategyPublishing(IContentService sender, PublishEventArgs e) - { - if (e.PublishedEntities.Any(x => x.Name == "Text Page 2")) - e.Cancel = true; - } - - [Test] - [Ignore("cannot work now that publishing has changed")] // fixme redo - public void Publishes_Many_Ignores_Unpublished_Items() - { - CreateTestData(); - - var evtMsgs = new EventMessages(); - - //publish root and nodes at it's children level - var uow = TestObjects.GetUnitOfWorkMock(); - var result1 = ((ContentService)ServiceContext.ContentService).StrategyPublish(uow, _homePage, false, 0, evtMsgs); - Assert.IsTrue(result1.Success); - Assert.IsTrue(_homePage.Published); - foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id)) - { - var r = ((ContentService)ServiceContext.ContentService).StrategyPublish(uow, c, false, 0, evtMsgs); - Assert.IsTrue(r.Success); - 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 - beware that StrategyPublishWithChildren expects content to be ordered by level - var content = new[] {_homePage}.Union(ServiceContext.ContentService.GetDescendants(_homePage)); - var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(uow, content, null, 0, evtMsgs, 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. - // NOTE - not true, we'll have Success for _homePage because we DO want to publish it - Assert.AreEqual(1, result.Count(x => x.Result == PublishResultType.Success)); - } - - [Test] - [Ignore("cannot work now that publishing has changed")] // fixme redo - public void Publishes_Many_Does_Not_Ignore_Unpublished_Items() - { - CreateTestData(); - - var evtMsgs = new EventMessages(); - - //publish root and nodes at it's children level - var uow = TestObjects.GetUnitOfWorkMock(); - var result1 = ((ContentService)ServiceContext.ContentService).StrategyPublish(uow, _homePage, false, 0, evtMsgs); - Assert.IsTrue(result1.Success); - 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 = ((ContentService)ServiceContext.ContentService).StrategyPublish(uow, c, false, 0, evtMsgs); - Assert.IsTrue(r.Success); - Assert.IsTrue(c.Published); - } - - //NOTE (MCH) when doing the test like this the Publish status will not actually have been persisted - //since its only updating a property. The actual persistence and publishing is done through the ContentService. - //So when descendants are fetched from the ContentService the Publish status will be "reset", which - //means the result will be 1 'SuccessAlreadyPublished' and 3 'Success' because the Homepage is - //inserted in the list and since that item has the status of already being Published it will be the one item - //with 'SuccessAlreadyPublished' - // - // NOTE - this is WRONG, the first one (_homepage) we really want to publish! - // not sure this test makes sense at all... - - var contents = new[] {_homePage} // top-level MUST be the first one here - .Concat(ServiceContext.ContentService.GetDescendants(_homePage)); - var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(uow, contents, null, 0, evtMsgs, true); - - Assert.AreEqual(4, result.Count(x => x.Result == PublishResultType.Success)); - Assert.AreEqual(0, result.Count(x => x.Result == PublishResultType.SuccessAlready)); - Assert.IsTrue(result.First(x => x.Result == PublishResultType.Success).Success); - Assert.IsTrue(result.First(x => x.Result == PublishResultType.Success).Content.Published); - } - [NUnit.Framework.Ignore("fixme - ignored test")] [Test] public void Can_Publish_And_Update_Xml_Cache() diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index b99a4ebf88..193b00bd26 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -724,7 +724,7 @@ namespace Umbraco.Tests.Services contentService.Save(child2); // Act - contentService.PublishWithChildren(content, includeUnpublished: true); + contentService.SaveAndPublishBranch(content, true); // Assert var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id; @@ -1064,24 +1064,24 @@ namespace Umbraco.Tests.Services var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 4).ToList(); Assert.AreEqual(1, versions.Count); - var version1 = content.Version; - Console.WriteLine($"1 e={((Content)content).VersionPk} p={((Content)content).PublishedVersionPk}"); + var version1 = content.VersionId; + Console.WriteLine($"1 e={content.VersionId} p={content.PublishedVersionId}"); content.Name = "Text Page 2 Updated"; content.SetValue("author", "Jane Doe"); content.PublishValues(); contentService.SaveAndPublish(content); // publishes the current version, creates a version - var version2 = content.Version; - Console.WriteLine($"2 e={((Content) content).VersionPk} p={((Content) content).PublishedVersionPk}"); + var version2 = content.VersionId; + Console.WriteLine($"2 e={content.VersionId} p={content.PublishedVersionId}"); content.Name = "Text Page 2 ReUpdated"; content.SetValue("author", "Bob Hope"); content.PublishValues(); contentService.SaveAndPublish(content); // publishes again, creates a version - var version3 = content.Version; - Console.WriteLine($"3 e={((Content) content).VersionPk} p={((Content) content).PublishedVersionPk}"); + var version3 = content.VersionId; + Console.WriteLine($"3 e={content.VersionId} p={content.PublishedVersionId}"); var content1 = contentService.GetById(content.Id); Assert.AreEqual("Bob Hope", content1.GetValue("author")); @@ -1099,15 +1099,15 @@ namespace Umbraco.Tests.Services Assert.AreEqual(3, versions.Count); // versions come with most recent first - Assert.AreEqual(version3, versions[0].Version); // the edited version - Assert.AreEqual(version2, versions[1].Version); // the published version - Assert.AreEqual(version1, versions[2].Version); // the previously published version + Assert.AreEqual(version3, versions[0].VersionId); // the edited version + Assert.AreEqual(version2, versions[1].VersionId); // the published version + Assert.AreEqual(version1, versions[2].VersionId); // the previously published version // p is always the same, published version // e is changing, actual version we're loading Console.WriteLine(); foreach (var version in ((IEnumerable) versions).Reverse()) - Console.WriteLine($"+ e={((Content) version).VersionPk} p={((Content) version).PublishedVersionPk}"); + Console.WriteLine($"+ e={((Content) version).VersionId} p={((Content) version).PublishedVersionId}"); // and proper values // first, the current (edited) version, with edited and published versions @@ -1272,7 +1272,7 @@ namespace Umbraco.Tests.Services content.PublishValues(); var published = contentService.SaveAndPublish(content, 0); - Assert.That(published, Is.True); + Assert.That(published.Success, Is.True); Assert.That(content.Published, Is.True); var e = ServiceContext.ContentService.GetById(content.Id); @@ -1297,46 +1297,92 @@ namespace Umbraco.Tests.Services [Test] public void Can_Publish_Only_Valid_Content() { - // Arrange - var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true); contentTypeService.Save(contentType); - Content content = MockedContent.CreateSimpleContent(contentType, "Invalid Content", NodeDto.NodeIdSeed + 1); + const int parentId = NodeDto.NodeIdSeed + 2; + + var contentService = ServiceContext.ContentService; + var content = MockedContent.CreateSimpleContent(contentType, "Invalid Content", parentId); content.SetValue("author", string.Empty); - contentService.Save(content, 0); + contentService.Save(content); - // Act - var parent = contentService.GetById(NodeDto.NodeIdSeed + 2); - parent.PublishValues(); - var parentPublished = contentService.SaveAndPublish(parent, 0); - content.PublishValues(); - var published = contentService.SaveAndPublish(content, 0); + var parent = contentService.GetById(parentId); - // Assert - Assert.That(parentPublished.Success, Is.True); - Assert.That(published.Success, Is.False); + var parentCanPublishValues = parent.PublishValues(); + var parentPublished = contentService.SaveAndPublish(parent); + + // parent can publish values + // and therefore can be published + Assert.IsTrue(parentCanPublishValues); + Assert.IsTrue(parentPublished.Success); + Assert.IsTrue(parent.Published); + + var contentCanPublishValues = content.PublishValues(); + var contentPublished = contentService.SaveAndPublish(content); + + // content cannot publish values because they are invalid + Assert.IsFalse(contentCanPublishValues); Assert.IsNotEmpty(content.Validate()); - Assert.That(parent.Published, Is.True); - Assert.That(content.Published, Is.False); + + // and therefore cannot be published, + // because it did not have a published version at all + Assert.IsFalse(contentPublished.Success); + Assert.AreEqual(PublishResultType.FailedNoPublishedValues, contentPublished.Result); + Assert.IsFalse(content.Published); + } + + // documents: an enumeration of documents, in tree order + // map: applies (if needed) PublishValue, returns a value indicating whether to proceed with the branch + private IEnumerable MapPublishValues(IEnumerable documents, Func map) + { + var exclude = new HashSet(); + foreach (var document in documents) + { + if (exclude.Contains(document.ParentId)) + { + exclude.Add(document.Id); + continue; + } + if (!map(document)) + { + exclude.Add(document.Id); + continue; + } + yield return document; + } } [Test] public void Can_Publish_Content_Children() { - // Arrange + const int parentId = NodeDto.NodeIdSeed + 2; + var contentService = ServiceContext.ContentService; - var content = contentService.GetById(NodeDto.NodeIdSeed + 2); + var parent = contentService.GetById(parentId); - // Act - var published = contentService.PublishWithChildren(content, 0); - var children = contentService.GetChildren(NodeDto.NodeIdSeed + 2); + Console.WriteLine(" " + parent.Id); + foreach (var x in contentService.GetDescendants(parent)) + Console.WriteLine(" ".Substring(0, x.Level) + x.Id); + Console.WriteLine(); - // Assert - Assert.That(published.All(x => x.Success), Is.True);//Nothing was cancelled, so should be true - Assert.That(content.Published, Is.True);//No restrictions, so should be published - Assert.That(children.First(x => x.Id == NodeDto.NodeIdSeed + 3).Published, Is.True);//Released 5 mins ago, so should be published + // publish parent & its branch + // only those that are not already published + // only invariant/neutral values + var parentPublished = contentService.SaveAndPublishBranch(parent, true); + + foreach (var result in parentPublished) + Console.WriteLine(" ".Substring(0, result.Content.Level) + $"{result.Content.Id}: {result.Result}"); + + // everything should be successful + Assert.IsTrue(parentPublished.All(x => x.Success)); + Assert.IsTrue(parent.Published); + + var children = contentService.GetChildren(parentId); + + // children are published including ... that was released 5 mins ago + Assert.IsTrue(children.First(x => x.Id == NodeDto.NodeIdSeed + 4).Published); } [Test] @@ -1394,7 +1440,7 @@ namespace Umbraco.Tests.Services contentService.Save(content, 0); // Act - var published = contentService.PublishWithChildren(content, 0); + var published = contentService.SaveAndPublishBranch(content, true); // Assert Assert.That(published.All(x => x.Success), Is.False); @@ -1467,8 +1513,8 @@ namespace Umbraco.Tests.Services Assert.That(content.Published, Is.True); Assert.That(childContent.HasIdentity, Is.True); Assert.That(childContent.Published, Is.True); - Assert.That(published, Is.True); - Assert.That(childPublished, Is.True); + Assert.That(published.Success, Is.True); + Assert.That(childPublished.Success, Is.True); } [Test] @@ -1485,11 +1531,11 @@ namespace Umbraco.Tests.Services content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Published"); content.PublishValues(); var contentPublished = contentService.SaveAndPublish(content); - var publishedVersion = content.Version; + var publishedVersion = content.VersionId; content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Saved"); contentService.Save(content); - Assert.AreEqual(publishedVersion, content.Version); + Assert.AreEqual(publishedVersion, content.VersionId); // Act var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root).ToList(); @@ -1501,10 +1547,10 @@ namespace Umbraco.Tests.Services //Console.WriteLine(publishedVersion); //foreach (var d in publishedDescendants) Console.WriteLine(d.Version); - Assert.IsTrue(publishedDescendants.Any(x => x.Version == publishedVersion)); + Assert.IsTrue(publishedDescendants.Any(x => x.VersionId == publishedVersion)); //Ensure that the published content version has the correct property value and is marked as published - var publishedContentVersion = publishedDescendants.First(x => x.Version == publishedVersion); + var publishedContentVersion = publishedDescendants.First(x => x.VersionId == publishedVersion); Assert.That(publishedContentVersion.Published, Is.True); Assert.That(publishedContentVersion.Properties["title"].GetValue(published: true), Contains.Substring("Published")); @@ -1516,7 +1562,7 @@ namespace Umbraco.Tests.Services Assert.That(currentContent.Published, Is.True); Assert.That(currentContent.Properties["title"].GetValue(published: true), Contains.Substring("Published")); Assert.That(currentContent.Properties["title"].GetValue(), Contains.Substring("Saved")); - Assert.That(currentContent.Version, Is.EqualTo(publishedContentVersion.Version)); + Assert.That(currentContent.VersionId, Is.EqualTo(publishedContentVersion.VersionId)); } [Test] @@ -1959,7 +2005,7 @@ namespace Umbraco.Tests.Services var versions = contentService.GetVersions(NodeDto.NodeIdSeed + 4).ToList(); Assert.AreEqual(1, versions.Count); - var version1 = content.Version; + var version1 = content.VersionId; content.Name = "Text Page 2 Updated"; content.SetValue("author", "Francis Doe"); @@ -1969,7 +2015,7 @@ namespace Umbraco.Tests.Services content.PublishValues(); contentService.SaveAndPublish(content); // new version - var version2 = content.Version; + var version2 = content.VersionId; Assert.AreNotEqual(version1, version2); Assert.IsTrue(content.Published); @@ -1994,7 +2040,7 @@ namespace Umbraco.Tests.Services content.PublishValues(); contentService.SaveAndPublish(content); // new version - var version3 = content.Version; + var version3 = content.VersionId; Assert.AreNotEqual(version2, version3); Assert.IsTrue(content.Published); @@ -2016,7 +2062,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(rollback.Published); Assert.IsTrue(rollback.Edited); Assert.AreEqual("Francis Doe", contentService.GetById(content.Id).GetValue("author")); // author is now Francis again - Assert.AreEqual(version3, rollback.Version); // same version but with edits + Assert.AreEqual(version3, rollback.VersionId); // same version but with edits // props and name have rolled back Assert.AreEqual("Francis Doe", rollback.GetValue("author")); @@ -2047,7 +2093,7 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Bob Doe"); contentService.Save(content); Assert.IsTrue(content.Edited); - content = contentService.Rollback(content.Id, content.Version); + content = contentService.Rollback(content.Id, content.VersionId); Assert.IsFalse(content.Edited); Assert.AreEqual("Text Page 2 ReReUpdated", content.Name); Assert.AreEqual("Jane Doe", content.GetValue("author")); @@ -2134,14 +2180,14 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var content = contentService.GetById(NodeDto.NodeIdSeed + 5); - var version = content.Version; + var version = content.VersionId; // Act contentService.DeleteVersion(NodeDto.NodeIdSeed + 5, version, true, 0); var sut = contentService.GetById(NodeDto.NodeIdSeed + 5); // Assert - Assert.That(sut.Version, Is.EqualTo(version)); + Assert.That(sut.VersionId, Is.EqualTo(version)); } [Test] @@ -2326,8 +2372,8 @@ namespace Umbraco.Tests.Services Assert.AreEqual("foo", content.GetValue("title", published: true)); Assert.AreEqual("foo", content.GetValue("title")); - var vpk = ((Content) content).VersionPk; - var ppk = ((Content) content).PublishedVersionPk; + var vpk = ((Content) content).VersionId; + var ppk = ((Content) content).PublishedVersionId; content = contentService.GetById(content.Id); Assert.IsFalse(content.Published); @@ -2336,8 +2382,8 @@ namespace Umbraco.Tests.Services // fixme - depending on 1 line in ContentBaseFactory.BuildEntity // the published infos can be gone or not // if gone, it's not consistent with above - Assert.AreEqual(vpk, ((Content) content).VersionPk); - Assert.AreEqual(ppk, ((Content) content).PublishedVersionPk); // still there + Assert.AreEqual(vpk, ((Content) content).VersionId); + Assert.AreEqual(ppk, ((Content) content).PublishedVersionId); // still there // fixme - depending on 1 line in ContentRepository.MapDtoToContent // the published values can be null or not diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index b39528fea1..8dd3c3ee5a 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -110,7 +110,6 @@ namespace Umbraco.Web.Models public abstract string Path { get; } public abstract DateTime CreateDate { get; } public abstract DateTime UpdateDate { get; } - public abstract Guid Version { get; } public abstract int Level { get; } public abstract bool IsDraft { get; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs index 916bc6207e..452e234849 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/Database.cs @@ -30,7 +30,6 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .AndSelect(x => Alias(x.ContentTypeId, "ContentTypeId")) .AndSelect(x => Alias(x.Published, "Published"), x => Alias(x.Edited, "Edited")) - .AndSelect(x => Alias(x.VersionId, "Version")) .AndSelect(x => Alias(x.Text, "DraftName"), x => Alias(x.VersionDate, "DraftVersionDate"), x => Alias(x.UserId, "DraftWriterId")) .AndSelect(x => Alias(x.TemplateId, "DraftTemplateId")) @@ -51,7 +50,6 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource .AndSelect(x => Alias(x.ContentTypeId, "ContentTypeId")) //.AndSelect(x => Alias(x.Published, "Published"), x => Alias(x.Edited, "Edited")) - .AndSelect(x => Alias(x.VersionId, "Version")) .AndSelect(x => Alias(x.Text, "PubName"), x => Alias(x.VersionDate, "PubVersionDate"), x => Alias(x.UserId, "PubWriterId")) .AndSelect(x => Alias(x.TemplateId, "PubTemplateId")) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index e8530b78f8..25b6900d39 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -164,7 +164,6 @@ namespace Umbraco.Web.PublishedCache.NuCache public override int Level => _contentNode.Level; public override string Path => _contentNode.Path; public override int SortOrder => _contentNode.SortOrder; - public override Guid Version => _contentData.Version; public override int TemplateId => _contentData.TemplateId; public override string UrlName => _urlName; diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs index 8bdce0fb26..a034411c5e 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedMember.cs @@ -28,7 +28,6 @@ namespace Umbraco.Web.PublishedCache.NuCache Name = member.Name, Published = previewing, TemplateId = -1, - Version = member.Version, VersionDate = member.UpdateDate, WriterId = member.CreatorId, // what else? Properties = GetPropertyValues(contentType, member) diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs index 8f7bed8f23..78264638cc 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs @@ -145,8 +145,6 @@ namespace Umbraco.Web.PublishedCache public override DateTime UpdateDate => _member.UpdateDate; - public override Guid Version => _member.Version; - public override int Level => _member.Level; #endregion diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 016e25b8eb..2768133072 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -768,8 +768,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override DateTime UpdateDate => _updateDate; - public override Guid Version => Guid.Empty; - public override int Level => _level; public override bool IsDraft => false; diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 24457468fe..62dbbb406e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -6,7 +6,6 @@ using System.Xml.Serialization; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.Composing; using Umbraco.Web.Models; @@ -59,7 +58,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private string _path; private DateTime _createDate; private DateTime _updateDate; - private Guid _version; private int _sortOrder; private int _level; private bool _isDraft; @@ -233,15 +231,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } } - public override Guid Version - { - get - { - if (_nodeInitialized == false) InitializeNode(); - return _version; - } - } - public override string UrlName { get @@ -304,7 +293,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache InitializeNode(this, _xmlNode, _isPreviewing, out _id, out _key, out _template, out _sortOrder, out _name, out _writerName, out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path, - out _version, out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties, + out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties, _contentTypeCache.Get); // warn: this is not thread-safe... @@ -314,7 +303,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing, out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName, out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path, - out Guid version, out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft, + out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft, out PublishedContentType contentType, out Dictionary properties, Func getPublishedContentType) { @@ -322,7 +311,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache writerName = null; docTypeAlias = null; id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int); - key = version = default(Guid); + key = default(Guid); name = writerName = urlName = creatorName = docTypeAlias = path = null; createDate = updateDate = default(DateTime); isDraft = false; @@ -361,8 +350,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value); if (xmlNode.Attributes.GetNamedItem("path") != null) path = xmlNode.Attributes.GetNamedItem("path").Value; - if (xmlNode.Attributes.GetNamedItem("version") != null) - version = new Guid(xmlNode.Attributes.GetNamedItem("version").Value); if (xmlNode.Attributes.GetNamedItem("createDate") != null) createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value); if (xmlNode.Attributes.GetNamedItem("updateDate") != null) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index 8f1f9f01ac..de06d65cd0 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -1487,7 +1487,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; OnRemovedVersion(args.UnitOfWork.Database, args.EntityId, args.VersionId); } - private static void OnRemovedVersion(IUmbracoDatabase db, int entityId, Guid versionId) + private static void OnRemovedVersion(IUmbracoDatabase db, int entityId, int versionId) { // we do not version cmsPreviewXml anymore - nothing to do here } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index f061a2c36f..7171121e78 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -290,7 +290,7 @@ namespace Umbraco.Web.Search private static void ReIndexForContent(IContent content, IContent published) { - if (published != null && content.Version == published.Version) + if (published != null && content.VersionId == published.VersionId) { ReIndexForContent(content); // same = both } diff --git a/src/Umbraco.Web/WebServices/BulkPublishController.cs b/src/Umbraco.Web/WebServices/BulkPublishController.cs index 55e78a97bc..c2f888527e 100644 --- a/src/Umbraco.Web/WebServices/BulkPublishController.cs +++ b/src/Umbraco.Web/WebServices/BulkPublishController.cs @@ -39,9 +39,8 @@ namespace Umbraco.Web.WebServices } else { - var result = Services.ContentService - .PublishWithChildren(content, Security.CurrentUser.Id, includeUnpublished) - .ToArray(); + // fixme variants? + var result = Services.ContentService.SaveAndPublishBranch(content, includeUnpublished); return Json(new { diff --git a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs index f3d419b6f9..61c334f184 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs @@ -37,7 +37,8 @@ namespace Umbraco.Web._Legacy.PackageActions { if (rootDoc.Name.Trim() == documentName.Trim() && rootDoc != null && rootDoc.ContentType != null) { - Current.Services.ContentService.PublishWithChildren(rootDoc, 0, true); + // fixme variants? + Current.Services.ContentService.SaveAndPublishBranch(rootDoc, true); break; } } diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 155badd774..c3a27f3438 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -180,8 +180,6 @@ namespace umbraco { case "id": return doc.Id.ToString(); - case "version": - return doc.Version.ToString(); case "parentID": return doc.Parent.Id.ToString(); case "level": diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index efe80824ab..258ae93d6c 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -67,7 +67,7 @@ namespace umbraco populatePageData(frequest.PublishedContent.Id, frequest.PublishedContent.Name, frequest.PublishedContent.DocumentTypeId, frequest.PublishedContent.DocumentTypeAlias, frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, - frequest.PublishedContent.Path, frequest.PublishedContent.Version, frequest.PublishedContent.Parent == null ? -1 : frequest.PublishedContent.Parent.Id); + frequest.PublishedContent.Path, frequest.PublishedContent.Parent == null ? -1 : frequest.PublishedContent.Parent.Id); if (frequest.HasTemplate) { @@ -91,7 +91,7 @@ namespace umbraco populatePageData(doc.Id, doc.Name, doc.DocumentTypeId, doc.DocumentTypeAlias, doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate, - doc.Path, doc.Version, doc.Parent == null ? -1 : doc.Parent.Id); + doc.Path, doc.Parent == null ? -1 : doc.Parent.Id); if (doc.TemplateId > 0) { @@ -119,7 +119,7 @@ namespace umbraco void populatePageData(int pageID, string pageName, int nodeType, string nodeTypeAlias, string writerName, string creatorName, DateTime createDate, DateTime updateDate, - string path, Guid pageVersion, int parentId) + string path, int parentId) { this._pageId = pageID; this._pageName = pageName; @@ -132,7 +132,6 @@ namespace umbraco this._parentId = parentId; this._path = path; this._splitpath = path.Split(','); - this._pageVersion = pageVersion; // Update the elements hashtable _elements.Add("pageID", pageID); @@ -146,7 +145,6 @@ namespace umbraco _elements.Add("updateDate", updateDate); _elements.Add("path", path); _elements.Add("splitpath", _splitpath); - _elements.Add("pageVersion", pageVersion); } void populatePageData(XmlNode node) @@ -526,11 +524,6 @@ namespace umbraco get { return _inner.UpdateDate; } } - public Guid Version - { - get { return _inner.Version; } - } - public int Level { get { return _inner.Level; }