From fcf2b27953041b682830907c6b28a4e28c5f0e53 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 10 Nov 2017 11:27:12 +0100 Subject: [PATCH] More content refactoring (tests) --- .../Cache/DeepCloneRuntimeCacheProvider.cs | 3 +- src/Umbraco.Core/Models/Content.cs | 79 +-- src/Umbraco.Core/Models/ContentBase.cs | 50 +- src/Umbraco.Core/Models/ContentExtensions.cs | 181 ----- src/Umbraco.Core/Models/EntityBase/Entity.cs | 2 +- src/Umbraco.Core/Models/EntityBase/IEntity.cs | 17 +- src/Umbraco.Core/Models/IContent.cs | 10 - src/Umbraco.Core/Models/IContentBase.cs | 7 +- src/Umbraco.Core/Models/Property.cs | 167 +++-- src/Umbraco.Core/Models/PropertyCollection.cs | 2 +- src/Umbraco.Core/Models/PropertyType.cs | 2 +- src/Umbraco.Core/Models/PublishedState.cs | 62 +- src/Umbraco.Core/Models/Rdbms/ContentDto.cs | 25 +- .../Models/Rdbms/ContentVersionDto.cs | 4 +- src/Umbraco.Core/Models/Rdbms/DocumentDto.cs | 19 +- .../Models/Rdbms/DocumentVersionDto.cs | 4 +- src/Umbraco.Core/Models/Rdbms/MemberDto.cs | 7 +- src/Umbraco.Core/Models/Rdbms/NodeDto.cs | 2 +- .../Models/Rdbms/PropertyDataDto.cs | 6 +- .../Persistence/Factories/ContentFactory.cs | 17 +- .../Factories/ContentTypeFactory.cs | 6 +- .../Factories/DataTypeDefinitionFactory.cs | 3 +- .../Factories/DictionaryItemFactory.cs | 4 +- .../Factories/DictionaryTranslationFactory.cs | 3 +- .../Factories/ExternalLoginFactory.cs | 3 +- .../Persistence/Factories/LanguageFactory.cs | 3 +- .../Persistence/Factories/MacroFactory.cs | 3 +- .../Persistence/Factories/MediaFactory.cs | 7 +- .../Persistence/Factories/MemberFactory.cs | 7 +- .../Factories/MemberGroupFactory.cs | 3 +- .../Factories/MemberTypeReadOnlyFactory.cs | 8 +- .../Factories/MigrationEntryFactory.cs | 4 +- .../Persistence/Factories/PropertyFactory.cs | 3 - .../Factories/PropertyGroupFactory.cs | 7 +- .../Factories/PublicAccessEntryFactory.cs | 3 +- .../Persistence/Factories/RelationFactory.cs | 3 +- .../Factories/RelationTypeFactory.cs | 3 +- .../Factories/ServerRegistrationFactory.cs | 3 +- .../Persistence/Factories/TagFactory.cs | 3 +- .../Persistence/Factories/TaskFactory.cs | 4 +- .../Persistence/Factories/TaskTypeFactory.cs | 3 +- .../Persistence/Factories/TemplateFactory.cs | 3 +- .../Persistence/Factories/UserFactory.cs | 3 +- .../Persistence/Mappers/ContentMapper.cs | 15 +- .../Initial/DatabaseSchemaCreation.cs | 3 +- .../TargetVersionEight/VariantsMigration.cs | 21 +- .../Persistence/NPocoSqlExtensions.cs | 97 ++- .../Querying/ExpressionVisitorBase.cs | 113 +--- .../Querying/PocoToSqlExpressionVisitor.cs | 37 +- .../Persistence/Querying/Query.cs | 15 +- .../Repositories/AuditRepository.cs | 11 +- .../Repositories/ContentRepository.cs | 287 +++----- .../Repositories/ContentTypeRepository.cs | 8 +- .../Repositories/ContentTypeRepositoryBase.cs | 23 +- .../DataTypeDefinitionRepository.cs | 15 +- .../Repositories/DictionaryRepository.cs | 9 +- .../Repositories/DomainRepository.cs | 7 +- .../Repositories/EntityContainerRepository.cs | 3 +- .../Repositories/ExternalLoginRepository.cs | 10 +- .../Interfaces/IContentRepository.cs | 14 - .../Interfaces/IRepositoryVersionable.cs | 13 +- .../Repositories/LanguageRepository.cs | 6 +- .../Repositories/MacroRepository.cs | 14 +- .../Repositories/MediaRepository.cs | 138 ++-- .../Repositories/MediaTypeRepository.cs | 6 +- .../Repositories/MemberGroupRepository.cs | 16 +- .../Repositories/MemberRepository.cs | 127 ++-- .../Repositories/MemberTypeRepository.cs | 8 +- .../Repositories/MigrationEntryRepository.cs | 7 +- .../Repositories/NPocoRepositoryBase.cs | 4 +- .../Repositories/PartialViewRepository.cs | 3 +- .../Repositories/PublicAccessRepository.cs | 6 +- .../Repositories/RecycleBinRepository.cs | 5 +- .../Repositories/RedirectUrlRepository.cs | 4 +- .../Repositories/RelationRepository.cs | 9 +- .../Repositories/RelationTypeRepository.cs | 9 +- .../Repositories/ScriptRepository.cs | 3 +- .../ServerRegistrationRepository.cs | 4 +- .../Repositories/SimpleGetRepository.cs | 3 +- .../Repositories/StylesheetRepository.cs | 3 +- .../Persistence/Repositories/TagRepository.cs | 12 +- .../Repositories/TaskRepository.cs | 4 +- .../Repositories/TaskTypeRepository.cs | 6 +- .../Repositories/TemplateRepository.cs | 25 +- .../Repositories/UserGroupRepository.cs | 10 +- .../Repositories/UserRepository.cs | 14 +- .../Repositories/VersionableRepositoryBase.cs | 184 ++--- .../Repositories/XsltFileRepository.cs | 3 +- src/Umbraco.Core/Persistence/SqlTemplate.cs | 52 +- src/Umbraco.Core/Services/ContentService.cs | 35 +- src/Umbraco.Core/StringExtensions.cs | 13 +- .../SqlTemplatesBenchmark.cs | 2 +- .../Models/ContentExtensionsTests.cs | 467 +------------ src/Umbraco.Tests/Models/ContentTests.cs | 38 +- src/Umbraco.Tests/Models/ContentXmlTest.cs | 1 + .../Persistence/Mappers/ContentMapperTest.cs | 4 +- .../Mappers/DataTypeDefinitionMapperTest.cs | 2 +- .../Persistence/Mappers/MediaMapperTest.cs | 4 +- .../NPocoTests/NPocoSqlTemplateTests.cs | 31 +- .../Persistence/NPocoTests/NPocoSqlTests.cs | 6 +- .../ContentRepositorySqlClausesTest.cs | 162 ----- .../Persistence/Querying/ExpressionTests.cs | 4 +- .../Querying/MediaRepositorySqlClausesTest.cs | 8 +- .../Persistence/Querying/QueryBuilderTests.cs | 30 +- .../Repositories/ContentRepositoryTest.cs | 636 ++++++++---------- .../Repositories/ContentTypeRepositoryTest.cs | 2 +- .../DataTypeDefinitionRepositoryTest.cs | 2 +- .../Repositories/MediaRepositoryTest.cs | 80 +-- .../Repositories/MediaTypeRepositoryTest.cs | 2 +- .../Repositories/MemberRepositoryTest.cs | 22 +- .../Repositories/MemberTypeRepositoryTest.cs | 2 +- .../RedirectUrlRepositoryTests.cs | 1 + .../Repositories/RelationRepositoryTest.cs | 9 +- .../Repositories/TagRepositoryTest.cs | 19 + .../Repositories/TemplateRepositoryTest.cs | 3 +- .../Publishing/PublishingStrategyTests.cs | 1 + .../Services/ContentTypeServiceTests.cs | 13 + .../Services/TestWithSomeContentBase.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 105 +-- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - .../XmlPublishedCache/XmlStore.cs | 8 +- 121 files changed, 1459 insertions(+), 2400 deletions(-) delete mode 100644 src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs diff --git a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs index d42d934c78..0c6fd1127f 100644 --- a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs @@ -141,8 +141,7 @@ namespace Umbraco.Core.Cache input = cloneable.DeepClone(); } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) var tracksChanges = input as IRememberBeingDirty; if (tracksChanges != null) { diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index b16270a867..3699b1b082 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -15,11 +15,10 @@ namespace Umbraco.Core.Models private IContentType _contentType; private ITemplate _template; private bool _published; - private bool? _publishedOriginal; + private PublishedState _publishedState; private string _language; private DateTime? _releaseDate; private DateTime? _expireDate; - private int _writer; private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name. private static readonly Lazy Ps = new Lazy(); @@ -45,7 +44,7 @@ namespace Umbraco.Core.Models : base(name, parent, contentType, properties) { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); - PublishedState = PublishedState.Unpublished; + _publishedState = PublishedState.Unpublished; } /// @@ -69,7 +68,7 @@ namespace Umbraco.Core.Models : base(name, parentId, contentType, properties) { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); - PublishedState = PublishedState.Unpublished; + _publishedState = PublishedState.Unpublished; } // ReSharper disable once ClassNeverInstantiated.Local @@ -80,7 +79,6 @@ namespace Umbraco.Core.Models public readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language); public readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ReleaseDate); public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate); - public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); public readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeName); } @@ -124,26 +122,40 @@ namespace Umbraco.Core.Models } /// - /// Boolean indicating whether this Content is Published or not + /// Gets or sets a value indicating whether this content item is published or not. /// - /// - /// Setting Published to true/false should be private or internal and should ONLY be used for wiring up the value - /// from the db or modifying it based on changing the published state. - /// [DataMember] public bool Published { get => _published; + + // the setter is internal and should only be invoked from + // - the ContentFactory when creating a content entity from a dto + // - the ContentRepository when updating a content entity internal set { SetPropertyValueAndDetectChanges(value, ref _published, Ps.Value.PublishedSelector); - _publishedOriginal = _publishedOriginal ?? _published; - PublishedState = _published ? PublishedState.Published : PublishedState.Unpublished; + _publishedState = _published ? PublishedState.Published : PublishedState.Unpublished; } } - [IgnoreDataMember] - public bool PublishedOriginal => _publishedOriginal ?? false; + /// + /// Gets the published state of the content item. + /// + /// The state should be Published or Unpublished, depending on whether Published + /// is true or false, but can also temporarily be Publishing or Unpublishing when the + /// content item is about to be saved. + [DataMember] + internal PublishedState PublishedState + { + get => _publishedState; + set + { + if (value != PublishedState.Publishing && value != PublishedState.Unpublishing) + throw new ArgumentException("Invalid state, only Publishing and Unpublishing are accepted."); + _publishedState = value; + } + } /// /// Language of the data contained within this Content object. @@ -176,16 +188,6 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _expireDate, Ps.Value.ExpireDateSelector); } - /// - /// Id of the user who wrote/updated this Content - /// - [DataMember] - public virtual int WriterId - { - get => _writer; - set => SetPropertyValueAndDetectChanges(value, ref _writer, Ps.Value.WriterSelector); - } - /// /// Name of the Node (non-localized). /// @@ -240,20 +242,6 @@ namespace Umbraco.Core.Models ChangeContentType(contentType); } - /// - /// Changes the Published state of the content object - /// - public void ChangePublishedState(PublishedState state) - { - if (state == PublishedState.Published || state == PublishedState.Unpublished) - throw new ArgumentException("Invalid state."); - Published = state == PublishedState.Publishing; - PublishedState = state; - } - - [DataMember] - internal PublishedState PublishedState { get; private set; } - /// /// Gets or sets the unique identifier of the published version, if any. /// @@ -263,7 +251,7 @@ namespace Umbraco.Core.Models /// /// Gets a value indicating whether the content has a published version. /// - public bool HasPublishedVersion => PublishedVersionGuid != default(Guid); + public bool HasPublishedVersion => PublishedVersionGuid != default; [IgnoreDataMember] internal DateTime PublishedDate { get; set; } @@ -276,18 +264,7 @@ namespace Umbraco.Core.Models base.ResetDirtyProperties(rememberDirty); // take care of the published state - switch (PublishedState) - { - case PublishedState.Saving: - case PublishedState.Unpublishing: - PublishedState = PublishedState.Unpublished; - break; - case PublishedState.Publishing: - PublishedState = PublishedState.Published; - break; - } - - _publishedOriginal = _published; + _publishedState = _published ? PublishedState.Published : PublishedState.Unpublished; } /// diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 531c358e22..7b14a9c4e1 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -32,6 +32,7 @@ namespace Umbraco.Core.Models private bool _trashed; private int _creatorId; + private int _writerId; // fixme need to deal with localized names, how? // for the time being, this is the node text = unique @@ -90,6 +91,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); public readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId); public readonly PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); + public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); } protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -190,6 +192,16 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _creatorId, Ps.Value.CreatorIdSelector); } + /// + /// Id of the user who wrote/updated this entity + /// + [DataMember] + public virtual int WriterId + { + get => _writerId; + set => SetPropertyValueAndDetectChanges(value, ref _writerId, Ps.Value.WriterSelector); + } + /// /// Gets or sets the identifier of the version. /// @@ -243,7 +255,7 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public IEnumerable PropertyTypes => ContentTypeBase.CompositionPropertyTypes; - #region Has, Get, Set Property Value + #region Has, Get, Set, Publish Property Value /// /// Gets a value indicating whether the content entity has a property with the supplied alias. @@ -312,6 +324,42 @@ namespace Umbraco.Core.Models return convertAttempt.Success ? convertAttempt.Result : default; } + /// + /// Publish the neutral value. + /// + internal virtual void PublishValues() + { + foreach (var property in Properties) + property.PublishValues(); + } + + /// + /// Publish the culture value. + /// + internal virtual void PublishValues(int? nLanguageId) + { + foreach (var property in Properties) + property.PublishValues(nLanguageId); + } + + /// + /// Publish the segment value. + /// + internal virtual void PublishValues(int? nLanguageId, string segment) + { + foreach (var property in Properties) + property.PublishValues(nLanguageId, segment); + } + + /// + /// Publish all values. + /// + internal virtual void PublishAllValues() + { + foreach (var property in Properties) + property.PublishAllValues(); + } + /// /// Sets the neutral (draft) value of a property. /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 9dcabec098..123ba0ce1f 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -37,187 +37,6 @@ namespace Umbraco.Core.Models return dirty.WasPropertyDirty("Published") && entity.Published; } - /// - /// Determines whether the content should be persisted. - /// - /// The content. - /// True is the content should be persisted, otherwise false. - /// See remarks in overload. - internal static bool RequiresSaving(this IContent entity) - { - return RequiresSaving(entity, ((Content) entity).PublishedState); - } - - /// - /// Determines whether the content should be persisted. - /// - /// The content. - /// The published state of the content. - /// True is the content should be persisted, otherwise false. - /// - /// This is called by the repository when persisting an existing content, to - /// figure out whether it needs to persist the content at all. - /// - internal static bool RequiresSaving(this IContent entity, PublishedState publishedState) - { - // note: publishedState is always the entity's PublishedState except for tests - - var content = (Content) entity; - var userPropertyChanged = content.IsAnyUserPropertyDirty(); - var dirtyProps = content.GetDirtyProperties(); - //var contentPropertyChanged = content.IsEntityDirty(); - var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published"); - - // we don't want to save (write to DB) if we are "saving" either a published content - // (.Saving) or an unpublished content (.Unpublished) and strictly nothing has changed - - var noSave = (publishedState == PublishedState.Saving || publishedState == PublishedState.Unpublished) - && userPropertyChanged == false - && contentPropertyChangedExceptPublished == false; - - return noSave == false; - } - - /// - /// Determines whether a new version of the content should be created. - /// - /// The content. - /// True if a new version should be created, otherwise false. - /// See remarks in overload. - internal static bool RequiresNewVersion(this IContent entity) - { - return RequiresNewVersion(entity, ((Content) entity).PublishedState); - } - - /// - /// Determines whether a new version of the content should be created. - /// - /// The content. - /// The published state of the content. - /// True if a new version should be created, otherwise false. - /// - /// This is called by the repository when persisting an existing content, to - /// figure out whether it needs to create a new version for that content. - /// A new version needs to be created when: - /// * The publish status is changed - /// * The language is changed - /// * A content property is changed (? why ?) - /// * The item is already published and is being published again and any property value is changed (to enable a rollback) - /// - internal static bool RequiresNewVersion(this IContent entity, PublishedState publishedState) - { - // note: publishedState is always the entity's PublishedState except for tests - - // read - // http://issues.umbraco.org/issue/U4-2589 (save & publish & creating new versions) - // http://issues.umbraco.org/issue/U4-3404 (pressing preview does save then preview) - // http://issues.umbraco.org/issue/U4-5510 (previewing & creating new versions) - // - // slightly modifying the rules to make more sense (marked with CHANGE) - // but should respect the result of the discussions in those issues - - // figure out whether .Language has changed - // this language stuff was an old POC and should be removed - var hasLanguageChanged = entity.IsPropertyDirty("Language"); - if (hasLanguageChanged) - return true; // language change => new version - - var content = (Content) entity; - //var contentPropertyChanged = content2.IsEntityDirty(); - var userPropertyChanged = content.IsAnyUserPropertyDirty(); - var dirtyProps = content.GetDirtyProperties(); - var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published"); - var wasPublished = content.PublishedOriginal; - - switch (publishedState) - { - case PublishedState.Publishing: - // changed state, publishing either a published or an unpublished version: - // DO create a new (published) version IF it was published already AND - // anything has changed, else can reuse the current version - return (contentPropertyChangedExceptPublished || userPropertyChanged) && wasPublished; - - case PublishedState.Unpublishing: - // changed state, unpublishing a published version: - // DO create a new (draft) version and preserve the (formerly) published - // version for rollback purposes IF the version that's being saved is the - // published version, else it's a draft that we can reuse - return wasPublished; - - case PublishedState.Saving: - // changed state, saving a published version: - // DO create a new (draft) version and preserve the published version IF - // anything has changed, else do NOT create a new version (pointless) - return contentPropertyChangedExceptPublished || userPropertyChanged; - - case PublishedState.Published: - // unchanged state, saving a published version: - // (can happen eg when moving content, never otherwise) - // do NOT create a new version as we're just saving after operations (eg - // move) that cannot be rolled back anyway - ensure that's really it - if (userPropertyChanged) - throw new InvalidOperationException("Invalid PublishedState \"Published\" with user property changes."); - return false; - - case PublishedState.Unpublished: - // unchanged state, saving an unpublished version: - // do NOT create a new version for user property changes, - // BUT create a new version in case of content property changes, for - // rollback purposes - return contentPropertyChangedExceptPublished; - - default: - throw new NotSupportedException(); - } - } - - /// - /// Determines whether the database published flag should be cleared for versions - /// other than this content version. - /// - /// The content. - /// True if the published flag should be cleared, otherwise false. - /// See remarks in overload. - internal static bool RequiresClearPublishedFlag(this IContent entity) - { - var publishedState = ((Content) entity).PublishedState; - var requiresNewVersion = entity.RequiresNewVersion(publishedState); - return entity.RequiresClearPublishedFlag(publishedState, requiresNewVersion); - } - - /// - /// Determines whether the database published flag should be cleared for versions - /// other than this content version. - /// - /// The content. - /// The published state of the content. - /// Indicates whether the content is a new version. - /// True if the published flag should be cleared, otherwise false. - /// - /// This is called by the repository when persisting an existing content, to - /// figure out whether it needs to clear the published flag for other versions. - /// - internal static bool RequiresClearPublishedFlag(this IContent entity, PublishedState publishedState, bool isNewVersion) - { - // note: publishedState is always the entity's PublishedState except for tests - - // new, published version => everything else must be cleared - if (isNewVersion && entity.Published) - return true; - - // if that entity was published then that entity has the flag and - // it does not need to be cleared for other versions - // NOT TRUE when unpublishing we create a NEW version - //var wasPublished = ((Content)entity).PublishedOriginal; - //if (wasPublished) - // return false; - - // clear whenever we are publishing or unpublishing - // publishing: because there might be a previously published version, which needs to be cleared - // unpublishing: same - we might be a saved version, not the published one, which needs to be cleared - return publishedState == PublishedState.Publishing || publishedState == PublishedState.Unpublishing; - } - /// /// Returns a list of the current contents ancestors, not including the content itself. /// diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index d764314907..f3970cec17 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -43,7 +43,7 @@ namespace Umbraco.Core.Models.EntityBase set { SetPropertyValueAndDetectChanges(value, ref _id, Ps.Value.IdSelector); - HasIdentity = true; //set the has Identity + HasIdentity = value != 0; //set the has Identity } } diff --git a/src/Umbraco.Core/Models/EntityBase/IEntity.cs b/src/Umbraco.Core/Models/EntityBase/IEntity.cs index 607b841a9e..d871719826 100644 --- a/src/Umbraco.Core/Models/EntityBase/IEntity.cs +++ b/src/Umbraco.Core/Models/EntityBase/IEntity.cs @@ -4,41 +4,36 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models.EntityBase { /// - /// Defines an Entity. - /// Entities should always have an Id, Created and Modified date + /// Defines an entity. /// - /// The current database schema doesn't provide a modified date - /// for all entities, so this will have to be changed at a later stage. public interface IEntity : IDeepCloneable { /// - /// The Id of the entity + /// The integer identifier of the entity. /// [DataMember] int Id { get; set; } /// - /// Guid based Id + /// The Guid unique identifier of the entity. /// - /// The key is currectly used to store the Unique Id from the - /// umbracoNode table, which many of the entities are based on. [DataMember] Guid Key { get; set; } /// - /// Gets or sets the Created Date + /// Gets or sets the creation date. /// [DataMember] DateTime CreateDate { get; set; } /// - /// Gets or sets the Modified Date + /// Gets or sets the last update date. /// [DataMember] DateTime UpdateDate { get; set; } /// - /// Indicates whether the current entity has an identity, eg. Id. + /// Gets a value indicating whether the entity has an identity. /// [IgnoreDataMember] bool HasIdentity { get; } diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index fd8b9dbb97..7ff68da8d0 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -33,11 +33,6 @@ namespace Umbraco.Core.Models /// DateTime? ExpireDate { get; set; } - /// - /// Id of the user who wrote/updated the Content - /// - int WriterId { get; set; } - /// /// Gets the ContentType used by this content object /// @@ -63,11 +58,6 @@ namespace Umbraco.Core.Models /// Boolean indicating whether to clear PropertyTypes upon change void ChangeContentType(IContentType contentType, bool clearProperties); - /// - /// Changes the Published state of the content object - /// - void ChangePublishedState(PublishedState state); - /// /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset /// diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index d60764a8f5..406b7e9671 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -16,10 +16,15 @@ namespace Umbraco.Core.Models int ContentTypeId { get; } /// - /// Gets the Guid Id of the Content's Version + /// Gets the Guid identifier of the content's version. /// Guid Version { get; } + /// + /// Gets the identifier of the writer. + /// + int WriterId { get; set; } + /// /// 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 195aff88bd..c3d1a3d06c 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -45,6 +45,9 @@ namespace Umbraco.Core.Models public string Segment { get; set; } public object PublishedValue { get; set; } public object DraftValue { get; set; } + + public PropertyValue Clone() + => new PropertyValue { LanguageId = LanguageId, Segment = Segment, PublishedValue = PublishedValue, DraftValue = DraftValue }; } // ReSharper disable once ClassNeverInstantiated.Local @@ -57,27 +60,18 @@ namespace Umbraco.Core.Models { if (o == null && o1 == null) return true; - //custom comparer for strings. + // custom comparer for strings. + // if one is null and another is empty then they are the same if (o is string || o1 is string) - { - //if one is null and another is empty then they are the same - if ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace()) - { - return true; - } - if (o == null || o1 == null) return false; - return o.Equals(o1); - } + return ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace()) || (o != null && o1 != null && o.Equals(o1)); if (o == null || o1 == null) return false; - //Custom comparer for enumerable if it is enumerable - var enum1 = o as IEnumerable; - var enum2 = o1 as IEnumerable; - if (enum1 != null && enum2 != null) - { - return enum1.Cast().UnsortedSequenceEqual(enum2.Cast()); - } + // custom comparer for enumerable + // ReSharper disable once MergeCastWithTypeCheck + if (o is IEnumerable && o1 is IEnumerable) + return ((IEnumerable) o).Cast().UnsortedSequenceEqual(((IEnumerable) o1).Cast()); + return o.Equals(o1); }, o => o.GetHashCode()); } @@ -99,6 +93,8 @@ namespace Umbraco.Core.Models { _values = value; + _pvalue = value.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null); + _lvalues = value.Where(x => x.LanguageId.HasValue && x.Segment == null) .ToDictionary(x => x.LanguageId.Value, x => x); @@ -164,20 +160,68 @@ namespace Umbraco.Core.Models return published ? value.PublishedValue : value.DraftValue; } + internal void PublishValues() + { + (var pvalue, _) = GetPropertyValue(false); + if (pvalue == null) return; + PublishPropertyValue(pvalue); + } + + internal void PublishValues(int? nLanguageId) + { + if (nLanguageId == null) + { + PublishValues(); + return; + } + + var languageId = nLanguageId.Value; + + (var pvalue, _) = GetPropertyValue(languageId, false); + if (pvalue == null) return; + PublishPropertyValue(pvalue); + } + + internal void PublishValues(int? nLanguageId, string segment) + { + if (segment == null) + { + PublishValues(nLanguageId); + return; + } + + if (!nLanguageId.HasValue) + throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId)); + + var languageId = nLanguageId.Value; + + (var pvalue, _) = GetPropertyValue(languageId, segment, false); + if (pvalue == null) return; + PublishPropertyValue(pvalue); + } + + internal void PublishAllValues() + { + foreach (var pvalue in Values) + PublishPropertyValue(pvalue); + } + + private void PublishPropertyValue(PropertyValue pvalue) + { + var origValue = pvalue.PublishedValue; + pvalue.PublishedValue = ConvertSetValue(pvalue.DraftValue); + DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, false); + } + /// /// Sets a (draft) neutral value. /// public void SetValue(object value) { - var change = false; - if (_pvalue == null) - { - _pvalue = new PropertyValue(); - _values.Add(_pvalue); - change = true; - } - var origValue = _pvalue.DraftValue; - _pvalue.DraftValue = ConvertSetValue(value); + (var pvalue, var change) = GetPropertyValue(true); + + var origValue = pvalue.DraftValue; + pvalue.DraftValue = ConvertSetValue(value); DetectChanges(_pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change); } @@ -195,18 +239,8 @@ namespace Umbraco.Core.Models var languageId = nLanguageId.Value; - var change = false; - if (_lvalues == null) - { - _lvalues = new Dictionary(); - change = true; - } - if (!_lvalues.TryGetValue(languageId, out var pvalue)) - { - pvalue = _lvalues[languageId] = new PropertyValue(); - _values.Add(pvalue); - change = true; - } + (var pvalue, var change) = GetPropertyValue(languageId, true); + var origValue = pvalue.DraftValue; pvalue.DraftValue = ConvertSetValue(value); @@ -226,30 +260,71 @@ namespace Umbraco.Core.Models if (!nLanguageId.HasValue) throw new ArgumentException("Cannot be null when segment is not null.", nameof(nLanguageId)); - var languageId = nLanguageId.Value; + (var pvalue, var change) = GetPropertyValue(languageId, segment, true); + + var origValue = pvalue.DraftValue; + pvalue.DraftValue = ConvertSetValue(value); + + DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change); + } + + private (PropertyValue, bool) GetPropertyValue(bool create) + { + var change = false; + if (_pvalue == null) + { + if (!create) return (null, false); + _pvalue = new PropertyValue(); + _values.Add(_pvalue); + change = true; + } + return (_pvalue, change); + } + + private (PropertyValue, bool) GetPropertyValue(int languageId, bool create) + { + var change = false; + if (_lvalues == null) + { + if (!create) return (null, false); + _lvalues = new Dictionary(); + change = true; + } + if (!_lvalues.TryGetValue(languageId, out var pvalue)) + { + if (!create) return (null, false); + pvalue = _lvalues[languageId] = new PropertyValue(); + _values.Add(pvalue); + change = true; + } + return (pvalue, change); + } + + private (PropertyValue, bool) GetPropertyValue(int languageId, string segment, bool create) + { var change = false; if (_svalues == null) { + if (!create) return (null, false); _svalues = new Dictionary>(); change = true; } if (!_svalues.TryGetValue(languageId, out var svalue)) { + if (!create) return (null, false); svalue = _svalues[languageId] = new Dictionary(); change = true; } if (!svalue.TryGetValue(segment, out var pvalue)) { + if (!create) return (null, false); pvalue = svalue[segment] = new PropertyValue(); _values.Add(pvalue); change = true; } - var origValue = pvalue.DraftValue; - pvalue.DraftValue = ConvertSetValue(value); - - DetectChanges(pvalue.DraftValue, origValue, Ps.Value.ValuesSelector, Ps.Value.PropertyValueComparer, change); + return (pvalue, change); } private object ConvertSetValue(object value) @@ -309,7 +384,7 @@ namespace Umbraco.Core.Models /// An invalid value can be saved, but only valid values can be published. public bool IsValid() { - return IsValid(_pvalue.DraftValue); + return IsValid(GetValue()); } /// @@ -335,9 +410,9 @@ namespace Umbraco.Core.Models /// /// /// True is property value is valid, otherwise false - public bool IsValid(object value) + private bool IsValid(object value) { - return _propertyType.IsPropertyValueValid(value); + return _propertyType.IsValidPropertyValue(value); } public override object DeepClone() diff --git a/src/Umbraco.Core/Models/PropertyCollection.cs b/src/Umbraco.Core/Models/PropertyCollection.cs index d9ca9a4813..1bedf57333 100644 --- a/src/Umbraco.Core/Models/PropertyCollection.cs +++ b/src/Umbraco.Core/Models/PropertyCollection.cs @@ -111,7 +111,7 @@ namespace Umbraco.Core.Models property.Id = existing.Id; if (property.Values.Count == 0 && existing.Values.Count > 0) - property.Values = existing.Values; + property.Values = existing.Values.Select(x => x.Clone()).ToList(); // replace existing with property and return, // SetItem invokes OnCollectionChanged (but not OnAdd) diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 5fd594bcdc..42b8ad839b 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -323,7 +323,7 @@ namespace Umbraco.Core.Models /// /// /// True if valid, otherwise false - public bool IsPropertyValueValid(object value) + public bool IsValidPropertyValue(object value) { //If the Property is mandatory and value is null or empty, return false as the validation failed if (Mandatory && (value == null || string.IsNullOrEmpty(value.ToString()))) diff --git a/src/Umbraco.Core/Models/PublishedState.cs b/src/Umbraco.Core/Models/PublishedState.cs index 0ef0ee608c..af9777710d 100644 --- a/src/Umbraco.Core/Models/PublishedState.cs +++ b/src/Umbraco.Core/Models/PublishedState.cs @@ -7,66 +7,54 @@ namespace Umbraco.Core.Models /// public enum PublishedState { + // versions management in repo: + // + // - published = the content is published + // repo: saving draft values + // update current version (draft) values + // + // - unpublished = the content is not published + // repo: saving draft values + // update current version (draft) values + // + // - publishing = the content is being published (transitory) + // if currently published: + // delete all draft values from current version, not current anymore + // create new version with published+draft values + // + // - unpublishing = the content is being unpublished (transitory) + // if currently published (just in case): + // delete all draft values from current version, not current anymore + // create new version with published+draft values (should be managed by service) + // when a content item is loaded, its state is one of those two: /// /// The content item is published. /// Published, + // also: handled over to repo to save draft values for a published content /// /// The content item is not published. /// - /// Also: the version is being saved, in order to register changes - /// made to an unpublished version of the content. Unpublished, + // also: handled over to repo to save draft values for an unpublished content - // when it is saved, its state can also be one of those: - - // fixme - // what we can do is: - // - save changes to unpublished data (update current version) - // - save changes resulting from a publish (spawn a version) - // - save changes resulting from unpublishing (spawn a version) - // - // ContentRepository.CreateVersion - // only if uDocument.Published already, and Publishing - // ie updating what's already published - // creates a new uContentVersion, uDocumentVersion - // by copying everything from the existing (current) ones - // becomes the current ones - // then - // move all non-published uPropertyData to the new version - // create uPropertyData for new published values - // update/return the content w/new version - // - // saving is just updating draft - // publishing is described above - // un-publishing is ... - // also create a version to remember what was published - // and then on the new version, there is no 'new published values' - - /// - /// The version is being saved, in order to register changes made to a published content. - /// - /// The Saving state is transitional. Once the version - /// is saved, its state changes to Unpublished. - Saving, + // when it is handled over to the repository, its state can also be one of those: /// /// The version is being saved, in order to publish the content. /// /// The Publishing state is transitional. Once the version - /// is saved, its state changes to Published. The content is published, - /// and all other versions are unpublished. + /// is saved, its state changes to Published. Publishing, /// /// The version is being saved, in order to unpublish the content. /// /// The Unpublishing state is transitional. Once the version - /// is saved, its state changes to Unpublished. The content and all - /// other versions are unpublished. + /// is saved, its state changes to Unpublished. Unpublishing } diff --git a/src/Umbraco.Core/Models/Rdbms/ContentDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentDto.cs index 99d4bcf0c0..996d3120db 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentDto.cs @@ -1,34 +1,41 @@ -using NPoco; +using System; +using NPoco; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { [TableName(TableName)] - [PrimaryKey("id")] + [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] internal class ContentDto { private const string TableName = Constants.DatabaseSchema.Tables.Content; - [Column("id")] - [PrimaryKeyColumn] - public int Id { get; set; } - [Column("nodeId")] + [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId")] public int NodeId { get; set; } [Column("contentTypeId")] - [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] + [ForeignKey(typeof(ContentTypeDto), Column = "NodeId")] public int ContentTypeId { get; set; } + [Column("writerUserId")] + public int WriterUserId { get; set; } + + [Column("updateDate")] + [Constraint(Default = SystemMethods.CurrentDateTime)] + public DateTime UpdateDate { get; set; } + [ResultColumn] [Reference(ReferenceType.OneToOne, ColumnName = "NodeId")] public NodeDto NodeDto { get; set; } + // although a content has many content versions, + // they can only be loaded one by one (as several content) [ResultColumn] - [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] // FIXME not one-to-one! BUT it depends on the query! + [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] public ContentVersionDto ContentVersionDto { get; set; } } } diff --git a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs index b2f9aa5e46..507d53c63c 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs @@ -17,8 +17,8 @@ namespace Umbraco.Core.Models.Rdbms public int Id { get; set; } [Column("nodeId")] - [ForeignKey(typeof(ContentDto), Column = "nodeId")] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_NodeId")] + [ForeignKey(typeof(ContentDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeIdVersionId", ForColumns = "nodeId, versionId")] public int NodeId { get; set; } [Column("versionId")] diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs index f031fb254c..8ddcebd1ff 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs @@ -1,30 +1,25 @@ using System; using NPoco; using Umbraco.Core.Persistence.DatabaseAnnotations; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { [TableName(TableName)] - [PrimaryKey("versionId", AutoIncrement = false)] + [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] internal class DocumentDto { private const string TableName = Constants.DatabaseSchema.Tables.Document; [Column("nodeId")] - [ForeignKey(typeof(ContentDto), Column = "nodeId")] - [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId, versionId")] + [PrimaryKeyColumn(AutoIncrement = false)] + [ForeignKey(typeof(ContentDto))] public int NodeId { get; set; } [Column("published")] [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Published")] public bool Published { get; set; } - [Column("writerUserId")] - public int WriterUserId { get; set; } - [Column("releaseDate")] [NullSetting(NullSetting = NullSettings.Null)] public DateTime? ReleaseDate { get; set; } @@ -33,16 +28,14 @@ namespace Umbraco.Core.Models.Rdbms [NullSetting(NullSetting = NullSettings.Null)] public DateTime? ExpiresDate { get; set; } - [Column("updateDate")] - [Constraint(Default = SystemMethods.CurrentDateTime)] - public DateTime UpdateDate { get; set; } - [ResultColumn] [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] public ContentDto ContentDto { get; set; } + // although a content has many content versions, + // they can only be loaded one by one (as several content) [ResultColumn] - [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] // FIXME not one-to-one! BUT it depends on the query! + [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] public DocumentVersionDto DocumentVersionDto { get; set; } } } diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentVersionDto.cs index d53064a331..69fb4d0442 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentVersionDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentVersionDto.cs @@ -4,14 +4,14 @@ using Umbraco.Core.Persistence.DatabaseAnnotations; namespace Umbraco.Core.Models.Rdbms { [TableName(TableName)] - [PrimaryKey("id")] + [PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] internal class DocumentVersionDto { private const string TableName = Constants.DatabaseSchema.Tables.DocumentVersion; [Column("id")] - [PrimaryKeyColumn] + [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(ContentVersionDto))] public int Id { get; set; } diff --git a/src/Umbraco.Core/Models/Rdbms/MemberDto.cs b/src/Umbraco.Core/Models/Rdbms/MemberDto.cs index 315c9b68f4..75822abd92 100644 --- a/src/Umbraco.Core/Models/Rdbms/MemberDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/MemberDto.cs @@ -3,15 +3,16 @@ using Umbraco.Core.Persistence.DatabaseAnnotations; namespace Umbraco.Core.Models.Rdbms { - [TableName(Constants.DatabaseSchema.Tables.Member)] + [TableName(TableName)] [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] internal class MemberDto { + private const string TableName = Constants.DatabaseSchema.Tables.Member; + [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false)] - [ForeignKey(typeof(ContentDto), Column = "nodeId")] - [ForeignKey(typeof(NodeDto))] + [ForeignKey(typeof(ContentDto))] public int NodeId { get; set; } [Column("Email")] diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs index b1d4cb3c64..d9b150eeb5 100644 --- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Models.Rdbms public const int NodeIdSeed = 1060; [Column("id")] - [PrimaryKeyColumn(Name = "PK_" + TableName, IdentitySeed = NodeIdSeed)] + [PrimaryKeyColumn(IdentitySeed = NodeIdSeed)] public int NodeId { get; set; } [Column("uniqueId")] diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index c49a8ab62d..b4d921f683 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -15,19 +15,19 @@ namespace Umbraco.Core.Models.Rdbms private decimal? _decimalValue; + // fixme - should we just kill that one? we never update! [Column("id")] [PrimaryKeyColumn] public int Id { get; set; } [Column("nodeId")] [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,versionId,propertyTypeId")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,versionId,propertyTypeId,languageId,segment,published")] public int NodeId { get; set; } [Column("versionId")] - [NullSetting(NullSetting = NullSettings.Null)] [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_VersionId")] - public Guid? VersionId { get; set; } + public Guid VersionId { get; set; } [Column("propertyTypeId")] [ForeignKey(typeof(PropertyTypeDto))] diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs index 10016e39c1..2368cb3e07 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs @@ -26,8 +26,8 @@ namespace Umbraco.Core.Persistence.Factories content.Key = nodeDto.UniqueId; content.Version = contentVersionDto.VersionId; - content.Name = nodeDto.Text; - content.NodeName = nodeDto.Text; + content.Name = contentVersionDto.Text; + content.NodeName = contentVersionDto.Text; content.Path = nodeDto.Path; content.Level = nodeDto.Level; @@ -36,9 +36,9 @@ namespace Umbraco.Core.Persistence.Factories content.Trashed = nodeDto.Trashed; content.CreatorId = nodeDto.UserId ?? 0; - content.WriterId = dto.WriterUserId; + content.WriterId = contentDto.WriterUserId; content.CreateDate = nodeDto.CreateDate; - content.UpdateDate = contentVersionDto.VersionDate; + content.UpdateDate = contentDto.UpdateDate; content.Published = dto.Published; content.ExpireDate = dto.ExpiresDate; @@ -68,10 +68,8 @@ namespace Umbraco.Core.Persistence.Factories { NodeId = entity.Id, Published = entity.Published, - WriterUserId = entity.WriterId, ReleaseDate = entity.ReleaseDate, ExpiresDate = entity.ExpireDate, - UpdateDate = entity.UpdateDate, ContentDto = contentDto, DocumentVersionDto = BuildDocumentVersionDto(entity, contentDto) @@ -84,9 +82,10 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new ContentDto { - // Id = _primaryKey if >0 - fixme - kill that id entirely NodeId = entity.Id, ContentTypeId = entity.ContentTypeId, + WriterUserId = entity.WriterId, + UpdateDate = entity.UpdateDate, NodeDto = BuildNodeDto(entity) }; @@ -108,7 +107,7 @@ namespace Umbraco.Core.Persistence.Factories UserId = entity.CreatorId, Text = entity.Name, NodeObjectType = Constants.ObjectTypes.Document, - CreateDate = entity.CreateDate, + CreateDate = entity.CreateDate }; return dto; @@ -119,7 +118,7 @@ namespace Umbraco.Core.Persistence.Factories var dto = new DocumentVersionDto { //Id =, // fixme - TemplateId = entity.Template?.Id ?? 0, + TemplateId = entity.Template?.Id, ContentVersionDto = BuildContentVersionDto(entity, contentDto) }; diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs index 7bca902672..1a33bb1344 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs @@ -28,8 +28,7 @@ namespace Umbraco.Core.Persistence.Factories BuildCommonEntity(contentType, dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) contentType.ResetDirtyProperties(false); return contentType; } @@ -52,8 +51,7 @@ namespace Umbraco.Core.Persistence.Factories BuildCommonEntity(contentType, dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) contentType.ResetDirtyProperties(false); } finally diff --git a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs index c2ed31cae1..8eefd05033 100644 --- a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs @@ -39,8 +39,7 @@ namespace Umbraco.Core.Persistence.Factories dataTypeDefinition.Trashed = dto.NodeDto.Trashed; dataTypeDefinition.CreatorId = dto.NodeDto.UserId.Value; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) dataTypeDefinition.ResetDirtyProperties(false); return dataTypeDefinition; } diff --git a/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs b/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs index 6284aa3be8..12f2ca25db 100644 --- a/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/DictionaryItemFactory.cs @@ -18,8 +18,8 @@ namespace Umbraco.Core.Persistence.Factories item.Id = dto.PrimaryKey; item.Key = dto.UniqueId; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) item.ResetDirtyProperties(false); return item; } diff --git a/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs b/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs index 49ed6d717f..dc0fff3008 100644 --- a/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/DictionaryTranslationFactory.cs @@ -25,8 +25,7 @@ namespace Umbraco.Core.Persistence.Factories item.Id = dto.PrimaryKey; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) item.ResetDirtyProperties(false); return item; } diff --git a/src/Umbraco.Core/Persistence/Factories/ExternalLoginFactory.cs b/src/Umbraco.Core/Persistence/Factories/ExternalLoginFactory.cs index c256fc66cc..285764b688 100644 --- a/src/Umbraco.Core/Persistence/Factories/ExternalLoginFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ExternalLoginFactory.cs @@ -9,8 +9,7 @@ namespace Umbraco.Core.Persistence.Factories { var entity = new IdentityUserLogin(dto.Id, dto.LoginProvider, dto.ProviderKey, dto.UserId, dto.CreateDate); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs index 57e792fe4a..cd555e558a 100644 --- a/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/LanguageFactory.cs @@ -9,8 +9,7 @@ namespace Umbraco.Core.Persistence.Factories public ILanguage BuildEntity(LanguageDto dto) { var lang = new Language(dto.IsoCode) { CultureName = dto.CultureName, Id = dto.Id }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) lang.ResetDirtyProperties(false); return lang; } diff --git a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs index 5dbec9741b..1cec07c30d 100644 --- a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs @@ -20,8 +20,7 @@ namespace Umbraco.Core.Persistence.Factories model.Properties.Add(new MacroProperty(p.Id, p.UniqueId, p.Alias, p.Name, p.SortOrder, p.EditorAlias)); } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) model.ResetDirtyProperties(false); return model; } diff --git a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs index e84dbf8da2..1a7709d655 100644 --- a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs @@ -33,9 +33,9 @@ namespace Umbraco.Core.Persistence.Factories content.Trashed = nodeDto.Trashed; content.CreatorId = nodeDto.UserId ?? 0; - // fixme missing writerId - which then should move to nodeDto + content.WriterId = dto.WriterUserId; content.CreateDate = nodeDto.CreateDate; - content.UpdateDate = contentVersionDto.VersionDate; + content.UpdateDate = dto.UpdateDate; // reset dirty initial properties (U4-1946) content.ResetDirtyProperties(false); @@ -61,9 +61,10 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new ContentDto { - // Id = _primaryKey if >0 - fixme - kill that id entirely NodeId = entity.Id, ContentTypeId = entity.ContentTypeId, + WriterUserId = entity.WriterId, + UpdateDate = entity.UpdateDate, NodeDto = BuildNodeDto(entity) }; diff --git a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs index 0a16faa325..84faa9d8a7 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberFactory.cs @@ -33,9 +33,9 @@ namespace Umbraco.Core.Persistence.Factories content.Trashed = nodeDto.Trashed; content.CreatorId = nodeDto.UserId ?? 0; - // fixme missing writerId - which then should move to nodeDto + content.WriterId = dto.ContentDto.WriterUserId; content.CreateDate = nodeDto.CreateDate; - content.UpdateDate = contentVersionDto.VersionDate; + content.UpdateDate = dto.ContentDto.UpdateDate; content.ProviderUserKey = content.Key; // fixme explain @@ -73,9 +73,10 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new ContentDto { - // Id = _primaryKey if >0 - fixme - kill that id entirely NodeId = entity.Id, ContentTypeId = entity.ContentTypeId, + WriterUserId = entity.WriterId, + UpdateDate = entity.UpdateDate, NodeDto = BuildNodeDto(entity) }; diff --git a/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs index d921ff57fd..eef2f26498 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberGroupFactory.cs @@ -29,8 +29,7 @@ namespace Umbraco.Core.Persistence.Factories group.Key = dto.UniqueId; group.Name = dto.Text; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) group.ResetDirtyProperties(false); return group; } diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index 9596862401..203b9bc919 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -133,13 +133,13 @@ namespace Umbraco.Core.Persistence.Factories UpdateDate = memberType.UpdateDate, Key = typeDto.UniqueId }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) propertyType.ResetDirtyProperties(false); group.PropertyTypes.Add(propertyType); } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) group.ResetDirtyProperties(false); propertyGroups.Add(group); } diff --git a/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs index 145505f629..daa3484d3b 100644 --- a/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs @@ -16,8 +16,8 @@ namespace Umbraco.Core.Persistence.Factories } var model = new MigrationEntry(dto.Id, dto.CreateDate, dto.Name, parsed); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) model.ResetDirtyProperties(false); return model; } diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index bf000337f4..31f034830a 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -40,9 +40,6 @@ namespace Umbraco.Core.Persistence.Factories { var dto = new PropertyDataDto { NodeId = nodeId, VersionId = versionId, PropertyTypeId = property.PropertyTypeId }; - if (property.HasIdentity) - dto.Id = property.Id; - if (propertyValue.LanguageId.HasValue) dto.LanguageId = propertyValue.LanguageId; diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs index 1d2fa4071b..07f82d4354 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs @@ -79,8 +79,7 @@ namespace Umbraco.Core.Persistence.Factories propertyType.CreateDate = _createDate; propertyType.UpdateDate = _updateDate; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) propertyType.ResetDirtyProperties(false); group.PropertyTypes.Add(propertyType); } @@ -89,8 +88,8 @@ namespace Umbraco.Core.Persistence.Factories propertyType.EnableChangeTracking(); } } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) group.ResetDirtyProperties(false); propertyGroups.Add(group); } diff --git a/src/Umbraco.Core/Persistence/Factories/PublicAccessEntryFactory.cs b/src/Umbraco.Core/Persistence/Factories/PublicAccessEntryFactory.cs index 73c4fdaf78..b751035dd9 100644 --- a/src/Umbraco.Core/Persistence/Factories/PublicAccessEntryFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PublicAccessEntryFactory.cs @@ -22,8 +22,7 @@ namespace Umbraco.Core.Persistence.Factories UpdateDate = dto.UpdateDate }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs index 1ebba27150..0186cd66db 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs @@ -27,8 +27,7 @@ namespace Umbraco.Core.Persistence.Factories entity.Id = dto.Id; entity.UpdateDate = dto.Datetime; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs index 86f06f45e0..e79b1310ed 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs @@ -20,8 +20,7 @@ namespace Umbraco.Core.Persistence.Factories entity.IsBidirectional = dto.Dual; entity.Name = dto.Name; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs index 20236e8f8c..8a2c59dd06 100644 --- a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs @@ -8,8 +8,7 @@ namespace Umbraco.Core.Persistence.Factories public ServerRegistration BuildEntity(ServerRegistrationDto dto) { var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive, dto.IsMaster); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) model.ResetDirtyProperties(false); return model; } diff --git a/src/Umbraco.Core/Persistence/Factories/TagFactory.cs b/src/Umbraco.Core/Persistence/Factories/TagFactory.cs index 5193bd58fb..f7956a7673 100644 --- a/src/Umbraco.Core/Persistence/Factories/TagFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TagFactory.cs @@ -8,8 +8,7 @@ namespace Umbraco.Core.Persistence.Factories public ITag BuildEntity(TagDto dto) { var model = new Tag(dto.Id, dto.Tag, dto.Group, dto.NodeCount); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) model.ResetDirtyProperties(false); return model; } diff --git a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs b/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs index 0b3ee24951..96c0cfa626 100644 --- a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs @@ -23,8 +23,8 @@ namespace Umbraco.Core.Persistence.Factories entity.EntityId = dto.NodeId; entity.Id = dto.Id; entity.OwnerUserId = dto.ParentUserId; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs index c092390c28..0a75b66e98 100644 --- a/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TaskTypeFactory.cs @@ -8,8 +8,7 @@ namespace Umbraco.Core.Persistence.Factories public TaskType BuildEntity(TaskTypeDto dto) { var entity = new TaskType(dto.Alias) {Id = dto.Id}; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; } diff --git a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs index 34c51ff078..8cf6963a5e 100644 --- a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs @@ -51,8 +51,7 @@ namespace Umbraco.Core.Persistence.Factories if (dto.NodeDto.ParentId > 0) template.MasterTemplateId = new Lazy(() => dto.NodeDto.ParentId); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) template.ResetDirtyProperties(false); return template; } diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs index a3d687d6d5..b0aca4268e 100644 --- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs @@ -37,8 +37,7 @@ namespace Umbraco.Core.Persistence.Factories user.EmailConfirmedDate = dto.EmailConfirmedDate; user.InvitedDate = dto.InvitedDate; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) user.ResetDirtyProperties(false); return user; diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs index 55257517cd..ca273fff68 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ContentMapper.cs @@ -21,22 +21,27 @@ namespace Umbraco.Core.Persistence.Mappers if (PropertyInfoCache.IsEmpty == false) return; CacheMap(src => src.Id, dto => dto.NodeId); - CacheMap(src => src.CreateDate, dto => dto.CreateDate); - CacheMap(src => src.Level, dto => dto.Level); + CacheMap(src => src.Key, dto => dto.UniqueId); + + CacheMap(src => src.NodeName, dto => dto.Text); + CacheMap(src => src.Name, dto => dto.Text); + CacheMap(src => src.ParentId, dto => dto.ParentId); + CacheMap(src => src.Level, dto => dto.Level); CacheMap(src => src.Path, dto => dto.Path); CacheMap(src => src.SortOrder, dto => dto.SortOrder); - CacheMap(src => src.NodeName, dto => dto.Text); CacheMap(src => src.Trashed, dto => dto.Trashed); - CacheMap(src => src.Key, dto => dto.UniqueId); + + CacheMap(src => src.CreateDate, dto => dto.CreateDate); 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); - CacheMap(src => src.Name, dto => dto.ContentDto.NodeDto.Text); CacheMap(src => src.ExpireDate, dto => dto.ExpiresDate); CacheMap(src => src.ReleaseDate, dto => dto.ReleaseDate); CacheMap(src => src.Published, dto => dto.Published); + //CacheMap(src => src.Name, dto => dto.Alias); //CacheMap(src => src, dto => dto.Newest); //CacheMap(src => src.Template, dto => dto.TemplateId); diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index 61d7f6d7a8..931200d29f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -97,7 +97,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial {52, typeof (UserGroup2NodePermissionDto) }, {53, typeof (UserGroup2AppDto) }, {54, typeof (UserStartNodeDto) }, - {55, typeof (ContentNuDto) } + {55, typeof (ContentNuDto) }, + {56, typeof (DocumentVersionDto) } }; #endregion diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs index 1853ff7eff..6b5ff7aa19 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionEight/VariantsMigration.cs @@ -83,8 +83,22 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionEight // rename columns if (ColumnExists(PreTables.Content, "contentType")) ReplaceColumn(PreTables.Content, "contentType", "contentTypeId"); + + // add columns + if (!ColumnExists(PreTables.Content, "writerUserId")) + AddColumn(PreTables.Content, "writerUserId"); + if (!ColumnExists(PreTables.Content, "updateDate")) + AddColumn(PreTables.Content, "updateDate"); + + // copy data for added columns + Execute.Sql($@"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Content)} +SET writerUserId=doc.documentUser, updateDate=doc.updateDate, +FROM {SqlSyntax.GetQuotedTableName(PreTables.Content)} con +JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON con.nodeId=doc.nodeId AND doc.newest=1"); + + // drop columns if (ColumnExists(PreTables.Content, "pk")) - ReplaceColumn(PreTables.Content, "pk", "id"); + Delete.Column("pk").FromTable(PreTables.Content); // rename table Rename.Table(PreTables.Content).To(Constants.DatabaseSchema.Tables.Content); @@ -143,9 +157,8 @@ JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON doc.versionId=cve // drop some columns Delete.Column("text").FromTable(PreTables.Document); // fixme usage Delete.Column("templateId").FromTable(PreTables.Document); // fixme usage - - // replace some columns - ReplaceColumn(PreTables.Document, "documentUser", "writerUserId"); + Delete.Column("documentUser").FromTable(PreTables.Document); // fixme usage + Delete.Column("updateDate").FromTable(PreTables.Document); // fixme usage // update PropertyData.Published for published versions if (Context.SqlContext.DatabaseType.IsMySql()) diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index c5f254657e..54e70aca5f 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -26,9 +26,9 @@ namespace Umbraco.Core.Persistence /// The Sql statement. /// A predicate to transform and append to the Sql statement. /// The Sql statement. - public static Sql Where(this Sql sql, Expression> predicate) + public static Sql Where(this Sql sql, Expression> predicate, string alias = null) { - var expresionist = new PocoToSqlExpressionVisitor(sql.SqlContext); + var expresionist = new PocoToSqlExpressionVisitor(sql.SqlContext, alias); var whereExpression = expresionist.Visit(predicate); sql.Where(whereExpression, expresionist.GetSqlParameters()); return sql; @@ -148,13 +148,18 @@ namespace Umbraco.Core.Persistence /// /// The type of the Dto. /// The Sql statement. + /// An optional table alias /// The Sql statement. - public static Sql From(this Sql sql) + public static Sql From(this Sql sql, string alias = null) { var type = typeof (TDto); var tableName = type.GetTableName(); - sql.From(sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName)); + var from = sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName); + if (!string.IsNullOrWhiteSpace(alias)) + from += " " + sql.SqlContext.SqlSyntax.GetQuotedTableName(alias); + sql.From(from); + return sql; } @@ -394,12 +399,12 @@ namespace Umbraco.Core.Persistence /// The type of Dto 2. /// The SqlJoin statement. /// A predicate to transform and use as the ON clause body. - /// An optional alias for Dto 1 table. - /// An optional alias for Dto 2 table. + /// An optional alias for Dto 1 table. + /// An optional alias for Dto 2 table. /// The Sql statement. - public static Sql On(this Sql.SqlJoinClause sqlJoin, Expression> predicate, string alias1 = null, string alias2 = null) + public static Sql On(this Sql.SqlJoinClause sqlJoin, Expression> predicate, string aliasLeft = null, string aliasRight = null) { - var expresionist = new PocoToSqlExpressionVisitor(sqlJoin.SqlContext, alias1, alias2); + var expresionist = new PocoToSqlExpressionVisitor(sqlJoin.SqlContext, aliasLeft, aliasRight); var onExpression = expresionist.Visit(predicate); return sqlJoin.On(onExpression, expresionist.GetSqlParameters()); } @@ -634,6 +639,82 @@ namespace Umbraco.Core.Persistence #endregion + #region Delete + + public static Sql Delete(this Sql sql) + { + sql.Append("DELETE"); + return sql; + } + + public static Sql Delete(this Sql sql) + { + var type = typeof(TDto); + var tableName = type.GetTableName(); + + sql.Append($"DELETE {sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName)}"); + return sql; + } + + #endregion + + #region Update + + public static Sql Update(this Sql sql) + { + sql.Append("UPDATE"); + return sql; + } + + public static Sql Update(this Sql sql) + { + var type = typeof(TDto); + var tableName = type.GetTableName(); + + sql.Append($"UPDATE {sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName)}"); + return sql; + } + + public static Sql Update(this Sql sql, Func, SqlUpd> updates) + { + var type = typeof(TDto); + var tableName = type.GetTableName(); + + sql.Append($"UPDATE {sql.SqlContext.SqlSyntax.GetQuotedTableName(tableName)} SET"); + + var u = new SqlUpd(sql.SqlContext); + u = updates(u); + for (var i = 0; i < u.SetExpressions.Count; i++) + { + var setExpression = u.SetExpressions[i]; + sql.Append(setExpression.Item1 + "=@0" + (i < u.SetExpressions.Count - 1 ? "," : ""), setExpression.Item2); + } + + return sql; + } + + public class SqlUpd + { + private readonly ISqlContext _sqlContext; + private readonly List> _setExpressions = new List>(); + + public SqlUpd(ISqlContext sqlContext) + { + _sqlContext = sqlContext; + } + + public SqlUpd Set(Expression> fieldSelector, object value) + { + var fieldName = GetFieldName(fieldSelector, _sqlContext.SqlSyntax); + _setExpressions.Add(new Tuple(fieldName, value)); + return this; + } + + public List> SetExpressions => _setExpressions; + } + + #endregion + #region Utilities private static object[] GetColumns(this Sql sql, string tableAlias = null, string referenceName = null, Expression>[] columnExpressions = null, bool withAlias = true) diff --git a/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs b/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs index 214cf950c5..ba6d919718 100644 --- a/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs +++ b/src/Umbraco.Core/Persistence/Querying/ExpressionVisitorBase.cs @@ -175,35 +175,27 @@ namespace Umbraco.Core.Persistence.Querying var operand = BindOperant(b.NodeType); if (operand == "AND" || operand == "OR") { - var m = b.Left as MemberExpression; - if (m != null && m.Expression != null) + if (b.Left is MemberExpression mLeft && mLeft.Expression != null) { - string r = VisitMemberAccess(m); + var r = VisitMemberAccess(mLeft); - SqlParameters.Add(1); + SqlParameters.Add(true); - //don't execute if compiled if (Visited == false) - { - left = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); - } + left = $"{r} = @{SqlParameters.Count - 1}"; } else { left = Visit(b.Left); } - m = b.Right as MemberExpression; - if (m != null && m.Expression != null) + if (b.Right is MemberExpression mRight && mRight.Expression != null) { - var r = VisitMemberAccess(m); + var r = VisitMemberAccess(mRight); - SqlParameters.Add(1); + SqlParameters.Add(true); - //don't execute if compiled if (Visited == false) - { - right = string.Format("{0} = @{1}", r, SqlParameters.Count - 1); - } + right = $"{r} = @{SqlParameters.Count - 1}"; } else { @@ -213,29 +205,25 @@ namespace Umbraco.Core.Persistence.Querying else if (operand == "=") { // deal with (x == true|false) - most common - var constRight = b.Right as ConstantExpression; - if (constRight != null && constRight.Type == typeof(bool)) - return (bool)constRight.Value ? VisitNotNot(b.Left) : VisitNot(b.Left); + if (b.Right is ConstantExpression constRight && constRight.Type == typeof(bool)) + return (bool) constRight.Value ? VisitNotNot(b.Left) : VisitNot(b.Left); right = Visit(b.Right); // deal with (true|false == x) - why not - var constLeft = b.Left as ConstantExpression; - if (constLeft != null && constLeft.Type == typeof(bool)) - return (bool)constLeft.Value ? VisitNotNot(b.Right) : VisitNot(b.Right); + if (b.Left is ConstantExpression constLeft && constLeft.Type == typeof(bool)) + return (bool) constLeft.Value ? VisitNotNot(b.Right) : VisitNot(b.Right); left = Visit(b.Left); } else if (operand == "<>") { // deal with (x != true|false) - most common - var constRight = b.Right as ConstantExpression; - if (constRight != null && constRight.Type == typeof (bool)) - return (bool)constRight.Value ? VisitNot(b.Left) : VisitNotNot(b.Left); + if (b.Right is ConstantExpression constRight && constRight.Type == typeof (bool)) + return (bool) constRight.Value ? VisitNot(b.Left) : VisitNotNot(b.Left); right = Visit(b.Right); // deal with (true|false != x) - why not - var constLeft = b.Left as ConstantExpression; - if (constLeft != null && constLeft.Type == typeof (bool)) - return (bool)constLeft.Value ? VisitNot(b.Right) : VisitNotNot(b.Right); + if (b.Left is ConstantExpression constLeft && constLeft.Type == typeof (bool)) + return (bool) constLeft.Value ? VisitNot(b.Right) : VisitNotNot(b.Right); left = Visit(b.Left); } else @@ -307,33 +295,20 @@ namespace Umbraco.Core.Persistence.Querying var o = getter(); SqlParameters.Add(o); - - //don't execute if compiled - if (Visited == false) - { - return string.Format("@{0}", SqlParameters.Count - 1); - } - //already compiled, return - return string.Empty; + return Visited ? string.Empty : $"@{SqlParameters.Count - 1}"; } catch (InvalidOperationException) { - //don't execute if compiled - if (Visited == false) + if (Visited) return string.Empty; + + var exprs = VisitExpressionList(nex.Arguments); + var r = new StringBuilder(); + foreach (var e in exprs) { - // FieldName ? - List exprs = VisitExpressionList(nex.Arguments); - var r = new StringBuilder(); - foreach (var e in exprs) - { - r.AppendFormat("{0}{1}", - r.Length > 0 ? "," : "", - e); - } - return r.ToString(); + if (r.Length > 0) r.Append(","); + r.Append(e); } - //already compiled, return - return string.Empty; + return r.ToString(); } } @@ -348,14 +323,7 @@ namespace Umbraco.Core.Persistence.Querying return "null"; SqlParameters.Add(c.Value); - - //don't execute if compiled - if (Visited == false) - { - return string.Format("@{0}", SqlParameters.Count - 1); - } - //already compiled, return - return string.Empty; + return Visited ? string.Empty : $"@{SqlParameters.Count - 1}"; } protected virtual string VisitUnary(UnaryExpression u) @@ -381,22 +349,10 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.MemberAccess: // false property , i.e. x => !Trashed SqlParameters.Add(true); - //don't execute if compiled - if (Visited == false) - { - return string.Format("NOT ({0} = @{1})", o, SqlParameters.Count - 1); - } - //already compiled, return - return string.Empty; + return Visited ? string.Empty : $"NOT ({o} = @{SqlParameters.Count - 1})"; default: - //don't execute if compiled - if (Visited == false) - { - // could be anything else, such as: x => !x.Path.StartsWith("-20") - return string.Concat("NOT (", o, ")"); - } - //already compiled, return - return string.Empty; + // could be anything else, such as: x => !x.Path.StartsWith("-20") + return Visited ? string.Empty : string.Concat("NOT (", o, ")"); } } @@ -409,17 +365,10 @@ namespace Umbraco.Core.Persistence.Querying case ExpressionType.MemberAccess: // true property, i.e. x => Trashed SqlParameters.Add(true); - - //don't execute if compiled - if (Visited == false) - { - return string.Format("({0} = @{1})", o, SqlParameters.Count - 1); - } - //already compiled, return - return string.Empty; + return Visited ? string.Empty : $"({o} = @{SqlParameters.Count - 1})"; default: // could be anything else, such as: x => x.Path.StartsWith("-20") - return o; + return Visited ? string.Empty : o; } } diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs index 2b0350d8c7..fd4e5f609f 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs @@ -13,11 +13,13 @@ namespace Umbraco.Core.Persistence.Querying internal class PocoToSqlExpressionVisitor : ExpressionVisitorBase { private readonly PocoData _pd; + private readonly string _alias; - public PocoToSqlExpressionVisitor(ISqlContext sqlContext) + public PocoToSqlExpressionVisitor(ISqlContext sqlContext, string alias) : base(sqlContext.SqlSyntax) { _pd = sqlContext.PocoDataFactory.ForType(typeof(TDto)); + _alias = alias; } protected override string VisitMethodCall(MethodCallExpression m) @@ -26,7 +28,7 @@ namespace Umbraco.Core.Persistence.Querying if (declaring != typeof (SqlTemplate)) return base.VisitMethodCall(m); - if (m.Method.Name != "Arg" && m.Method.Name != "ArgIn") + if (m.Method.Name != "ArgValue" && m.Method.Name != "ArgValueIn") throw new NotSupportedException($"Method SqlTemplate.{m.Method.Name} is not supported."); var parameters = m.Method.GetParameters(); @@ -48,7 +50,7 @@ namespace Umbraco.Core.Persistence.Querying name = getter().ToString(); } - SqlParameters.Add(RemoveQuote(name)); + SqlParameters.Add(new SqlTemplate.TemplateArg(RemoveQuote(name))); return Visited ? string.Empty @@ -59,22 +61,12 @@ namespace Umbraco.Core.Persistence.Querying { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(TDto)) { - //don't execute if compiled - if (Visited == false) - return GetFieldName(_pd, m.Member.Name); - - //already compiled, return - return string.Empty; + return Visited ? string.Empty : GetFieldName(_pd, m.Member.Name, _alias); } if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) { - //don't execute if compiled - if (Visited == false) - return GetFieldName(_pd, m.Member.Name); - - //already compiled, return - return string.Empty; + return Visited ? string.Empty : GetFieldName(_pd, m.Member.Name, _alias); } var member = Expression.Convert(m, typeof(object)); @@ -84,21 +76,16 @@ namespace Umbraco.Core.Persistence.Querying SqlParameters.Add(o); - //don't execute if compiled - if (Visited == false) - return $"@{SqlParameters.Count - 1}"; - - //already compiled, return - return string.Empty; + return Visited ? string.Empty : "@" + (SqlParameters.Count - 1); } - protected virtual string GetFieldName(PocoData pocoData, string name) + protected virtual string GetFieldName(PocoData pocoData, string name, string alias) { var column = pocoData.Columns.FirstOrDefault(x => x.Value.MemberInfoData.Name == name); - var tableName = SqlSyntax.GetQuotedTableName(pocoData.TableInfo.TableName); + var tableName = SqlSyntax.GetQuotedTableName(alias ?? pocoData.TableInfo.TableName); var columnName = SqlSyntax.GetQuotedColumnName(column.Value.ColumnName); - return $"{tableName}.{columnName}"; + return tableName + "." + columnName; } } @@ -167,7 +154,7 @@ namespace Umbraco.Core.Persistence.Querying SqlParameters.Add(o); // execute if not already compiled - return Visited ? string.Empty : $"@{SqlParameters.Count - 1}"; + return Visited ? string.Empty : "@" + (SqlParameters.Count - 1); } protected virtual string GetFieldName(PocoData pocoData, string name, string alias) diff --git a/src/Umbraco.Core/Persistence/Querying/Query.cs b/src/Umbraco.Core/Persistence/Querying/Query.cs index cb81965659..0c5dec7d4e 100644 --- a/src/Umbraco.Core/Persistence/Querying/Query.cs +++ b/src/Umbraco.Core/Persistence/Querying/Query.cs @@ -8,9 +8,9 @@ using NPoco; namespace Umbraco.Core.Persistence.Querying { /// - /// Represents the Query Builder for building LINQ translatable queries + /// Represents a query builder. /// - /// + /// A query builder translates Linq queries into Sql queries. public class Query : IQuery { private readonly ISqlContext _sqlContext; @@ -22,10 +22,8 @@ namespace Umbraco.Core.Persistence.Querying } /// - /// Adds a where clause to the query + /// Adds a where clause to the query. /// - /// - /// This instance so calls to this method are chainable public virtual IQuery Where(Expression> predicate) { if (predicate == null) return this; @@ -36,6 +34,9 @@ namespace Umbraco.Core.Persistence.Querying return this; } + /// + /// Adds a where-in clause to the query. + /// public virtual IQuery WhereIn(Expression> fieldSelector, IEnumerable values) { if (fieldSelector == null) return this; @@ -49,8 +50,6 @@ namespace Umbraco.Core.Persistence.Querying /// /// Adds a set of OR-ed where clauses to the query. /// - /// - /// This instance so calls to this method are chainable. public virtual IQuery WhereAny(IEnumerable>> predicates) { if (predicates == null) return this; @@ -84,7 +83,6 @@ namespace Umbraco.Core.Persistence.Querying if (sb == null) return this; sb.Append(")"); - //_wheres.Add(Tuple.Create(sb.ToString(), parameters.ToArray())); _wheres.Add(Tuple.Create("(" + sql.SQL + ")", sql.Arguments)); return this; @@ -93,7 +91,6 @@ namespace Umbraco.Core.Persistence.Querying /// /// Returns all translated where clauses and their sql parameters /// - /// public IEnumerable> GetWhereClauses() { return _wheres; diff --git a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs index 9365eab74f..1c93c5bb15 100644 --- a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs @@ -21,7 +21,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistNewItem(AuditItem entity) { - throw new NotImplementedException(); + Database.Insert(new LogDto + { + Comment = entity.Comment, + Datestamp = DateTime.Now, + Header = entity.AuditType.ToString(), + NodeId = entity.Id, + UserId = entity.UserId + }); } protected override void PersistUpdatedItem(AuditItem entity) @@ -80,7 +87,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "id = @Id"; + return "id = @id"; } protected override IEnumerable GetDeleteClauses() diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index a03be685cd..32dc955914 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -97,7 +97,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = SqlContext.Sql(); - switch (queryType) // FIXME pretend we still need these queries for now + switch (queryType) { case QueryType.Count: sql = sql.SelectCount(); @@ -130,41 +130,43 @@ namespace Umbraco.Core.Persistence.Repositories return sql; } - // fixme - move that one up to Versionable! or better: kill it! + // fixme - kill, eventually protected override Sql GetBaseQuery(bool isCount) { return GetBaseQuery(isCount ? QueryType.Count : QueryType.Single); } - protected override string GetBaseWhereClause() // fixme - can we kill / refactor this? + // fixme - kill, eventually + // ah maybe not, that what's used for eg Exists in base repo + protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; } protected override IEnumerable GetDeleteClauses() { - var list = new List // fixme table names, escape everything, etc - { - "DELETE FROM umbracoRedirectUrl WHERE contentKey IN (SELECT uniqueID FROM umbracoNode WHERE id = @Id)", - "DELETE FROM cmsTask WHERE nodeId = @Id", - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "DELETE FROM umbracoUserStartNode WHERE startNode = @Id", - "UPDATE umbracoUserGroup SET startContentId = NULL WHERE startContentId = @Id", - "DELETE FROM umbracoRelation WHERE parentId = @Id", - "DELETE FROM umbracoRelation WHERE childId = @Id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", - "DELETE FROM umbracoDomains WHERE domainRootStructureID = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentVersion + " WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @Id", - "DELETE FROM cmsPreviewXml WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE ContentId = @Id", - "DELETE FROM cmsContentXml WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @Id", - "DELETE FROM umbracoAccess WHERE nodeId = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" - }; + var list = new List + { + "DELETE FROM " + Constants.DatabaseSchema.Tables.RedirectUrl + " WHERE contentKey IN (SELECT uniqueId FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id)", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Task + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2NodePermission + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", + "UPDATE " + Constants.DatabaseSchema.Tables.UserGroup + " SET startContentId = NULL WHERE startContentId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE parentId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE childId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Domain + " WHERE domainRootStructureID = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentVersion + " WHERE id IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", + "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @id", + "DELETE FROM cmsPreviewXml WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", + "DELETE FROM cmsContentXml WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Access + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id" + }; return list; } @@ -184,7 +186,7 @@ namespace Umbraco.Core.Persistence.Repositories public override IContent GetByVersion(Guid versionId) { - var sql = GetBaseQuery(QueryType.Single) + var sql = GetBaseQuery(QueryType.Single, false) .Where(x => x.VersionId == versionId); var dto = Database.Fetch(sql).FirstOrDefault(); @@ -225,10 +227,7 @@ namespace Umbraco.Core.Persistence.Repositories var dto = ContentFactory.BuildDto(entity); // derive path and level from parent - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); var level = parent.Level + 1; // get sort order @@ -242,26 +241,15 @@ namespace Umbraco.Core.Persistence.Repositories // see if there's a reserved identifier for this unique id // and then either update or insert the node dto - template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetReservedId", tsql => - tsql.Select(x => x.NodeId).Where(x => x.UniqueId == SqlTemplate.Arg("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation) - ); - var id = Database.ExecuteScalar(template.Sql(nodeDto.UniqueId)); // fixme can we mix named & non-named? + var id = GetReservedId(nodeDto.UniqueId); if (id > 0) - { nodeDto.NodeId = id; - nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); - nodeDto.ValidatePathWithException(); - Database.Update(nodeDto); - } else - { Database.Insert(nodeDto); - // update path, now that we have an id - nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); - nodeDto.ValidatePathWithException(); - Database.Update(nodeDto); - } + nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); + nodeDto.ValidatePathWithException(); + Database.Update(nodeDto); // update entity entity.Id = nodeDto.NodeId; @@ -276,30 +264,25 @@ namespace Umbraco.Core.Persistence.Repositories // persist the content version dto // assumes a new version id and version date (modified date) has been set - var contentVersionDto = dto.DocumentVersionDto.ContentVersionDto; // fixme version id etc? + var contentVersionDto = dto.DocumentVersionDto.ContentVersionDto; contentVersionDto.NodeId = nodeDto.NodeId; contentVersionDto.Current = true; Database.Insert(contentVersionDto); // persist the document version dto var documentVersionDto = dto.DocumentVersionDto; - documentVersionDto.Id = contentVersionDto.Id; // fixme do we want this? + documentVersionDto.Id = contentVersionDto.Id; Database.Insert(documentVersionDto); // persist the document dto - dto.NodeId = nodeDto.NodeId; // fixme version id etc? + dto.NodeId = nodeDto.NodeId; Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; - // if published, set tags accordingly if (entity.Published) UpdateEntityTags(entity, _tagRepository); @@ -307,8 +290,7 @@ namespace Umbraco.Core.Persistence.Repositories // published => update published version infos, else leave it blank if (entity.Published) { - // fixme anything else we should do? - ((Content) entity).PublishedDate = dto.UpdateDate; + ((Content) entity).PublishedDate = contentDto.UpdateDate; } OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity)); @@ -319,27 +301,27 @@ namespace Umbraco.Core.Persistence.Repositories protected override void PersistUpdatedItem(IContent entity) { var content = (Content) entity; - var publishedState = content.PublishedState; - var publishedStateChanged = publishedState == PublishedState.Publishing || publishedState == PublishedState.Unpublishing; // check if we need to make any database changes at all - if (entity.RequiresSaving(publishedState) == false) + if (content.PublishedState == PublishedState.Published || content.PublishedState == PublishedState.Unpublished) { - entity.ResetDirtyProperties(); - return; + if (!content.IsEntityDirty() && !content.IsAnyUserPropertyDirty()) + return; // no change to save, do nothing, don't even update dates } - //check if we need to create a new version - var requiresNewVersion = entity.RequiresNewVersion(publishedState); + // check if we need to create a new version + var requiresNewVersion = content.PublishedState == PublishedState.Publishing && content.Published || content.PublishedState == PublishedState.Unpublishing; if (requiresNewVersion) { // drop all draft infos for the current version, won't need it anymore - Database.Delete("WHERE nodeId=@nodeId AND versionId=@versionId AND published=@published", - new { nodeId = content.Id, versionId = content.Version, published = false }); + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version && x.Published); + Database.Execute(deletePropertyDataSql); // current version is not current anymore - Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentVersion)} SET current=0 WHERE nodeId=@nodeId AND versionId=@versionId", - new { nodeId = content.Id, versionId = content.Version }); + var updateCurrentSql = SqlContext.Sql() + .Update(u => u.Set(x => x.Current, false)) + .Where(x => x.VersionId == content.Version); + Database.Execute(updateCurrentSql); // resets identifiers ie get a new version id content.UpdatingEntity(); @@ -361,10 +343,7 @@ namespace Umbraco.Core.Persistence.Repositories // if parent has changed, get path, level and sort order if (entity.IsPropertyDirty("ParentId")) { - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; @@ -379,36 +358,23 @@ namespace Umbraco.Core.Persistence.Repositories nodeDto.ValidatePathWithException(); Database.Update(nodeDto); - // update the content dto - only if the content type has changed - var origContentDto = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.NodeId == entity.Id)).FirstOrDefault(); - if (origContentDto.ContentTypeId != entity.ContentTypeId) - { - dto.ContentDto.Id = origContentDto.Id; // fixme - annoying, needed? - Database.Update(dto.ContentDto); - } - - //If Published state has changed then previous versions should have their publish state reset. - //If state has been changed to unpublished the previous versions publish state should also be reset. - //if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished)) - if (entity.RequiresClearPublishedFlag(publishedState, requiresNewVersion)) - ClearPublishedFlag(entity); // FIXME OUCH NO - - //Look up (newest) entries by id in cmsDocument table to set newest = false - ClearNewestFlag(entity); // FIXME OUCH NO + // update the content dto + Database.Update(dto.ContentDto); // insert or update the content & document version dtos - var contentVersionDto = dto.DocumentVersionDto.ContentVersionDto; // fixme version id etc? + var contentVersionDto = dto.DocumentVersionDto.ContentVersionDto; contentVersionDto.Current = true; var documentVersionDto = dto.DocumentVersionDto; if (requiresNewVersion) { // assumes a new version id and date (modified date) have been set Database.Insert(contentVersionDto); + documentVersionDto.Id = contentVersionDto.Id; Database.Insert(documentVersionDto); } else { - // fixme this pk thing is annoying + // fixme this pk thing is annoying - could we store that ID somewhere? var id = Database.ExecuteScalar(SqlContext.Sql().Select(x => x.Id).From().Where(x => x.VersionId == entity.Version)); contentVersionDto.Id = id; Database.Update(contentVersionDto); @@ -417,25 +383,26 @@ namespace Umbraco.Core.Persistence.Repositories } // update the document dto + // at that point, when un/publishing, the entity still has its old Published value + // so we need to explicitely update the dto to persist the correct value + if (content.PublishedState == PublishedState.Publishing) + dto.Published = true; + else if (content.PublishedState == PublishedState.Unpublishing) + dto.Published = false; Database.Update(dto); - // update the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); - foreach (var propertyDataDto in propertyDataDtos) + // replace the property data + if (!requiresNewVersion) { - if (requiresNewVersion == false && propertyDataDto.Id > 0) - Database.Update(propertyDataDto); - else - Database.Insert(propertyDataDto); + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version); + Database.Execute(deletePropertyDataSql); } - - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); + foreach (var propertyDataDto in propertyDataDtos) + Database.Insert(propertyDataDto); // update tags - if (HasTagProperty(entity)) // fixme what-if it had and now has not? + if (HasTagProperty(entity)) // fixme - what-if it had and now has not? { switch (content.PublishedState) { @@ -447,13 +414,11 @@ namespace Umbraco.Core.Persistence.Repositories // explicitely unpublishing, must clear tags ClearEntityTags(entity, _tagRepository); break; - case PublishedState.Saving: - // saving, nothing to do - break; case PublishedState.Published: case PublishedState.Unpublished: // no change, depends on path-published // that should take care of trashing and un-trashing + // fixme why, how would that work at all??? if (IsPathPublished(entity)) // slightly expensive ;-( UpdateEntityTags(entity, _tagRepository); else @@ -462,7 +427,16 @@ namespace Umbraco.Core.Persistence.Repositories } } - content.PublishedDate = dto.UpdateDate; + // flip the entity's published property + // this also flips its published state + if (content.PublishedState == PublishedState.Publishing) + content.Published = true; + else if (content.PublishedState == PublishedState.Unpublishing) + content.Published = false; + + if (content.Published) + content.PublishedDate = dto.ContentDto.UpdateDate; + OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity)); entity.ResetDirtyProperties(); @@ -491,22 +465,6 @@ namespace Umbraco.Core.Persistence.Repositories #region Content Repository - // fixme could we at least document what's that supposed to do? - public IEnumerable GetByPublishedVersion(IQuery query) - { - // we WANT to return contents in top-down order, ie parents should come before children - // ideal would be pure xml "document order" - which we cannot achieve at database level - - var sqlClause = GetBaseQuery(QueryType.Many); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate() - .Where(x => x.Published) - .OrderBy(x => x.Level) - .OrderBy(x => x.SortOrder); - - return MapDtosToContent(Database.Fetch(sql), true); - } - public int CountPublished(string contentTypeAlias = null) { var sql = SqlContext.Sql(); @@ -542,18 +500,6 @@ namespace Umbraco.Core.Persistence.Repositories PermissionRepository.ReplaceEntityPermissions(permissionSet); } - public void ClearPublishedFlag(IContent content) - { - var sql = "UPDATE cmsDocument SET published=0 WHERE nodeId=@id AND published=1"; - Database.Execute(sql, new { id = content.Id }); - } - - public void ClearNewestFlag(IContent content) - { - var sql = "UPDATE cmsDocument SET newest=0 WHERE nodeId=@id AND newest=1"; - Database.Execute(sql, new { id = content.Id }); - } - /// /// Assigns a single permission to the current content item for the specified group ids /// @@ -579,35 +525,26 @@ namespace Umbraco.Core.Persistence.Repositories PermissionRepository.AddOrUpdate(permission); } - // fixme must refactor entirely, will not work! /// - /// Gets paged content results + /// Gets paged content results. /// - /// Query to excute - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects - public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null, bool newest = true) + public IEnumerable GetPagedResultsByQuery(IQuery query, + long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, bool orderBySystemField, + IQuery filter = null) { - var filterSql = SqlContext.Sql(); - if (newest) - filterSql.Append("AND (cmsDocument.newest = 1)"); + Sql filterSql = null; if (filter != null) { + filterSql = Sql(); foreach (var filterClause in filter.GetWhereClauses()) - filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); + filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, x => MapDtosToContent(x), - orderBy, orderDirection, orderBySystemField, "cmsDocument", + orderBy, orderDirection, orderBySystemField, filterSql); } @@ -658,13 +595,8 @@ namespace Umbraco.Core.Persistence.Repositories return _contentByGuidReadRepository.Exists(id); } - /// - /// A reading repository purely for looking up by GUID - /// - /// - /// TODO: This is ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things! - /// Then we can do the same thing with repository instances and we wouldn't need to leave all these methods as not implemented because we wouldn't need to implement them - /// + // reading repository purely for looking up by GUID + // fixme - ugly and to fix we need to decouple the IRepositoryQueryable -> IRepository -> IReadRepository which should all be separate things! private class ContentByGuidReadRepository : NPocoRepositoryBase { private readonly ContentRepository _outerRepo; @@ -743,10 +675,10 @@ namespace Umbraco.Core.Persistence.Repositories switch (orderBy.ToUpperInvariant()) { case "UPDATER": - // fixme This isn't going to work very nicely because it's going to order by ID, not by letter + // fixme orders by id not letter = bad return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.Document, "writerUserId"); case "PUBLISHED": - // fixme is this still relevant? + // fixme kill return GetDatabaseFieldNameForOrderBy(Constants.DatabaseSchema.Tables.Document, "published"); case "CONTENTTYPEALIAS": throw new NotSupportedException("Don't know how to support ContentTypeAlias."); @@ -755,8 +687,6 @@ namespace Umbraco.Core.Persistence.Repositories return base.GetDatabaseFieldNameForOrderBy(orderBy); } - // fixme - we are not implementing the double-query thing for pagination from 7.6, need to do it differently! - private IEnumerable MapDtosToContent(List dtos, bool withCache = false) { var temps = new List>(); @@ -776,7 +706,7 @@ namespace Umbraco.Core.Persistence.Repositories var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); if (cached != null && cached.Version == versionId) { - content[i] = (Content) cached; // fixme should we just cache Content not IContent? + content[i] = (Content) cached; continue; } } @@ -839,33 +769,18 @@ namespace Umbraco.Core.Persistence.Repositories var properties = GetPropertyCollections(new List> { temp }); content.Properties = properties[dto.DocumentVersionDto.ContentVersionDto.VersionId]; - // clear dirty props on init - U4-1943 + // reset dirty initial properties (U4-1946) content.ResetDirtyProperties(false); return content; } - private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) + #region Utilities + + protected override string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { - if (EnsureUniqueNaming == false) - return nodeName; - - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => x.NodeId, x => x.Text) - .From() - .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId"))); - - var sql = template.Sql(NodeObjectTypeId, parentId); - var names = Database.Fetch(sql); - - return SimilarNodeName.GetUniqueName(names, id, nodeName); + return EnsureUniqueNaming == false ? nodeName : base.EnsureUniqueNodeName(parentId, nodeName, id); } - private int GetNewChildSortOrder(int parentId, int first) - { - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetSortOrder", tsql => - tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From().Where(x => x.NodeId == SqlTemplate.Arg("parentId") && x.NodeObjectType == NodeObjectTypeId) - ); - return Database.ExecuteScalar(template.Sql(parentId)) + 1; // fixme can we mix named & non-named? - } + #endregion } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index db1d867d9d..a9d2867dc7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -174,15 +174,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } protected override IEnumerable GetDeleteClauses() { var l = (List) base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @Id"); - l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id"); - l.Add("DELETE FROM umbracoNode WHERE id = @Id"); + l.Add("DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @id"); + l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); + l.Add("DELETE FROM umbracoNode WHERE id = @id"); return l; } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs index 1ffde67f1f..c7ed5b17cc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs @@ -698,8 +698,7 @@ AND umbracoNode.id <> @id", //Do something if adding fails? (Should hopefully not be possible unless someone created a circular reference) } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((Entity)contentType).ResetDirtyProperties(false); } } @@ -1251,16 +1250,16 @@ WHERE uContent.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", ne var list = new List { - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", - "DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id", - "DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id", - "DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id", - "DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE propertyTypeId IN (SELECT id FROM cmsPropertyType WHERE contentTypeId = @Id)", - "DELETE FROM cmsPropertyType WHERE contentTypeId = @Id", - "DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id", + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @id", + "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @id", + "DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @id", + "DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @id", + "DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @id", + "DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE propertyTypeId IN (SELECT id FROM cmsPropertyType WHERE contentTypeId = @id)", + "DELETE FROM cmsPropertyType WHERE contentTypeId = @id", + "DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @id", }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 261bc474d3..37aebf5086 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -139,12 +139,12 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } protected override IEnumerable GetDeleteClauses() { - return new List(); + return Array.Empty(); } protected override Guid NodeObjectTypeId => Constants.ObjectTypes.DataType; @@ -456,8 +456,13 @@ AND umbracoNode.id <> @id", private string EnsureUniqueNodeName(string nodeName, int id = 0) { - var names = Database.Fetch("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType", - new { objectType = NodeObjectTypeId }); + var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql + .Select(x => x.NodeId, x => x.Text) + .From() + .Where(x => x.NodeObjectType == SqlTemplate.ArgValue("nodeObjectType"))); + + var sql = template.Sql(NodeObjectTypeId); + var names = Database.Fetch(sql); return SimilarNodeName.GetUniqueName(names, id, nodeName); } @@ -510,7 +515,7 @@ AND umbracoNode.id <> @id", protected override IEnumerable GetDeleteClauses() { - return new List(); + return Array.Empty(); } protected override Guid NodeObjectTypeId diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 0c1dacdd4f..5a69b38f20 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -52,8 +52,7 @@ namespace Umbraco.Core.Persistence.Repositories var entity = ConvertFromDto(dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((Entity)entity).ResetDirtyProperties(false); return entity; @@ -108,7 +107,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsDictionary.pk = @Id"; + return "cmsDictionary.pk = @id"; } protected override IEnumerable GetDeleteClauses() @@ -319,7 +318,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " = @Id"; + return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " = @id"; } protected override IDictionaryItem ConvertToEntity(DictionaryDto dto) @@ -373,7 +372,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " = @Id"; + return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " = @id"; } protected override IDictionaryItem ConvertToEntity(DictionaryDto dto) diff --git a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs index 7a557c1434..789082286c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs @@ -69,14 +69,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoDomains.id = @Id"; + return "umbracoDomains.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoDomains WHERE id = @Id" + "DELETE FROM umbracoDomains WHERE id = @id" }; return list; } @@ -195,8 +195,7 @@ namespace Umbraco.Core.Persistence.Repositories LanguageId = dto.DefaultLanguage, RootContentId = dto.RootStructureId }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) domain.ResetDirtyProperties(false); return domain; } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index 8e17b1b251..7002518bf8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -96,8 +96,7 @@ namespace Umbraco.Core.Persistence.Repositories containedObjectType, nodeDto.Text, nodeDto.UserId ?? 0); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; diff --git a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs index fc55bd7f39..1ae88d9776 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs @@ -56,8 +56,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new ExternalLoginFactory(); var entity = factory.BuildEntity(dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; @@ -90,8 +89,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new ExternalLoginFactory(); foreach (var entity in dtos.Select(factory.BuildEntity)) { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); yield return entity; @@ -125,14 +123,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoExternalLogin.id = @Id"; + return "umbracoExternalLogin.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoExternalLogin WHERE id = @Id" + "DELETE FROM umbracoExternalLogin WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 4ac3bb0219..6f93338f97 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories { @@ -26,19 +25,6 @@ namespace Umbraco.Core.Persistence.Repositories /// void ReplaceContentPermissions(EntityPermissionSet permissionSet); - /// - /// Clears the published flag for a content. - /// - /// - void ClearPublishedFlag(IContent content); - - /// - /// Gets all published Content by the specified query - /// - /// Query to execute against published versions - /// An enumerable list of - IEnumerable GetByPublishedVersion(IQuery query); - /// /// Assigns a single permission to the current content item for the specified user group ids /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs index 9c83b929f2..da8beb6e59 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs @@ -63,18 +63,9 @@ namespace Umbraco.Core.Persistence.Repositories void DeleteVersions(int nodeId, DateTime versionDate); /// - /// Gets paged content results + /// Gets paged content results. /// - /// Query to excute - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null, bool newest = true); + string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index 5c7f8fd7a0..12bf303c28 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -81,7 +81,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoLanguage.id = @Id"; + return "umbracoLanguage.id = @id"; } protected override IEnumerable GetDeleteClauses() @@ -91,8 +91,8 @@ namespace Umbraco.Core.Persistence.Repositories { //NOTE: There is no constraint between the Language and cmsDictionary/cmsLanguageText tables (?) // but we still need to remove them - "DELETE FROM cmsLanguageText WHERE languageId = @Id", - "DELETE FROM umbracoLanguage WHERE id = @Id" + "DELETE FROM cmsLanguageText WHERE languageId = @id", + "DELETE FROM umbracoLanguage WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs index a9c503d0a4..8dd41582dc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override IMacro PerformGet(int id) { var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.Where(GetBaseWhereClause(), new { id }); return GetBySql(sql); } @@ -45,8 +45,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MacroFactory(); var entity = factory.BuildEntity(macroDto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); return entity; @@ -84,8 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MacroFactory(); foreach (var entity in dtos.Select(factory.BuildEntity)) { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); yield return entity; @@ -119,15 +117,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsMacro.id = @Id"; + return "cmsMacro.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM cmsMacroProperty WHERE macro = @Id", - "DELETE FROM cmsMacro WHERE id = @Id" + "DELETE FROM cmsMacroProperty WHERE macro = @id", + "DELETE FROM cmsMacro WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index ce7c37c677..c8891b0e5b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly MediaByGuidReadRepository _mediaByGuidReadRepository; public MediaRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection) - : base(work, cache, logger /*, contentSection*/) + : base(work, cache, logger) { _mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); @@ -89,7 +89,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = SqlContext.Sql(); - switch (queryType) // FIXME pretend we still need these queries for now + switch (queryType) { case QueryType.Count: sql = sql.SelectCount(); @@ -119,35 +119,37 @@ namespace Umbraco.Core.Persistence.Repositories return sql; } - // fixme - move that one up to Versionable! or better: kill it! + // fixme - kill, eventually protected override Sql GetBaseQuery(bool isCount) { return GetBaseQuery(isCount ? QueryType.Count : QueryType.Single); } - protected override string GetBaseWhereClause() // fixme - can we kill / refactor this? + // fixme - kill, eventually + // ah maybe not, that what's used for eg Exists in base repo + protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; } protected override IEnumerable GetDeleteClauses() { - var list = new List // fixme table names, escape everything, etc - { - "DELETE FROM cmsTask WHERE nodeId = @Id", - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "DELETE FROM umbracoUserStartNode WHERE startNode = @Id", - "UPDATE umbracoUserGroup SET startMediaId = NULL WHERE startMediaId = @Id", - "DELETE FROM umbracoRelation WHERE parentId = @Id", - "DELETE FROM umbracoRelation WHERE childId = @Id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE ContentId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" - }; + var list = new List + { + "DELETE FROM " + Constants.DatabaseSchema.Tables.Task + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2NodePermission + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", + "UPDATE " + Constants.DatabaseSchema.Tables.UserGroup + " SET startContentId = NULL WHERE startContentId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE parentId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE childId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id" + }; return list; } @@ -219,7 +221,7 @@ namespace Umbraco.Core.Persistence.Repositories 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 nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId }); } #endregion @@ -241,10 +243,7 @@ namespace Umbraco.Core.Persistence.Repositories var dto = MediaFactory.BuildDto(entity); // derive path and level from parent - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); var level = parent.Level + 1; // get sort order @@ -258,10 +257,7 @@ namespace Umbraco.Core.Persistence.Repositories // see if there's a reserved identifier for this unique id // and then either update or insert the node dto - template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetReservedId", tsql => - tsql.Select(x => x.NodeId).Where(x => x.UniqueId == SqlTemplate.Arg("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation) - ); - var id = Database.ExecuteScalar(template.Sql(nodeDto.UniqueId)); // fixme can we mix named & non-named? + var id = GetReservedId(nodeDto.UniqueId); if (id > 0) { nodeDto.NodeId = id; @@ -297,15 +293,10 @@ namespace Umbraco.Core.Persistence.Repositories Database.Insert(contentVersionDto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; - // set tags UpdateEntityTags(entity, _tagRepository); @@ -329,10 +320,7 @@ namespace Umbraco.Core.Persistence.Repositories // if parent has changed, get path, level and sort order if (entity.IsPropertyDirty("ParentId")) { - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; @@ -347,36 +335,23 @@ namespace Umbraco.Core.Persistence.Repositories nodeDto.ValidatePathWithException(); Database.Update(nodeDto); - // update the content dto - only if the content type has changed - var origContentDto = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.NodeId == entity.Id)).FirstOrDefault(); - if (origContentDto.ContentTypeId != entity.ContentTypeId) - { - dto.Id = origContentDto.Id; // fixme - annoying, needed? - Database.Update(dto); - } + // update the content dto + Database.Update(dto); // update the content version dto var contentVersionDto = dto.ContentVersionDto; // fixme version id etc? contentVersionDto.Current = true; - // fixme this pk thing is annoying + // fixme this pk thing is annoying - could we store that ID somewhere? var id = Database.ExecuteScalar(SqlContext.Sql().Select(x => x.Id).From().Where(x => x.VersionId == entity.Version)); contentVersionDto.Id = id; Database.Update(contentVersionDto); // update the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version); + Database.Execute(deletePropertyDataSql); + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); foreach (var propertyDataDto in propertyDataDtos) - { - if (propertyDataDto.Id > 0) - Database.Update(propertyDataDto); - else - Database.Insert(propertyDataDto); - } - - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; + Database.Insert(propertyDataDto); UpdateEntityTags(entity, _tagRepository); @@ -494,32 +469,22 @@ namespace Umbraco.Core.Persistence.Repositories #endregion /// - /// Gets paged media results + /// Gets paged media results. /// - /// Query to excute - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null, bool newest = true) + string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null) { Sql filterSql = null; + if (filter != null) { filterSql = Sql(); foreach (var clause in filter.GetWhereClauses()) - { filterSql = filterSql.Append($"AND ({clause.Item1})", clause.Item2); - } } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, "cmsContentVersion", + x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, filterSql); } @@ -584,33 +549,14 @@ namespace Umbraco.Core.Persistence.Repositories var properties = GetPropertyCollections(new List> { temp }); media.Properties = properties[dto.ContentVersionDto.VersionId]; - // clear dirty props on init - U4-1943 + // reset dirty initial properties (U4-1946) media.ResetDirtyProperties(false); return media; } - private string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) + protected override string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { - if (EnsureUniqueNaming == false) - return nodeName; - - var template = SqlContext.Templates.Get("Umbraco.Core.MediaRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => x.NodeId, x => x.Text) - .From() - .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId"))); - - var sql = template.Sql(NodeObjectTypeId, parentId); - var names = Database.Fetch(sql); - - return SimilarNodeName.GetUniqueName(names, id, nodeName); - } - - private int GetNewChildSortOrder(int parentId, int first) - { - var template = SqlContext.Templates.Get("Umbraco.Core.MediaRepository.GetSortOrder", tsql => - tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From().Where(x => x.NodeId == SqlTemplate.Arg("parentId") && x.NodeObjectType == NodeObjectTypeId) - ); - return Database.ExecuteScalar(template.Sql(parentId)) + 1; // fixme can we mix named & non-named? + return EnsureUniqueNaming == false ? nodeName : base.EnsureUniqueNodeName(parentId, nodeName, id); } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index a38b7c9118..ce039afb8a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -125,14 +125,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } protected override IEnumerable GetDeleteClauses() { var l = (List) base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id"); - l.Add("DELETE FROM umbracoNode WHERE id = @Id"); + l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); + l.Add("DELETE FROM umbracoNode WHERE id = @id"); return l; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index 17f3aa8b61..ee7726f349 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -70,20 +70,20 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new[] { - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "DELETE FROM umbracoRelation WHERE parentId = @Id", - "DELETE FROM umbracoRelation WHERE childId = @Id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", - "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @id", + "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @id", + "DELETE FROM umbracoRelation WHERE parentId = @id", + "DELETE FROM umbracoRelation WHERE childId = @id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @id", + "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @id", + "DELETE FROM umbracoNode WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index adec5cc893..6074198782 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -115,20 +115,20 @@ namespace Umbraco.Core.Persistence.Repositories sql = sql.Select(r => r.Select(x => x.ContentVersionDto) .Select(x => x.ContentDto, r1 => - r1.Select(x => x.NodeDto))); + r1.Select(x => x.NodeDto))); break; } sql .From() - .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .InnerJoin().On(left => left.NodeId, right => right.NodeId) .InnerJoin().On(left => left.NodeId, right => right.NodeId) - .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .InnerJoin().On(left => left.NodeId, right => right.NodeId) // joining the type so we can do a query against the member type - not sure if this adds much overhead or not? // the execution plan says it doesn't so we'll go with that and in that case, it might be worth joining the content // types by default on the document and media repo's so we can query by content type there too. - .InnerJoin().On(left => left.NodeId, right => right.ContentTypeId); + .InnerJoin().On(left => left.ContentTypeId, right => right.NodeId); sql.Where(x => x.NodeObjectType == NodeObjectTypeId); @@ -146,7 +146,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() // fixme - can we kill / refactor this? { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } // fixme wtf? @@ -170,19 +170,19 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { - "DELETE FROM cmsTask WHERE nodeId = @Id", - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "DELETE FROM umbracoRelation WHERE parentId = @Id", - "DELETE FROM umbracoRelation WHERE childId = @Id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @Id", - "DELETE FROM cmsMember2MemberGroup WHERE Member = @Id", - "DELETE FROM cmsMember WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE ContentId = @Id", - "DELETE FROM cmsContentXml WHERE nodeId = @Id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" + "DELETE FROM cmsTask WHERE nodeId = @id", + "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @id", + "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @id", + "DELETE FROM umbracoRelation WHERE parentId = @id", + "DELETE FROM umbracoRelation WHERE childId = @id", + "DELETE FROM cmsTagRelationship WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE nodeId = @id", + "DELETE FROM cmsMember2MemberGroup WHERE Member = @id", + "DELETE FROM cmsMember WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE ContentId = @id", + "DELETE FROM cmsContentXml WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", + "DELETE FROM umbracoNode WHERE id = @id" }; return list; } @@ -235,10 +235,7 @@ namespace Umbraco.Core.Persistence.Repositories var dto = MemberFactory.BuildDto(entity); // derive path and level from parent - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); var level = parent.Level + 1; // get sort order @@ -252,10 +249,7 @@ namespace Umbraco.Core.Persistence.Repositories // see if there's a reserved identifier for this unique id // and then either update or insert the node dto - template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetReservedId", tsql => - tsql.Select(x => x.NodeId).Where(x => x.UniqueId == SqlTemplate.Arg("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation) - ); - var id = Database.ExecuteScalar(template.Sql(nodeDto.UniqueId)); // fixme can we mix named & non-named? + var id = GetReservedId(nodeDto.UniqueId); if (id > 0) { nodeDto.NodeId = id; @@ -306,15 +300,10 @@ namespace Umbraco.Core.Persistence.Repositories Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; - UpdateEntityTags(entity, _tagRepository); OnUowRefreshedEntity(new UnitOfWorkEntityEventArgs(UnitOfWork, entity)); @@ -334,10 +323,7 @@ namespace Umbraco.Core.Persistence.Repositories // if parent has changed, get path, level and sort order if (entity.IsPropertyDirty("ParentId")) { - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetParentNode", tsql => - tsql.Select(x => x.NodeId).Where(x => x.NodeId == SqlTemplate.Arg("parentId")) - ); - var parent = Database.Fetch(template.Sql(entity.ParentId)).First(); + var parent = GetParentNodeDto(entity.ParentId); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; @@ -351,19 +337,13 @@ namespace Umbraco.Core.Persistence.Repositories var nodeDto = dto.ContentDto.NodeDto; Database.Update(nodeDto); - // update the content dto - only if the content type has changed - var origContentDto = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.NodeId == entity.Id)).FirstOrDefault(); - if (origContentDto.ContentTypeId != entity.ContentTypeId) - { - dto.ContentDto.Id = origContentDto.Id; // fixme - annoying, needed? - Database.Update(dto.ContentDto); - } + // update the content dto + Database.Update(dto.ContentDto); - // insert or update the content version dtos - var contentVerDto = Database.SingleOrDefault("WHERE VersionId = @Version", new { /*Version =*/ entity.Version }); - dto.ContentVersionDto.Id = contentVerDto.Id; - //Updates the current version - cmsContentVersion - //Assumes a Version guid exists and Version date (modified date) has been set/updated + // update the content version dto + // fixme this pk thing is annoying - could we store that ID somewhere? + var id = Database.ExecuteScalar(SqlContext.Sql().Select(x => x.Id).From().Where(x => x.VersionId == entity.Version)); + dto.ContentVersionDto.Id = id; Database.Update(dto.ContentVersionDto); // update the member dto @@ -383,20 +363,12 @@ namespace Umbraco.Core.Persistence.Repositories if (changedCols.Count > 0) Database.Update(dto, changedCols); - // update the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties).ToArray(); + // replace the property data + var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == entity.Version); + Database.Execute(deletePropertyDataSql); + var propertyDataDtos = PropertyFactory.BuildDtos(entity.Id, entity.Version, entity.Properties); foreach (var propertyDataDto in propertyDataDtos) - { - if (propertyDataDto.Id > 0) - Database.Update(propertyDataDto); - else - Database.Insert(propertyDataDto); - } - - // assign ids to properties, using propertyTypeId as a key - var xids = propertyDataDtos.ToDictionary(x => x.PropertyTypeId, x => x.Id); - foreach (var property in entity.Properties) - property.Id = xids[property.PropertyTypeId]; + Database.Insert(propertyDataDto); UpdateEntityTags(entity, _tagRepository); @@ -513,37 +485,22 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Gets paged member results + /// Gets paged member results. /// - /// - /// The where clause, if this is null all records are queried - /// - /// Index of the page. - /// Size of the page. - /// The total records. - /// The order by column - /// The order direction. - /// Flag to indicate when ordering by system field - /// Search query - /// - /// - /// The query supplied will ONLY work with data specifically on the cmsMember table because we are using NPoco paging (SQL paging) - /// public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null, bool newest = true) + string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null) { Sql filterSql = null; + if (filter != null) { filterSql = Sql(); foreach (var clause in filter.GetWhereClauses()) - { filterSql = filterSql.Append($"AND ({clause.Item1})", clause.Item2); - } } return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, - x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, "cmsMember", + x => MapDtosToContent(x), orderBy, orderDirection, orderBySystemField, filterSql); } @@ -638,17 +595,9 @@ namespace Umbraco.Core.Persistence.Repositories var properties = GetPropertyCollections(new List> { temp }); member.Properties = properties[dto.ContentVersionDto.VersionId]; - // clear dirty props on init - U4-1943 + // reset dirty initial properties (U4-1946) member.ResetDirtyProperties(false); return member; } - - private int GetNewChildSortOrder(int parentId, int first) - { - var template = SqlContext.Templates.Get("Umbraco.Core.ContentRepository.GetSortOrder", tsql => - tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From().Where(x => x.NodeId == SqlTemplate.Arg("parentId") && x.NodeObjectType == NodeObjectTypeId) - ); - return Database.ExecuteScalar(template.Sql(parentId)) + 1; // fixme can we mix named & non-named? - } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 6a727b46fa..dad1a93b7d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -180,15 +180,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return "umbracoNode.id = @id"; } protected override IEnumerable GetDeleteClauses() { var l = (List) base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM cmsMemberType WHERE NodeId = @Id"); - l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id"); - l.Add("DELETE FROM umbracoNode WHERE id = @Id"); + l.Add("DELETE FROM cmsMemberType WHERE NodeId = @id"); + l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); + l.Add("DELETE FROM umbracoNode WHERE id = @id"); return l; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs index e8135334ac..b7e5548512 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs @@ -35,8 +35,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new MigrationEntryFactory(); var entity = factory.BuildEntity(dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); return entity; @@ -82,14 +81,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "id = @Id"; + return "id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoMigration WHERE id = @Id" + "DELETE FROM umbracoMigration WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs index 6e719e74c5..8cf7bd2608 100644 --- a/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/NPocoRepositoryBase.cs @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override bool PerformExists(TId id) { var sql = GetBaseQuery(true); - sql.Where(GetBaseWhereClause(), new { Id = id}); + sql.Where(GetBaseWhereClause(), new { id = id}); var count = Database.ExecuteScalar(sql); return count == 1; } @@ -81,7 +81,7 @@ namespace Umbraco.Core.Persistence.Repositories var deletes = GetDeleteClauses(); foreach (var delete in deletes) { - Database.Execute(delete, new { Id = GetEntityId(entity) }); + Database.Execute(delete, new { id = GetEntityId(entity) }); } entity.DeletedDate = DateTime.Now; } diff --git a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs index abc3138c0b..a92b9f9e6d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs @@ -41,8 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories VirtualPath = FileSystem.GetUrl(id) }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) view.ResetDirtyProperties(false); return view; diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 2593f59304..1f5f322e61 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -67,15 +67,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoAccess.id = @Id"; + return "umbracoAccess.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoAccessRule WHERE accessId = @Id", - "DELETE FROM umbracoAccess WHERE id = @Id" + "DELETE FROM umbracoAccessRule WHERE accessId = @id", + "DELETE FROM umbracoAccess WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs index 97c422cb2b..bfdf564c26 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.UnitOfWork; @@ -11,8 +10,8 @@ namespace Umbraco.Core.Persistence.Repositories where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected RecycleBinRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger /*, IContentSection contentSection*/) - : base(work, cache, logger /*, contentSection*/) + protected RecycleBinRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger) + : base(work, cache, logger) { } protected abstract int RecycleBinId { get; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs index 14b19d5eff..cd14aa227c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs @@ -64,14 +64,14 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); protected override string GetBaseWhereClause() { - return "id = @Id"; + return "id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoRedirectUrl WHERE id = @Id" + "DELETE FROM umbracoRedirectUrl WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index 5b6a2b06f0..3b9d109761 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -35,7 +35,7 @@ namespace Umbraco.Core.Persistence.Repositories protected override IRelation PerformGet(int id) { var sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { Id = id }); + sql.Where(GetBaseWhereClause(), new { id }); var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); if (dto == null) @@ -89,8 +89,7 @@ namespace Umbraco.Core.Persistence.Repositories { var entity = factory.BuildEntity(dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); return entity; @@ -116,14 +115,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoRelation.id = @Id"; + return "umbracoRelation.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoRelation WHERE id = @Id" + "DELETE FROM umbracoRelation WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index 57fcc8541e..2085a7cfb9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -83,8 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories { var entity = factory.BuildEntity(dto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase) entity).ResetDirtyProperties(false); return entity; @@ -110,15 +109,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoRelationType.id = @Id"; + return "umbracoRelationType.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoRelation WHERE relType = @Id", - "DELETE FROM umbracoRelationType WHERE id = @Id" + "DELETE FROM umbracoRelation WHERE relType = @id", + "DELETE FROM umbracoRelationType WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index 375c5f06ec..17875317c2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -51,8 +51,7 @@ namespace Umbraco.Core.Persistence.Repositories VirtualPath = FileSystem.GetUrl(path) }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) script.ResetDirtyProperties(false); return script; diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs index 097e0fc471..48b6250b33 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs @@ -82,14 +82,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "id = @Id"; + return "id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoServer WHERE id = @Id" + "DELETE FROM umbracoServer WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs index 18b92c3cb4..b0081a1ea8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs @@ -46,8 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories var dirtyEntity = entity as Entity; if (dirtyEntity != null) { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) dirtyEntity.ResetDirtyProperties(false); } diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index e7e78eae76..ea920b538c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -48,8 +48,7 @@ namespace Umbraco.Core.Persistence.Repositories VirtualPath = FileSystem.GetUrl(path) }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) stylesheet.ResetDirtyProperties(false); return stylesheet; diff --git a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs index f666f572a3..82e7d9b78a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs @@ -33,8 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new TagFactory(); var entity = factory.BuildEntity(tagDto); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); return entity; @@ -71,8 +70,7 @@ namespace Umbraco.Core.Persistence.Repositories var factory = new TagFactory(); foreach (var entity in dtos.Select(factory.BuildEntity)) { - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) ((TracksChangesEntityBase)entity).ResetDirtyProperties(false); yield return entity; } @@ -103,15 +101,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "id = @Id"; + return "id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM cmsTagRelationship WHERE tagId = @Id", - "DELETE FROM cmsTags WHERE id = @Id" + "DELETE FROM cmsTagRelationship WHERE tagId = @id", + "DELETE FROM cmsTags WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs index 1058783b66..d7ab29bd9b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs @@ -77,14 +77,14 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsTask.id = @Id"; + return "cmsTask.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM cmsTask WHERE id = @Id" + "DELETE FROM cmsTask WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs index 770a83b535..aaa17538bb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs @@ -67,15 +67,15 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "cmsTaskType.id = @Id"; + return "cmsTaskType.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM cmsTask WHERE taskTypeId = @Id", - "DELETE FROM cmsTaskType WHERE id = @Id" + "DELETE FROM cmsTask WHERE taskTypeId = @id", + "DELETE FROM cmsTaskType WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index e5f7820044..4b62523a79 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -124,20 +124,20 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoNode.id = @Id"; + return Constants.DatabaseSchema.Tables.Node + ".id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List - { - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE nodeId = @Id", - "UPDATE cmsDocument SET templateId = NULL WHERE templateId = @Id", - "DELETE FROM cmsDocumentType WHERE templateNodeId = @Id", - "DELETE FROM cmsTemplate WHERE nodeId = @Id", - "DELETE FROM umbracoNode WHERE id = @Id" - }; + { + "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2NodePermission + " WHERE nodeId = @id", + "UPDATE " + Constants.DatabaseSchema.Tables.DocumentVersion + " SET templateId = NULL WHERE templateId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentType + " WHERE templateNodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Template + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id" + }; return list; } @@ -294,14 +294,14 @@ namespace Umbraco.Core.Persistence.Repositories { foreach (var delete in deletes) { - Database.Execute(delete, new { Id = GetEntityId(descendant) }); + Database.Execute(delete, new { id = GetEntityId(descendant) }); } } //now we can delete this one foreach (var delete in deletes) { - Database.Execute(delete, new { Id = GetEntityId(entity) }); + Database.Execute(delete, new { id = GetEntityId(entity) }); } if (DetermineTemplateRenderingEngine(entity) == RenderingEngine.Mvc) @@ -370,8 +370,7 @@ namespace Umbraco.Core.Persistence.Repositories // path changes - but do not get content, will get loaded only when required GetFileContent(template, true); - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) template.ResetDirtyProperties(false); return template; diff --git a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs index 189a5adcb9..d85e128f77 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserGroupRepository.cs @@ -271,17 +271,17 @@ namespace Umbraco.Core.Persistence.Repositories protected override string GetBaseWhereClause() { - return "umbracoUserGroup.id = @Id"; + return "umbracoUserGroup.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoUser2UserGroup WHERE userGroupId = @Id", - "DELETE FROM umbracoUserGroup2App WHERE userGroupId = @Id", - "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @Id", - "DELETE FROM umbracoUserGroup WHERE id = @Id" + "DELETE FROM umbracoUser2UserGroup WHERE userGroupId = @id", + "DELETE FROM umbracoUserGroup2App WHERE userGroupId = @id", + "DELETE FROM umbracoUserGroup2NodePermission WHERE userGroupId = @id", + "DELETE FROM umbracoUserGroup WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index edf54333ee..fa5bfb8103 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -313,19 +313,19 @@ ORDER BY colName"; protected override string GetBaseWhereClause() { - return "umbracoUser.id = @Id"; + return "umbracoUser.id = @id"; } protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM cmsTask WHERE userId = @Id", - "DELETE FROM cmsTask WHERE parentUserId = @Id", - "DELETE FROM umbracoUser2UserGroup WHERE userId = @Id", - "DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id", - "DELETE FROM umbracoUser WHERE id = @Id", - "DELETE FROM umbracoExternalLogin WHERE id = @Id" + "DELETE FROM cmsTask WHERE userId = @id", + "DELETE FROM cmsTask WHERE parentUserId = @id", + "DELETE FROM umbracoUser2UserGroup WHERE userId = @id", + "DELETE FROM umbracoUser2NodeNotify WHERE userId = @id", + "DELETE FROM umbracoUser WHERE id = @id", + "DELETE FROM umbracoExternalLogin WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 5d9bac40ed..24ee659c11 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -52,7 +52,7 @@ namespace Umbraco.Core.Persistence.Repositories var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersionIds", tsql => tsql.Select(x => x.VersionId) .From() - .Where(x => x.NodeId == SqlTemplate.Arg("nodeId")) + .Where(x => x.NodeId == SqlTemplate.ArgValue("nodeId")) .OrderByDescending(x => x.Current) // current '1' comes before others '0' .AndByDescending(x => x.VersionDate) // most recent first ); @@ -66,7 +66,7 @@ namespace Umbraco.Core.Persistence.Repositories // 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.VersionId == SqlTemplate.ArgValue("versionId")) ); var versionDto = Database.Fetch(template.Sql(versionId)).FirstOrDefault(); @@ -88,7 +88,7 @@ namespace Umbraco.Core.Persistence.Repositories // get the versions we want to delete, excluding the current one var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetVersion", tsql => - tsql.Select().From().Where(x => x.NodeId == SqlTemplate.Arg("nodeId") && !x.Current && x.VersionDate < SqlTemplate.Arg("date")) + tsql.Select().From().Where(x => x.NodeId == SqlTemplate.ArgValue("nodeId") && !x.Current && x.VersionDate < SqlTemplate.ArgValue("date")) ); var versionDtos = Database.Fetch(template.Sql(nodeId, versionDate)); // fixme ok params? foreach (var versionDto in versionDtos) @@ -245,7 +245,12 @@ namespace Umbraco.Core.Persistence.Repositories #endregion - private Sql PrepareSqlForPagedResults(Sql sql, Sql filterSql, string orderBy, Direction orderDirection, bool orderBySystemField, string table) + // sql: the main sql + // filterSql: a filtering ? fixme different from v7? + // orderBy: the name of an ordering field + // orderDirection: direction for orderBy + // orderBySystemField: whether orderBy is a system field or a custom field (property value) + private Sql PrepareSqlForPagedResults(Sql sql, Sql filterSql, string orderBy, Direction orderDirection, bool orderBySystemField) { if (filterSql == null && string.IsNullOrEmpty(orderBy)) return sql; @@ -263,7 +268,7 @@ namespace Umbraco.Core.Persistence.Repositories // else apply sort var dbfield = orderBySystemField ? GetOrderBySystemField(ref psql, orderBy) - : GetOrderByNonSystemField(ref psql, orderBy, table); + : GetOrderByNonSystemField(ref psql, orderBy); if (orderDirection == Direction.Ascending) psql.OrderBy(dbfield); @@ -272,23 +277,23 @@ namespace Umbraco.Core.Persistence.Repositories // no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique. // if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column - // is empty for many nodes) - // see: http://issues.umbraco.org/issue/U4-8831 - // fixme - commented out is 7.6 and looks suspicious ??!! - //if (orderBySystemField && orderBy.InvariantEquals("umbraconode.id") == false) + // is empty for many nodes) - see: http://issues.umbraco.org/issue/U4-8831 + dbfield = GetDatabaseFieldNameForOrderBy("umbracoNode", "id"); if (orderBySystemField == false || orderBy.InvariantEquals(dbfield) == false) { + // get alias, if aliased var matches = VersionableRepositoryBaseAliasRegex.For(SqlContext.SqlSyntax).Matches(sql.SQL); var match = matches.Cast().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield)); - if (match != null) - dbfield = match.Groups[2].Value; + if (match != null) dbfield = match.Groups[2].Value; + + // add field psql.OrderBy(dbfield); } - // fixme - temp - for the time being NPoco PagingHelper cannot deal with multiline - psql = new Sql(psql.SqlContext, psql.SQL.Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " "), psql.Arguments); - + // create prepared sql + // ensure it's single-line as NPoco PagingHelper has issues with multi-lines + psql = new Sql(psql.SqlContext, psql.SQL.ToSingleLine(), psql.Arguments); return psql; } @@ -309,60 +314,39 @@ namespace Umbraco.Core.Persistence.Repositories // note: ContentTypeAlias is not properly managed because it's not part of the query to begin with! + // get alias, if aliased var matches = VersionableRepositoryBaseAliasRegex.For(SqlContext.SqlSyntax).Matches(sql.SQL); var match = matches.Cast().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(dbfield)); - if (match != null) - dbfield = match.Groups[2].Value; + if (match != null) dbfield = match.Groups[2].Value; return dbfield; } - private string GetOrderByNonSystemField(ref Sql sql, string orderBy, string table) + private string GetOrderByNonSystemField(ref Sql sql, string orderBy) { - // Sorting by a custom field, so set-up sub-query for ORDER BY clause to pull through value - // from most recent content version for the given order by field + // sorting by a custom field, so set-up sub-query for ORDER BY clause to pull through value + // from 'current' content version for the given order by field var sortedInt = string.Format(SqlContext.SqlSyntax.ConvertIntegerToOrderableString, "intValue"); var sortedDate = string.Format(SqlContext.SqlSyntax.ConvertDateToOrderableString, "dateValue"); var sortedString = "COALESCE(varcharValue,'')"; // assuming COALESCE is ok for all syntaxes var sortedDecimal = string.Format(SqlContext.SqlSyntax.ConvertDecimalToOrderableString, "decimalValue"); - // variable query fragments that depend on what we are querying - string andVersion, andNewest, idField; - switch (table) - { - case "cmsDocument": - andVersion = " AND cpd.versionId = cd.versionId"; - andNewest = " AND cd.newest = 1"; - idField = "nodeId"; - break; - case "cmsMember": - andVersion = string.Empty; - andNewest = string.Empty; - idField = "nodeId"; - break; - case "cmsContentVersion": - andVersion = " AND cpd.versionId = cd.versionId"; - andNewest = string.Empty; - idField = "contentId"; - break; - default: - throw new NotSupportedException($"Table {table} is not supported."); - } - // needs to be an outer join since there's no guarantee that any of the nodes have values for this property - var outerJoinTempTable = $@"LEFT OUTER JOIN ( - SELECT CASE - WHEN intValue IS NOT NULL THEN {sortedInt} - WHEN decimalValue IS NOT NULL THEN {sortedDecimal} - WHEN dateValue IS NOT NULL THEN {sortedDate} - ELSE {sortedString} - END AS CustomPropVal, - cd.{idField} AS CustomPropValContentId - FROM {table} cd - INNER JOIN " + Constants.DatabaseSchema.Tables.PropertyData + @" cpd ON cpd.nodeId = cd.{idField}{andVersion} - INNER JOIN cmsPropertyType cpt ON cpt.Id = cpd.propertytypeId - WHERE cpt.Alias = @{sql.Arguments.Length}{andNewest}) AS CustomPropData - ON CustomPropData.CustomPropValContentId = umbracoNode.id "; // trailing space is important! + var innerSql = Sql().Select($@"CASE + WHEN intValue IS NOT NULL THEN {sortedInt} + WHEN decimalValue IS NOT NULL THEN {sortedDecimal} + WHEN dateValue IS NOT NULL THEN {sortedDate} + ELSE {sortedString} + END AS customPropVal, + cver.nodeId AS customPropNodeId") + .From("cver") + .InnerJoin("opdata").On((left, right) => left.NodeId == right.NodeId && left.VersionId == right.VersionId && left.Current, "cver", "opdata") + .InnerJoin("optype").On((left, right) => left.PropertyTypeId == right.Id, "opdata", "optype") + .Where(x => x.Alias == "", "optype"); + + var innerSqlString = innerSql.SQL.Replace("@0", "1").Replace("@1", "@" + sql.Arguments.Length); + var outerJoinTempTable = $@"LEFT OUTER JOIN ({innerSqlString}) AS customPropData + ON customPropData.customPropNodeId = {Constants.DatabaseSchema.Tables.Node}.id "; // trailing space is important! // insert this just above the last WHERE var pos = sql.SQL.InvariantIndexOf("WHERE"); @@ -373,44 +357,20 @@ namespace Umbraco.Core.Persistence.Repositories newArgs.Add(orderBy); // insert the SQL selected field, too, else ordering cannot work - if (sql.SQL.StartsWith("SELECT ") == false) throw new Exception("Oops, SELECT not found."); - newSql = newSql.Insert("SELECT ".Length, "CustomPropData.CustomPropVal, "); + if (sql.SQL.StartsWith("SELECT ") == false) throw new Exception("Oops: SELECT not found."); + newSql = newSql.Insert("SELECT ".Length, "customPropData.customPropVal, "); sql = new Sql(sql.SqlContext, newSql, newArgs.ToArray()); // and order by the custom field - return "CustomPropData.CustomPropVal"; + return "customPropData.customPropVal"; } - // fixme - // - // mergin from 7.6... well we cannot just merge what comes from 7.6 - too much distance - // - // need to understand what's been done in 7.6 and why, and reproduce here - // - 2a4e73c on 01/22 uses static factories, well we should use them everywhere but l8tr - // - c7b505f on 01/24 introduces QueryType and 2 queries for fetching content <<< why? - // because in 7.6, ProcessQuery calls GetPropertyCollection passing the sql which runs again - // but in v8 GetPropertyCollection has been refactored to use WhereIn() instead of a subquery - // as in most cases we won't get more than 2000 items -> nothing to do - // - 7f905bc on 01/26 fixes a "nasty" issue with reading properties - // - f192f24 on 01/31 fixes paged queries getting *all* property data - // - 86b2dac on 01/31 introduces the whole PagingQuery thing - // - 32d757b on 01/31 introduces the QueryType.Single vs .Many difference - // - af287c3 on 02/16 improves corruption handling for content (published/newest) - // - (more) on 02/22-27 fixes various minor issues - // - // so we want - // - to deal with the number of queries and getting *all* property data and outer joins - // - to deal with published/newest corruption - // - // but getting property data was already optimized, and we prob don't need the two queries thing - // so... doing our best but not really merging v7 - // see also Content/Member/Media repositories - - protected IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, - Func, IEnumerable> mapper, - string orderBy, Direction orderDirection, bool orderBySystemField, string table, - Sql filterSql = null) + protected IEnumerable GetPagedResultsByQuery(IQuery query, + long pageIndex, int pageSize, out long totalRecords, + Func, IEnumerable> mapDtos, + string orderBy, Direction orderDirection, bool orderBySystemField, + Sql filterSql = null) // fixme filter is different on v7? { if (orderBy == null) throw new ArgumentNullException(nameof(orderBy)); @@ -419,14 +379,14 @@ namespace Umbraco.Core.Persistence.Repositories var sqlNodeIds = new SqlTranslator(GetBaseQuery(QueryType.Many), query).Translate(); // sort and filter - sqlNodeIds = PrepareSqlForPagedResults(sqlNodeIds, filterSql, orderBy, orderDirection, orderBySystemField, table); + sqlNodeIds = PrepareSqlForPagedResults(sqlNodeIds, filterSql, orderBy, orderDirection, orderBySystemField); // get a page of DTOs and the total count var pagedResult = Database.Page(pageIndex + 1, pageSize, sqlNodeIds); totalRecords = Convert.ToInt32(pagedResult.TotalItems); // map the DTOs and return - return mapper(pagedResult.Items); + return mapDtos(pagedResult.Items); } protected IDictionary GetPropertyCollections(List> temps) @@ -486,7 +446,7 @@ namespace Umbraco.Core.Persistence.Repositories var indexedPropertyDataDtos = new Dictionary>(); foreach (var dto in allPropertyDataDtos) { - var version = dto.VersionId.Value; + var version = dto.VersionId; if (indexedPropertyDataDtos.TryGetValue(version, out var list) == false) indexedPropertyDataDtos[version] = list = new List(); list.Add(dto); @@ -1188,5 +1148,49 @@ ORDER BY nodeId, versionId, propertytypeid */ #endregion + + #region Utilities + + protected virtual string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) + { + var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.EnsureUniqueNodeName", tsql => tsql + .Select(x => x.NodeId, x => x.Text) + .From() + .Where(x => x.NodeObjectType == SqlTemplate.ArgValue("nodeObjectType") && x.ParentId == SqlTemplate.ArgValue("parentId"))); + + var sql = template.Sql(NodeObjectTypeId, parentId); + var names = Database.Fetch(sql); + + return SimilarNodeName.GetUniqueName(names, id, nodeName); + } + + protected virtual int GetNewChildSortOrder(int parentId, int first) + { + var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetSortOrder", tsql => + tsql.Select($"COALESCE(MAX(sortOrder),{first - 1})").From().Where(x => x.ParentId == SqlTemplate.ArgValue("parentId") && x.NodeObjectType == NodeObjectTypeId) + ); + + return Database.ExecuteScalar(template.Sql(new { parentId })) + 1; + } + + protected virtual NodeDto GetParentNodeDto(int parentId) + { + var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetParentNode", tsql => + tsql.Select().From().Where(x => x.NodeId == SqlTemplate.ArgValue("parentId")) + ); + + return Database.Fetch(template.Sql(parentId)).First(); + } + + protected virtual int GetReservedId(Guid uniqueId) + { + var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.GetReservedId", tsql => + tsql.Select(x => x.NodeId).From().Where(x => x.UniqueId == SqlTemplate.ArgValue("uniqueId") && x.NodeObjectType == Constants.ObjectTypes.IdReservation) + ); + var id = Database.ExecuteScalar(template.Sql(new { uniqueId = uniqueId })); + return id ?? 0; + } + + #endregion } } diff --git a/src/Umbraco.Core/Persistence/Repositories/XsltFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/XsltFileRepository.cs index 470b44fb0e..3993d87a6d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/XsltFileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/XsltFileRepository.cs @@ -38,8 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories VirtualPath = FileSystem.GetUrl(path) }; - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 + // reset dirty initial properties (U4-1946) xsltFile.ResetDirtyProperties(false); return xsltFile; diff --git a/src/Umbraco.Core/Persistence/SqlTemplate.cs b/src/Umbraco.Core/Persistence/SqlTemplate.cs index 3dc3b7389f..e933ac7947 100644 --- a/src/Umbraco.Core/Persistence/SqlTemplate.cs +++ b/src/Umbraco.Core/Persistence/SqlTemplate.cs @@ -10,16 +10,32 @@ namespace Umbraco.Core.Persistence { private readonly ISqlContext _sqlContext; private readonly string _sql; - private readonly Dictionary _args; + private readonly Dictionary _args; + + // these are created in PocoToSqlExpressionVisitor + internal class TemplateArg + { + public TemplateArg(string name) + { + Name = name; + } + + public string Name { get; } + + public override string ToString() + { + return "@" + Name; + } + } internal SqlTemplate(ISqlContext sqlContext, string sql, object[] args) { _sqlContext = sqlContext; _sql = sql; if (args.Length > 0) - _args = new Dictionary(); + _args = new Dictionary(); for (var i = 0; i < args.Length; i++) - _args[i] = args[i].ToString(); + _args[i] = args[i]; } public Sql Sql() @@ -27,7 +43,7 @@ namespace Umbraco.Core.Persistence return new Sql(_sqlContext, _sql); } - // must pass the args in the proper order, faster + // must pass the args, all of them, in the proper order, faster public Sql Sql(params object[] args) { // if the type is an "unspeakable name" it is an anonymous compiler-generated object @@ -47,7 +63,7 @@ namespace Umbraco.Core.Persistence return new Sql(_sqlContext, isBuilt, _sql, args); } - // can pass named args, slower + // can pass named args, not necessary all of them, slower // so, not much different from what Where(...) does (ie reflection) public Sql SqlNamed(object nargs) { @@ -56,10 +72,19 @@ namespace Umbraco.Core.Persistence var properties = nargs.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(nargs)); for (var i = 0; i < _args.Count; i++) { - if (!properties.TryGetValue(_args[i], out var value)) - throw new InvalidOperationException($"Missing argument \"{_args[i]}\"."); + object value; + if (_args[i] is TemplateArg templateArg) + { + if (!properties.TryGetValue(templateArg.Name, out value)) + throw new InvalidOperationException($"Missing argument \"{templateArg.Name}\"."); + properties.Remove(templateArg.Name); + } + else + { + value = _args[i]; + } + args[i] = value; - properties.Remove(_args[i]); // if value is enumerable then we'll need to expand arguments if (value is IEnumerable) @@ -72,15 +97,14 @@ namespace Umbraco.Core.Persistence internal void WriteToConsole() { - new Sql(_sqlContext, _sql, _args.Values.Cast().ToArray()).WriteToConsole(); + new Sql(_sqlContext, _sql, _args.Values.ToArray()).WriteToConsole(); } - public static T Arg(string name) - { - return default (T); - } + public static object Arg(string name) => new TemplateArg(name); - public static IEnumerable ArgIn(string name) + public static T ArgValue(string name) => default; + + public static IEnumerable ArgValueIn(string name) { // don't return an empty enumerable, as it breaks NPoco // fixme - should we cache these arrays? diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 12343621af..ee3e5d7752 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -460,7 +460,7 @@ namespace Umbraco.Core.Services uow.ReadLock(Constants.Locks.ContentTree); var repository = uow.CreateRepository(); var query = Query().Where(x => x.ContentTypeId == id); - return repository.GetByPublishedVersion(query); + return repository.GetByQuery(query); } } @@ -845,7 +845,7 @@ namespace Umbraco.Core.Services { uow.ReadLock(Constants.Locks.ContentTree); var repository = uow.CreateRepository(); - return repository.GetByPublishedVersion(QueryNotTrashed); + return repository.GetByQuery(QueryNotTrashed); } } @@ -1041,11 +1041,6 @@ namespace Umbraco.Core.Services content.CreatorId = userId; content.WriterId = userId; - // saving the Published version => indicate we are .Saving - // saving the Unpublished version => remains .Unpublished - if (content.Published) - content.ChangePublishedState(PublishedState.Saving); - repository.AddOrUpdate(content); if (raiseEvents) @@ -1109,11 +1104,6 @@ namespace Umbraco.Core.Services content.CreatorId = userId; content.WriterId = userId; - // saving the Published version => indicate we are .Saving - // saving the Unpublished version => remains .Unpublished - if (content.Published) - content.ChangePublishedState(PublishedState.Saving); - repository.AddOrUpdate(content); } @@ -1594,7 +1584,7 @@ namespace Umbraco.Core.Services { // however, it had been masked when being trashed, so there's no need for // any special event here - just change its state - content.ChangePublishedState(PublishedState.Unpublishing); + ((Content) content).PublishedState = PublishedState.Unpublishing; } PerformMoveLocked(repository, content, parentId, parent, userId, moves, trashed); @@ -1766,8 +1756,9 @@ namespace Umbraco.Core.Services // a copy is .Saving and will be .Unpublished // update the create author and last edit author - if (copy.Published) - copy.ChangePublishedState(PublishedState.Saving); + // fixme - not like this! + //if (copy.Published) + // copy.ChangePublishedState(PublishedState.Unpublished); copy.CreatorId = userId; copy.WriterId = userId; @@ -1806,8 +1797,9 @@ namespace Umbraco.Core.Services // a copy is .Saving and will be .Unpublished // update the create author and last edit author - if (descendantCopy.Published) - descendantCopy.ChangePublishedState(PublishedState.Saving); + // fixme - not like this! + //if (descendantCopy.Published) + // descendantCopy.ChangePublishedState(PublishedState.Unpublished); descendantCopy.CreatorId = userId; descendantCopy.WriterId = userId; @@ -1901,7 +1893,8 @@ namespace Umbraco.Core.Services var repository = uow.CreateRepository(); // a rolled back version is .Saving and will be .Unpublished - content.ChangePublishedState(PublishedState.Saving); + // fixme - not like this! + //content.ChangePublishedState(PublishedState.Unpublished); repository.AddOrUpdate(content); @@ -2014,7 +2007,7 @@ namespace Umbraco.Core.Services { var pathMatch = content.Path + ","; var query = Query().Where(x => x.Id != content.Id && x.Path.StartsWith(pathMatch) /*&& x.Trashed == false*/); - var contents = repository.GetByPublishedVersion(query); + var contents = repository.GetByQuery(query); // beware! contents contains all published version below content // including those that are not directly published because below an unpublished content @@ -2463,7 +2456,7 @@ namespace Umbraco.Core.Services return attempt; // change state to publishing - content.ChangePublishedState(PublishedState.Publishing); + ((Content) content).PublishedState = PublishedState.Publishing; Logger.Info($"Content '{content.Name}' with Id '{content.Id}' has been published."); @@ -2601,7 +2594,7 @@ namespace Umbraco.Core.Services // version is published or unpublished, but content is published // change state to unpublishing - content.ChangePublishedState(PublishedState.Unpublishing); + ((Content) content).PublishedState = PublishedState.Unpublishing; Logger.Info($"Content '{content.Name}' with Id '{content.Id}' has been unpublished."); diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 752e6d9de9..f08acbe74d 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -761,7 +761,6 @@ namespace Umbraco.Core return stringToConvert.GenerateHash("SHA1"); } - /// Generate a hash of a string based on the hashType passed in /// /// Referrs to itself @@ -950,6 +949,18 @@ namespace Umbraco.Core return input.Replace("\r", "").Replace("\n", ""); } + /// + /// Converts to single line by replacing line breaks with spaces. + /// + public static string ToSingleLine(this string text) + { + if (string.IsNullOrEmpty(text)) return text; + text = text.Replace("\r\n", " "); // remove CRLF + text = text.Replace("\r", " "); // remove CR + text = text.Replace("\n", " "); // remove LF + return text; + } + public static string OrIfNullOrWhiteSpace(this string input, string alternative) { return !string.IsNullOrWhiteSpace(input) diff --git a/src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs b/src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs index bc1bc4c01c..499b058c72 100644 --- a/src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs +++ b/src/Umbraco.Tests.Benchmarks/SqlTemplatesBenchmark.cs @@ -70,7 +70,7 @@ namespace Umbraco.Tests.Benchmarks var template = SqlTemplates.Get("test", s => s .Select() .From() - .Where(x => x.Name == SqlTemplate.Arg("name"))); + .Where(x => x.Name == SqlTemplate.ArgValue("name"))); var sql = template.Sql(new { name = "yada" }); diff --git a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs index f308466f4f..96e2857f6c 100644 --- a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs +++ b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs @@ -10,467 +10,6 @@ namespace Umbraco.Tests.Models [TestFixture] public class ContentExtensionsTests : UmbracoTestBase { - #region RequiresSaving - - // when Published... - - [Test] - public void RequireSaving_PublishedAndThatsAll_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_PublishedAndSavingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsFalse(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_PublishedAndSavingAndUserPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.Properties.First().SetValue("hello world"); // change data - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_PublishedAndSavingAndContentPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ReleaseDate = DateTime.Now; // change data - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_PublishedAndUnpublishing_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_PublishedAndPublishingAndNothingChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Publishing); // publishing - - Assert.IsTrue(content.RequiresSaving()); - } - - // when Unpublished... - - [Test] - public void RequireSaving_UnpublishedAndSavingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - Assert.IsFalse(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_UnpublishedAndSavingAndUserPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.Properties.First().SetValue("hello world"); // change data - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_UnpublishedAndSavingAndContentPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ReleaseDate = DateTime.Now; // change data - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_UnpublishedAndPublishing_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Publishing); // publishing - - Assert.IsTrue(content.RequiresSaving()); - } - - [Test] - public void RequireSaving_When_UnpublishedAndUnpublishingAndNothingChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - - Assert.IsTrue(content.RequiresSaving()); - } - - #endregion - - #region RequiresNewVersion - - // when language... - - [Test] - public void RequireNewVersion_LanguageChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.Language = "en-AU"; - Assert.IsTrue(content.RequiresNewVersion()); - } - - // when Published... - - [Test] - public void RequireNewVersion_PublishedAndThatsAll_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndPublishingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Publishing); - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndPublishdingAndUserPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.Properties.First().SetValue("hello world"); // change data - content.ChangePublishedState(PublishedState.Publishing); - - Assert.IsTrue(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndPublishdingAndContentPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ReleaseDate = DateTime.Now; // change content property - content.ChangePublishedState(PublishedState.Publishing); - - Assert.IsTrue(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndSavingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndSavingAndUserPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.Properties.First().SetValue("hello world"); // change data - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsTrue(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndSavingAndContentPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ReleaseDate = DateTime.Now; // change content property - content.ChangePublishedState(PublishedState.Saving); // saving - - Assert.IsTrue(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_PublishedAndUnpublishing_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - - Assert.IsTrue(content.RequiresNewVersion()); - } - - // when Unpublished... - - [Test] - public void RequireNewVersion_UnpublishedAndSavingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] // debatable - public void RequireNewVersion_UnpublishedAndSavingAndUserPropertyChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.Properties.First().SetValue("hello world"); // change user property - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_UnpublishedAndSavingAndContentPropertyChanged_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ReleaseDate = DateTime.Now; // change content property - - Assert.IsTrue(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_UnpublishedAndPublishingAndNothingChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Publishing); - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_UnpublishedAndPublishingAndUserPropertyChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.Properties.First().SetValue("hello world"); // change user property - content.ChangePublishedState(PublishedState.Publishing); // publishing - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_UnpublishedAndPublishingAndContentPropertyChanged_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ReleaseDate = DateTime.Now; // change content property - content.ChangePublishedState(PublishedState.Publishing); // publishing - - Assert.IsFalse(content.RequiresNewVersion()); - } - - [Test] - public void RequireNewVersion_UnpublishedAndUnpublishing_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - - Assert.IsFalse(content.RequiresNewVersion()); - } - - #endregion - - #region ClearPublishedFlag - - [Test] - public void ClearPublishedFlag_UnpublishedAndPublishing_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Publishing); - Assert.IsTrue(content.RequiresClearPublishedFlag()); - } - - [Test] - public void ClearPublishedFlag_UnpublishedAndUnpublishing_Should() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - does not "change it" - Assert.IsTrue(content.RequiresClearPublishedFlag()); - } - - [Test] - public void ClearPublishedFlag_UnpublishedAndSaving_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ResetDirtyProperties(false); - - content.ChangePublishedState(PublishedState.Saving); // saving - Assert.IsFalse(content.RequiresClearPublishedFlag()); - } - - [Test] - public void ClearPublishedFlag_PublishedAndSaving_ShouldNot() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Saving); // saving - Assert.IsFalse(content.RequiresClearPublishedFlag()); - } - - [Test] - public void ClearPublishedFlag_PublishedAndUnpublishing_Not() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - Assert.IsTrue(content.RequiresClearPublishedFlag()); - } - - [Test] - public void ClearPublishedFlag_When_PublishedAndPublishing_Not() - { - var contentType = MockedContentTypes.CreateTextpageContentType(); - var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - - content.ChangePublishedState(PublishedState.Publishing); - content.ResetDirtyProperties(false); // => .Published - - content.ChangePublishedState(PublishedState.Publishing); // publishing - does not "change it" - Assert.IsTrue(content.RequiresClearPublishedFlag()); - } - - #endregion - #region Others [Test] @@ -479,9 +18,11 @@ namespace Umbraco.Tests.Models var contentType = MockedContentTypes.CreateTextpageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - content.ChangePublishedState(PublishedState.Saving); // saved - content.ResetDirtyProperties(false); // reset to .Unpublished + content.PublishedState = PublishedState.Publishing; + Assert.IsFalse(content.Published); + content.ResetDirtyProperties(false); // resets Assert.AreEqual(PublishedState.Unpublished, content.PublishedState); + Assert.IsFalse(content.Published); } [Test] diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 398845833c..56febe024d 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -203,7 +203,7 @@ namespace Umbraco.Tests.Models content.Level = 3; content.Path = "-1,4,10"; content.ReleaseDate = DateTime.Now; - content.ChangePublishedState(PublishedState.Published); + //content.ChangePublishedState(PublishedState.Published); content.SortOrder = 5; content.Template = new Template((string) "Test Template", (string) "testTemplate") { @@ -250,7 +250,7 @@ namespace Umbraco.Tests.Models // should not try to clone something that's not Published or Unpublished // (and in fact it will not work) // but we cannot directly set the state to Published - hence this trick - content.ChangePublishedState(PublishedState.Publishing); + //content.ChangePublishedState(PublishedState.Publishing); content.ResetDirtyProperties(false); // => .Published var i = 200; @@ -373,7 +373,7 @@ namespace Umbraco.Tests.Models content.Level = 3; content.Path = "-1,4,10"; content.ReleaseDate = DateTime.Now; - content.ChangePublishedState(PublishedState.Publishing); + //content.ChangePublishedState(PublishedState.Publishing); content.SortOrder = 5; content.Template = new Template((string) "Test Template", (string) "testTemplate") { @@ -513,7 +513,7 @@ namespace Umbraco.Tests.Models }; contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType); var newProperty = new Property(propertyType); - newProperty.SetValue("This is a subtitle text"); + newProperty.SetValue("This is a subtitle Test"); content.Properties.Add(newProperty); // Assert @@ -647,18 +647,34 @@ namespace Umbraco.Tests.Models [Test] public void Can_Verify_Content_Is_Published() { - // Arrange var contentType = MockedContentTypes.CreateTextpageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); - // Act content.ResetDirtyProperties(); - content.ChangePublishedState(PublishedState.Publishing); + content.PublishedState = PublishedState.Publishing; - // Assert - Assert.That(content.IsPropertyDirty("Published"), Is.True); - Assert.That(content.Published, Is.True); - Assert.That(content.IsPropertyDirty("Name"), Is.False); + Assert.IsFalse(content.IsPropertyDirty("Published")); + Assert.IsFalse(content.Published); + Assert.IsFalse(content.IsPropertyDirty("Name")); + Assert.AreEqual(PublishedState.Publishing, content.PublishedState); + + // the repo would do + content.Published = true; + + // and then + Assert.IsTrue(content.IsPropertyDirty("Published")); + Assert.IsTrue(content.Published); + Assert.IsFalse(content.IsPropertyDirty("Name")); + Assert.AreEqual(PublishedState.Published, content.PublishedState); + + // and before returning, + content.ResetDirtyProperties(); + + // and then + Assert.IsFalse(content.IsPropertyDirty("Published")); + Assert.IsTrue(content.Published); + Assert.IsFalse(content.IsPropertyDirty("Name")); + Assert.AreEqual(PublishedState.Published, content.PublishedState); } [Test] diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index 3b8fae17f3..aa870675ea 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -19,6 +19,7 @@ namespace Umbraco.Tests.Models { // Arrange var contentType = MockedContentTypes.CreateTextpageContentType(); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); var content = MockedContent.CreateTextpageContent(contentType, "Root Home", -1); diff --git a/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs index d3b4997751..50ddfd0cac 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/ContentMapperTest.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Persistence.Mappers string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Published"); // Assert - Assert.That(column, Is.EqualTo("[cmsDocument].[published]")); + Assert.That(column, Is.EqualTo("[uDocument].[published]")); } [Test] @@ -49,7 +49,7 @@ namespace Umbraco.Tests.Persistence.Mappers string column = new ContentMapper().Map(new SqlCeSyntaxProvider(), "Version"); // Assert - Assert.That(column, Is.EqualTo("[cmsContentVersion].[VersionId]")); + Assert.That(column, Is.EqualTo("[uContentVersion].[versionId]")); } } } diff --git a/src/Umbraco.Tests/Persistence/Mappers/DataTypeDefinitionMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/DataTypeDefinitionMapperTest.cs index 261f0d6621..f015874ce0 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/DataTypeDefinitionMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/DataTypeDefinitionMapperTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Mappers string column = new DataTypeDefinitionMapper().Map(new SqlCeSyntaxProvider(), "Key"); // Assert - Assert.That(column, Is.EqualTo("[umbracoNode].[uniqueID]")); + Assert.That(column, Is.EqualTo("[umbracoNode].[uniqueId]")); } [Test] diff --git a/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs b/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs index 289f8edba4..8183a05c8c 100644 --- a/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs +++ b/src/Umbraco.Tests/Persistence/Mappers/MediaMapperTest.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence.Mappers string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "UpdateDate"); // Assert - Assert.That(column, Is.EqualTo("[cmsContentVersion].[VersionDate]")); + Assert.That(column, Is.EqualTo("[uContentVersion].[versionDate]")); } [Test] @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Persistence.Mappers string column = new MediaMapper().Map(new SqlCeSyntaxProvider(), "Version"); // Assert - Assert.That(column, Is.EqualTo("[cmsContentVersion].[VersionId]")); + Assert.That(column, Is.EqualTo("[uContentVersion].[versionId]")); } } } diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTemplateTests.cs b/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTemplateTests.cs index f0ce4cf021..6ffab2d5fb 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTemplateTests.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTemplateTests.cs @@ -25,7 +25,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests var sql = sqlTemplates.Get("xxx", s => s .SelectAll() .From("zbThing1") - .Where("id=@id", new { id = "id" })).Sql(new { id = 1 }); + .Where("id=@id", new { id = SqlTemplate.Arg("id") })).Sql(new { id = 1 }); sql.WriteToConsole(); @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests const string sqlBase = "SELECT [zbThing1].[id] AS [Id], [zbThing1].[name] AS [Name] FROM [zbThing1] WHERE "; var template = sqlTemplates.Get("sql1", s => s.Select().From() - .Where(x => x.Name == "value")); + .Where(x => x.Name == SqlTemplate.ArgValue("value"))); var sql = template.Sql("foo"); Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf()); @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests Assert.AreEqual(123, sql.Arguments[0]); template = sqlTemplates.Get("sql2", s => s.Select().From() - .Where(x => x.Name == "value")); + .Where(x => x.Name == SqlTemplate.ArgValue("value"))); sql = template.Sql(new { value = "foo" }); Assert.AreEqual(sqlBase + "(([zbThing1].[name] = @0))", sql.SQL.NoCrLf()); @@ -97,7 +97,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests // so we have to explicitely create the argument template = sqlTemplates.Get("sql4", s => s.Select().From() - .Where(x => x.Id == SqlTemplate.Arg("i"))); + .Where(x => x.Id == SqlTemplate.ArgValue("i"))); sql = template.Sql("foo"); Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0))", sql.SQL.NoCrLf()); @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests // now with more arguments template = sqlTemplates.Get("sql4a", s => s.Select().From() - .Where(x => x.Id == SqlTemplate.Arg("i") && x.Name == SqlTemplate.Arg("name"))); + .Where(x => x.Id == SqlTemplate.ArgValue("i") && x.Name == SqlTemplate.ArgValue("name"))); sql = template.Sql(0, 1); Assert.AreEqual(sqlBase + "((([zbThing1].[id] = @0) AND ([zbThing1].[name] = @1)))", sql.SQL.NoCrLf()); Assert.AreEqual(2, sql.Arguments.Length); @@ -135,8 +135,8 @@ namespace Umbraco.Tests.Persistence.NPocoTests Assert.AreEqual(1, sql.Arguments[1]); template = sqlTemplates.Get("sql4b", s => s.Select().From() - .Where(x => x.Id == SqlTemplate.Arg("i")) - .Where(x => x.Name == SqlTemplate.Arg("name"))); + .Where(x => x.Id == SqlTemplate.ArgValue("i")) + .Where(x => x.Name == SqlTemplate.ArgValue("name"))); sql = template.Sql(0, 1); Assert.AreEqual(sqlBase + "(([zbThing1].[id] = @0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf()); Assert.AreEqual(2, sql.Arguments.Length); @@ -146,7 +146,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests // works, magic template = sqlTemplates.Get("sql5", s => s.Select().From() - .WhereIn(x => x.Id, SqlTemplate.ArgIn("i"))); + .WhereIn(x => x.Id, SqlTemplate.ArgValueIn("i"))); sql = template.Sql("foo"); Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0))", sql.SQL.NoCrLf()); @@ -161,8 +161,8 @@ namespace Umbraco.Tests.Persistence.NPocoTests Assert.AreEqual(3, sql.Arguments[2]); template = sqlTemplates.Get("sql5a", s => s.Select().From() - .WhereIn(x => x.Id, SqlTemplate.ArgIn("i")) - .Where(x => x.Name == SqlTemplate.Arg("name"))); + .WhereIn(x => x.Id, SqlTemplate.ArgValueIn("i")) + .Where(x => x.Name == SqlTemplate.ArgValue("name"))); sql = template.Sql("foo", "bar"); Assert.AreEqual(sqlBase + "([zbThing1].[id] IN (@0)) AND (([zbThing1].[name] = @1))", sql.SQL.NoCrLf()); @@ -187,8 +187,15 @@ namespace Umbraco.Tests.Persistence.NPocoTests template = sqlTemplates.Get("sql6", s => s.Select().From() // do NOT do this, this is NOT a visited expression //.Append(" AND whatever=@0", SqlTemplate.Arg("j")) - .Append("AND whatever=@0", "j") // instead, directly name the argument - .Append("AND whatever=@0", "k") // same + + // does not work anymore - due to proper TemplateArg + //// instead, directly name the argument + //.Append("AND whatever=@0", "j") + //.Append("AND whatever=@0", "k") + + // instead, explicitely create the argument + .Append("AND whatever=@0", SqlTemplate.Arg("j")) + .Append("AND whatever=@0", SqlTemplate.Arg("k")) ); sql = template.Sql(new { j = new[] { 1, 2, 3 }, k = "oops" }); diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTests.cs index f05948d9c8..599804fc2f 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTests.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/NPocoSqlTests.cs @@ -193,9 +193,9 @@ namespace Umbraco.Tests.Persistence.NPocoTests { var expected = Sql(); expected.SelectAll() - .From("[cmsDocumentVersion]") - .InnerJoin("[cmsContentVersion]") - .On("[cmsDocumentVersion].[id] = [cmsContentVersion].[id]"); + .From("[uDocumentVersion]") + .InnerJoin("[uContentVersion]") + .On("[uDocumentVersion].[id] = [uContentVersion].[id]"); var sql = Sql(); sql.SelectAll().From() diff --git a/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs deleted file mode 100644 index 87f034ee84..0000000000 --- a/src/Umbraco.Tests/Persistence/Querying/ContentRepositorySqlClausesTest.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Diagnostics; -using NPoco; -using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Persistence.Querying -{ - [TestFixture] - public class ContentRepositorySqlClausesTest : BaseUsingSqlCeSyntax - { - [Test] - public void Can_Verify_Base_Clause() - { - var NodeObjectType = Constants.ObjectTypes.Document; - - var expected = new Sql(); - expected.Select("*") - .From("[cmsDocument]") - .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") - .InnerJoin("[uContent]").On("[cmsContentVersion].[ContentId] = [uContent].[nodeId]") - .InnerJoin("[umbracoNode]").On("[uContent].[nodeId] = [umbracoNode].[id]") - .Where("([umbracoNode].[nodeObjectType] = @0)", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972")); - - var sql = Sql(); - sql.SelectAll() - .From() - // fixme DocumentDto does not have VersionId anymore - //.InnerJoin() - //.On(left => left.VersionId, right => right.VersionId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectType); - - Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); - - Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length); - for (var i = 0; i < expected.Arguments.Length; i++) - { - Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]); - } - - Debug.Print(sql.SQL); - } - - [Test] - public void Can_Verify_Base_Where_Clause() - { - var NodeObjectType = Constants.ObjectTypes.Document; - - var expected = Sql(); - expected.SelectAll() - .From("[cmsDocument]") - .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") - .InnerJoin("[uContent]").On("[cmsContentVersion].[ContentId] = [uContent].[nodeId]") - .InnerJoin("[umbracoNode]").On("[uContent].[nodeId] = [umbracoNode].[id]") - .Where("([umbracoNode].[nodeObjectType] = @0)", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972")) - .Where("([umbracoNode].[id] = @0)", 1050); - - var sql = Sql(); - sql.SelectAll() - .From() - // fixme DocumentDto does not have VersionId anymore - //.InnerJoin() - //.On(left => left.VersionId, right => right.VersionId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectType) - .Where(x => x.NodeId == 1050); - - Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); - - Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length); - for (int i = 0; i < expected.Arguments.Length; i++) - { - Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]); - } - - Debug.Print(sql.SQL); - } - - [Test] - public void Can_Verify_Base_Where_With_Version_Clause() - { - var NodeObjectType = Constants.ObjectTypes.Document; - var versionId = new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07"); - - var expected = new Sql(); - expected.Select("*") - .From("[cmsDocument]") - .InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]") - .InnerJoin("[uContent]").On("[cmsContentVersion].[ContentId] = [uContent].[nodeId]") - .InnerJoin("[umbracoNode]").On("[uContent].[nodeId] = [umbracoNode].[id]") - .Where("([umbracoNode].[nodeObjectType] = @0)", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972")) - .Where("([umbracoNode].[id] = @0)", 1050) - .Where("([cmsContentVersion].[VersionId] = @0)", new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07")) - .OrderBy("([cmsContentVersion].[VersionDate]) DESC"); - - var sql = Sql(); - sql.SelectAll() - .From() - // fixme DocumentDto does not have VersionId anymore - //.InnerJoin() - //.On(left => left.VersionId, right => right.VersionId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectType) - .Where(x => x.NodeId == 1050) - .Where(x => x.VersionId == versionId) - .OrderByDescending(x => x.VersionDate); - - Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); - Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length); - for (int i = 0; i < expected.Arguments.Length; i++) - { - Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]); - } - - Debug.Print(sql.SQL); - } - - [Test] - public void Can_Verify_Property_Collection_Query() - { - var versionId = new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07"); - var id = 1050; - - var expected = new Sql(); - expected.Select("*"); - expected.From("[" + Constants.DatabaseSchema.Tables.PropertyData + "]"); - expected.InnerJoin("[cmsPropertyType]").On("[" + Constants.DatabaseSchema.Tables.PropertyData + "].[propertytypeid] = [cmsPropertyType].[id]"); - expected.Where("([" + Constants.DatabaseSchema.Tables.PropertyData + "].[nodeId] = @0)", 1050); - expected.Where("([" + Constants.DatabaseSchema.Tables.PropertyData + "].[versionId] = @0)", new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07")); - - var sql = Sql(); - sql.SelectAll() - .From() - .InnerJoin() - .On(left => left.PropertyTypeId, right => right.Id) - .Where(x => x.NodeId == id) - .Where(x => x.VersionId == versionId); - - Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); - Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length); - for (int i = 0; i < expected.Arguments.Length; i++) - { - Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]); - } - - Debug.Print(sql.SQL); - } - } -} diff --git a/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs b/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs index 7fc6201bb9..f3263b711f 100644 --- a/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/ExpressionTests.cs @@ -113,7 +113,7 @@ namespace Umbraco.Tests.Persistence.Querying Debug.Print("Model to Sql ExpressionHelper: \n" + result); - Assert.AreEqual("([umbracoNode].[parentID] = @0)", result); + Assert.AreEqual("([umbracoNode].[parentId] = @0)", result); Assert.AreEqual(-1, modelToSqlExpressionHelper.GetSqlParameters()[0]); } @@ -169,7 +169,7 @@ namespace Umbraco.Tests.Persistence.Querying var sqlContext = new SqlContext(sqlSyntax, DatabaseType.MySQL, SqlContext.PocoDataFactory); Expression> predicate = user => user.Login.StartsWith("mydomain\\myuser"); - var modelToSqlExpressionHelper = new PocoToSqlExpressionVisitor(sqlContext); + var modelToSqlExpressionHelper = new PocoToSqlExpressionVisitor(sqlContext, null); var result = modelToSqlExpressionHelper.Visit(predicate); Debug.Print("Poco to Sql ExpressionHelper: \n" + result); diff --git a/src/Umbraco.Tests/Persistence/Querying/MediaRepositorySqlClausesTest.cs b/src/Umbraco.Tests/Persistence/Querying/MediaRepositorySqlClausesTest.cs index 23d3445cf4..0aa3ef2a1a 100644 --- a/src/Umbraco.Tests/Persistence/Querying/MediaRepositorySqlClausesTest.cs +++ b/src/Umbraco.Tests/Persistence/Querying/MediaRepositorySqlClausesTest.cs @@ -15,12 +15,12 @@ namespace Umbraco.Tests.Persistence.Querying [Test] public void Can_Verify_Base_Clause() { - var NodeObjectTypeId = Constants.ObjectTypes.Media; + var nodeObjectTypeId = Constants.ObjectTypes.Media; var expected = new Sql(); expected.Select("*") - .From("[cmsContentVersion]") - .InnerJoin("[uContent]").On("[cmsContentVersion].[ContentId] = [uContent].[nodeId]") + .From("[uContentVersion]") + .InnerJoin("[uContent]").On("[uContentVersion].[nodeId] = [uContent].[nodeId]") .InnerJoin("[umbracoNode]").On("[uContent].[nodeId] = [umbracoNode].[id]") .Where("([umbracoNode].[nodeObjectType] = @0)", new Guid("b796f64c-1f99-4ffb-b886-4bf4bc011a9c")); @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence.Querying .On(left => left.NodeId, right => right.NodeId) .InnerJoin() .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectTypeId); + .Where(x => x.NodeObjectType == nodeObjectTypeId); Assert.That(sql.SQL, Is.EqualTo(expected.SQL)); diff --git a/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs index ae63eaa1ea..d7cb7efaca 100644 --- a/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/QueryBuilderTests.cs @@ -54,7 +54,7 @@ namespace Umbraco.Tests.Persistence.Querying var result = translator.Translate(); var strResult = result.SQL; - string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (([umbracoNode].[parentID] = @0))"; + string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (([umbracoNode].[parentId] = @0))"; // Assert Assert.That(strResult, Is.Not.Empty); @@ -95,32 +95,24 @@ namespace Umbraco.Tests.Persistence.Querying [Test] public void Can_Build_PublishedDescendants_Query_For_IContent() { - // Arrange - var path = "-1,1046,1076,1089"; - var id = 1046; - var nodeObjectTypeId = Constants.ObjectTypes.Document; + const string path = "-1,1046,1076,1089"; + const int id = 1046; var sql = Sql(); sql.SelectAll() - .From() - // fixme DocumentDto does not have VersionId anymore - //.InnerJoin() - //.On(left => left.VersionId, right => right.VersionId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == nodeObjectTypeId); + .From(); // the actual SELECT really does not matter - var query = new Query(SqlContext).Where(x => x.Path.StartsWith(path) && x.Id != id && x.Published == true && x.Trashed == false); + var query = SqlContext.Query().Where(x => x.Path.StartsWith(path) && x.Id != id && x.Published && x.Trashed == false); - // Act var translator = new SqlTranslator(sql, query); var result = translator.Translate(); - var strResult = result.SQL; - // Assert - Debug.Print(strResult); + result.WriteToConsole(); + + Assert.AreEqual("-1,1046,1076,1089%", result.Arguments[0]); + Assert.AreEqual(1046, result.Arguments[1]); + Assert.AreEqual(true, result.Arguments[2]); + Assert.AreEqual(true, result.Arguments[3]); } } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 6b526c6ced..1cff14abde 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading; using System.Web; using Moq; using NUnit.Framework; @@ -71,10 +72,8 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Cache_Active_By_Int_And_Guid() + public void CacheActiveForIntsAndGuids() { - ContentTypeRepository contentTypeRepository; - var realCache = new CacheHelper( new ObjectCacheRuntimeCacheProvider(), new StaticCacheProvider(), @@ -84,25 +83,26 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - var repository = CreateRepository(unitOfWork, out contentTypeRepository, cacheHelper: realCache); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository, cacheHelper: realCache); var udb = (UmbracoDatabase) unitOfWork.Database; udb.EnableSqlCount = false; var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); - var content = MockedContent.CreateSimpleContent(contentType); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); + var content = MockedContent.CreateSimpleContent(contentType); repository.AddOrUpdate(content); unitOfWork.Complete(); udb.EnableSqlCount = true; //go get it, this should already be cached since the default repository key is the INT - var found = repository.Get(content.Id); + repository.Get(content.Id); Assert.AreEqual(0, udb.SqlCount); //retrieve again, this should use cache - found = repository.Get(content.Id); + repository.Get(content.Id); Assert.AreEqual(0, udb.SqlCount); //reset counter @@ -110,129 +110,170 @@ namespace Umbraco.Tests.Persistence.Repositories udb.EnableSqlCount = true; //now get by GUID, this won't be cached yet because the default repo key is not a GUID - found = repository.Get(content.Key); + repository.Get(content.Key); var sqlCount = udb.SqlCount; Assert.Greater(sqlCount, 0); //retrieve again, this should use cache now - found = repository.Get(content.Key); + repository.Get(content.Key); Assert.AreEqual(sqlCount, udb.SqlCount); } } [Test] - public void Get_Always_Returns_Latest_Version() + public void CreateVersions() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - var repository = CreateRepository(unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dataTypeDefinitionRepository); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository, out DataTypeDefinitionRepository _); - IContent content1; var versions = new List(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); - content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType); + ServiceContext.FileService.SaveTemplate(hasPropertiesContentType.DefaultTemplate); // else, FK violation on contentType! - //save version + IContent content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType); + + // save = create the initial version contentTypeRepository.AddOrUpdate(hasPropertiesContentType); repository.AddOrUpdate(content1); unitOfWork.Flush(); versions.Add(content1.Version); // the first version - //publish version - content1.ChangePublishedState(PublishedState.Publishing); + // publish = no new version (was not published) + content1.SetValue("title", "title"); + ((Content) content1).PublishValues(); + ((Content) content1).PublishedState = PublishedState.Publishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the first version got published, same id + versions.Add(content1.Version); // the same version - //change something and make a pending version - content1.Name = "new name"; - if (content1.Published) - content1.ChangePublishedState(PublishedState.Saving); + // no new version has been created + Assert.AreEqual(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); + + // misc checks + Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); + + // change something + // save = update the current (draft) version + content1.Name = "name-1"; + content1.SetValue("title", "title-1"); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); // the second version + versions.Add(content1.Version); // the same version - Assert.AreEqual(2, versions.Distinct().Count()); + // 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); - var content = repository.GetByQuery(unitOfWork.SqlContext.Query().Where(c => c.Id == content1.Id)).ToArray()[0]; - Assert.AreEqual(versions[2], content.Version); + // misc checks + Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); - content = repository.Get(content1.Id); - Assert.AreEqual(versions[2], content.Version); - - foreach (var version in versions) - { - content = repository.GetByVersion(version); - Assert.IsNotNull(content); - Assert.AreEqual(version, content.Version); - } - } - } - - [Test] - public void Deal_With_Corrupt_Duplicate_Newest_Published_Flags() - { - // Arrange - var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); - using (var unitOfWork = provider.CreateUnitOfWork()) - { - var repository = CreateRepository(unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dataTypeDefinitionRepository); - - var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); - IContent content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType); - - contentTypeRepository.AddOrUpdate(hasPropertiesContentType); + // unpublish = should create a new version + ((Content) content1).PublishedState = PublishedState.Unpublishing; repository.AddOrUpdate(content1); unitOfWork.Flush(); + versions.Add(content1.Version); // the new version - var versionDtos = new List(); + // a new version has been created + Assert.AreNotEqual(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); - //Now manually corrupt the data - var versions = new[] { Guid.NewGuid(), Guid.NewGuid() }; - for (var index = 0; index < versions.Length; index++) - { - var version = versions[index]; - var versionDate = DateTime.Now.AddMinutes(index); - var versionDto = new ContentVersionDto - { - NodeId = content1.Id, - VersionDate = versionDate, - VersionId = version - }; - unitOfWork.Database.Insert(versionDto); - versionDtos.Add(versionDto); - unitOfWork.Database.Insert(new DocumentDto - { - // fixme DocumentDto has changed! - //Newest = true, - NodeId = content1.Id, - Published = true, - //Text = content1.Name, - //VersionId = version, - WriterUserId = 0, - UpdateDate = versionDate, - //TemplateId = content1.Template == null || content1.Template.Id <= 0 ? null : (int?)content1.Template.Id - }); + // misc checks + Assert.AreEqual(false, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); - var content = repository.GetByQuery(unitOfWork.SqlContext.Query().Where(c => c.Id == content1.Id)).ToArray(); - Assert.AreEqual(1, content.Length); - Assert.AreEqual(content[0].Version, versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionId); - Assert.AreEqual(content[0].UpdateDate.ToString(CultureInfo.InvariantCulture), versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionDate.ToString(CultureInfo.InvariantCulture)); + // change something + // save = update the current (draft) version + content1.Name = "name-2"; + content1.SetValue("title", "title-2"); + repository.AddOrUpdate(content1); + unitOfWork.Flush(); + versions.Add(content1.Version); // the same version - var contentItem = repository.GetByVersion(content1.Version); - Assert.IsNotNull(contentItem); + // 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); - contentItem = repository.Get(content1.Id); - Assert.IsNotNull(contentItem); - Assert.AreEqual(contentItem.UpdateDate.ToString(CultureInfo.InvariantCulture), versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionDate.ToString(CultureInfo.InvariantCulture)); - Assert.AreEqual(contentItem.Version, versionDtos.Single(x => x.Id == versionDtos.Max(y => y.Id)).VersionId); + // misc checks + Assert.AreEqual(false, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); - var allVersions = repository.GetAllVersions(content[0].Id); - var allKnownVersions = versionDtos.Select(x => x.VersionId).Union(new[] { content1.Version }).ToArray(); - Assert.IsTrue(allKnownVersions.ContainsAll(allVersions.Select(x => x.Version))); - Assert.IsTrue(allVersions.Select(x => x.Version).ContainsAll(allKnownVersions)); - } + // publish = no new version (was not published) + ((Content) content1).PublishValues(); + ((Content) content1).PublishedState = PublishedState.Publishing; + repository.AddOrUpdate(content1); + unitOfWork.Flush(); + versions.Add(content1.Version); // 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(PublishedState.Published, ((Content) content1).PublishedState); + Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).Version); + + // misc checks + Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); + + // change something + // save = update the current (draft) version + content1.Name = "name-3"; + content1.SetValue("title", "title-3"); + //Thread.Sleep(2000); // force date change + repository.AddOrUpdate(content1); + unitOfWork.Flush(); + versions.Add(content1.Version); // 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); + + // misc checks + Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); + + // publish = new version (was published) + content1.Name = "name-4"; + content1.SetValue("title", "title-4"); + ((Content) content1).PublishValues(); + ((Content) content1).PublishedState = PublishedState.Publishing; + repository.AddOrUpdate(content1); + unitOfWork.Flush(); + versions.Add(content1.Version); // 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); + + // misc checks + Assert.AreEqual(true, unitOfWork.Database.ExecuteScalar("SELECT published FROM uDocument WHERE nodeId=@id", new { id = content1.Id })); + Console.WriteLine(unitOfWork.Database.ExecuteScalar("SELECT updateDate FROM uContent WHERE nodeId=@id", new { id = content1.Id })); + + // get older version + var content = repository.GetByVersion(versions[versions.Count - 2]); + Assert.IsNotNull(content.Version); + Assert.AreEqual(versions[versions.Count - 2], content.Version); + Assert.AreEqual("name-4", content1.Name); + Assert.AreEqual("title-4", content1.GetValue("title")); + Assert.AreEqual("name-3", content.Name); + Assert.AreEqual("title-3", content.GetValue("title")); + + // get all versions - most recent first + var allVersions = repository.GetAllVersions(content1.Id).ToArray(); + 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); } } @@ -245,22 +286,20 @@ namespace Umbraco.Tests.Persistence.Repositories /// To test, we have 3 content items, the first has properties, the second doesn't and the third does. /// [Test] - public void Property_Data_Assigned_Correctly() + public void PropertyDataAssignedCorrectly() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); - var allContent = new List(); using (var unitOfWork = provider.CreateUnitOfWork()) { - var repository = CreateRepository(unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dataTypeDefinitionRepository); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository, out DataTypeDefinitionRepository _); var emptyContentType = MockedContentTypes.CreateBasicContentType(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); + ServiceContext.FileService.SaveTemplate(hasPropertiesContentType.DefaultTemplate); // else, FK violation on contentType! var content1 = MockedContent.CreateSimpleContent(hasPropertiesContentType); var content2 = MockedContent.CreateBasicContent(emptyContentType); var content3 = MockedContent.CreateSimpleContent(hasPropertiesContentType); - // Act contentTypeRepository.AddOrUpdate(emptyContentType); contentTypeRepository.AddOrUpdate(hasPropertiesContentType); repository.AddOrUpdate(content1); @@ -268,26 +307,22 @@ namespace Umbraco.Tests.Persistence.Repositories repository.AddOrUpdate(content3); unitOfWork.Flush(); - allContent.Add(content1); - allContent.Add(content2); - allContent.Add(content3); - - //this will cause the GetPropertyCollection to execute and we need to ensure that + // this will cause the GetPropertyCollection to execute and we need to ensure that // all of the properties and property types are all correct - var result = repository.GetAll(allContent.Select(x => x.Id).ToArray()).ToArray(); + var result = repository.GetAll(content1.Id, content2.Id, content3.Id).ToArray(); + var n1 = result[0]; + var n2 = result[1]; + var n3 = result[2]; - foreach (var content in result) - { - foreach (var contentProperty in content.Properties) - { - //prior to the fix, the 2nd document iteration in the GetPropertyCollection would have caused - //the enumerator to move forward past the first property of the 3rd document which would have - //ended up not assiging a property to the 3rd document. This would have ended up with the 3rd - //document still having 3 properties but the last one would not have been assigned an identity - //because the property data would not have been assigned. - Assert.IsTrue(contentProperty.HasIdentity); - } - } + Assert.AreEqual(content1.Id, n1.Id); + Assert.AreEqual(content2.Id, n2.Id); + Assert.AreEqual(content3.Id, n3.Id); + + // compare everything including properties and their values + // this ensures that they have been properly retrieved + TestHelper.AssertPropertyValuesAreEqual(content1, n1); + TestHelper.AssertPropertyValuesAreEqual(content2, n2); + TestHelper.AssertPropertyValuesAreEqual(content3, n3); } } @@ -301,17 +336,13 @@ namespace Umbraco.Tests.Persistence.Repositories /// sending the non-transformed data directly on to publishing. /// [Test] - public void Property_Values_With_Special_DatabaseTypes_Are_Equal_Before_And_After_Being_Persisted() + public void PropertyValuesWithSpecialTypes() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - DataTypeDefinitionRepository dataTypeDefinitionRepository; + var repository = CreateRepository(unitOfWork, out var contentTypeRepository, out DataTypeDefinitionRepository dataTypeDefinitionRepository); - var repository = CreateRepository(unitOfWork, out contentTypeRepository, out dataTypeDefinitionRepository); - - // Setup var dtd = new DataTypeDefinition(-1, Constants.PropertyEditors.DecimalAlias) { Name = "test", DatabaseType = DataTypeDatabaseType.Decimal }; dataTypeDefinitionRepository.AddOrUpdate(dtd); unitOfWork.Complete(); @@ -332,14 +363,12 @@ namespace Umbraco.Tests.Persistence.Repositories contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Complete(); - // Int and decimal values are passed in as strings as they would be from the backoffice UI + // int and decimal values are passed in as strings as they would be from the backoffice UI var textpage = MockedContent.CreateSimpleContentWithSpecialDatabaseTypes(contentType, "test@umbraco.org", -1, "100", "150", dateValue); - // Act repository.AddOrUpdate(textpage); unitOfWork.Complete(); - // Assert Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); @@ -355,49 +384,41 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Add_On_ContentRepository() + public void SaveContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository); + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! IContent textpage = MockedContent.CreateSimpleContent(contentType); - // Act contentTypeRepository.AddOrUpdate(contentType); repository.AddOrUpdate(textpage); unitOfWork.Complete(); - // Assert Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); } } [Test] - public void Can_Perform_Add_With_Default_Template() + public void SaveContentWithDefaultTemplate() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - TemplateRepository templateRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository, out templateRepository); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository, out TemplateRepository templateRepository); var template = new Template("hello", "hello"); templateRepository.AddOrUpdate(template); unitOfWork.Flush(); - ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); - contentType.AllowedTemplates = Enumerable.Empty(); // because CreateSimple... assigns one + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); + contentType.AllowedTemplates = Enumerable.Empty(); // because CreateSimpleContentType assigns one already contentType.SetDefaultTemplate(template); - Content textpage = MockedContent.CreateSimpleContent(contentType); - - // Act + var textpage = MockedContent.CreateSimpleContent(contentType); contentTypeRepository.AddOrUpdate(contentType); repository.AddOrUpdate(textpage); @@ -405,40 +426,37 @@ namespace Umbraco.Tests.Persistence.Repositories var fetched = repository.Get(textpage.Id); - // Assert - Assert.That(textpage.Template, Is.Not.Null); - Assert.That(textpage.Template, Is.EqualTo(contentType.DefaultTemplate)); + Assert.NotNull(textpage.Template); + Assert.AreEqual(textpage.Template, contentType.DefaultTemplate); unitOfWork.Complete(); - TestHelper.AssertAllPropertyValuesAreEquals(textpage, fetched, "yyyy-MM-dd HH:mm:ss"); + TestHelper.AssertPropertyValuesAreEqual(textpage, fetched, "yyyy-MM-dd HH:mm:ss"); } } //Covers issue U4-2791 and U4-2607 [Test] - public void Can_Save_Content_With_AtSign_In_Name_On_ContentRepository() + public void SaveContentWithAtSignInName() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); - var textpage = MockedContent.CreateSimpleContent(contentType, "test@umbraco.org", -1); - var anotherTextpage = MockedContent.CreateSimpleContent(contentType, "@lightgiants", -1); - - // Act + var textpage = MockedContent.CreateSimpleContent(contentType, "test@umbraco.org"); + var anotherTextpage = MockedContent.CreateSimpleContent(contentType, "@lightgiants"); repository.AddOrUpdate(textpage); repository.AddOrUpdate(anotherTextpage); unitOfWork.Flush(); - // Assert + Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); @@ -453,27 +471,24 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Multiple_Adds_On_ContentRepository() + public void SaveContentMultiple() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); - Content textpage = MockedContent.CreateSimpleContent(contentType); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository); + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! + var textpage = MockedContent.CreateSimpleContent(contentType); - // Act contentTypeRepository.AddOrUpdate(contentType); repository.AddOrUpdate(textpage); unitOfWork.Flush(); - Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); + var subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); repository.AddOrUpdate(subpage); unitOfWork.Flush(); - // Assert Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); Assert.That(subpage.HasIdentity, Is.True); @@ -484,83 +499,80 @@ namespace Umbraco.Tests.Persistence.Repositories [Test] - public void Can_Verify_Fresh_Entity_Is_Not_Dirty() + public void GetContentIsNotDirty() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var content = repository.Get(NodeDto.NodeIdSeed + 3); - bool dirty = ((Content)content).IsDirty(); + var repository = CreateRepository(unitOfWork, out _); + + var content = repository.Get(NodeDto.NodeIdSeed + 3); + var dirty = ((Content) content).IsDirty(); - // Assert Assert.That(dirty, Is.False); } } [Test] - public void Can_Perform_Update_On_ContentRepository() + public void UpdateContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var content = repository.Get(NodeDto.NodeIdSeed + 2); content.Name = "About 2"; repository.AddOrUpdate(content); unitOfWork.Flush(); var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); - // Assert - Assert.That(updatedContent.Id, Is.EqualTo(content.Id)); - Assert.That(updatedContent.Name, Is.EqualTo(content.Name)); - } + Assert.AreEqual(content.Id, updatedContent.Id); + Assert.AreEqual(content.Name, updatedContent.Name); + Assert.AreEqual(content.Version, updatedContent.Version); + Assert.AreEqual(content.GetValue("title"), "Welcome to our Home page"); + content.SetValue("title", "toot"); + repository.AddOrUpdate(content); + unitOfWork.Flush(); + updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); + + Assert.AreEqual("toot", updatedContent.GetValue("title")); + Assert.AreEqual(content.Version, updatedContent.Version); + } } [Test] - public void Can_Update_With_Null_Template() + public void UpdateContentWithNullTemplate() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var content = repository.Get(NodeDto.NodeIdSeed + 2); content.Template = null; repository.AddOrUpdate(content); unitOfWork.Flush(); var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); - // Assert - Assert.That(updatedContent.Template, Is.Null); + Assert.IsNull(updatedContent.Template); } } [Test] - public void Can_Perform_Delete_On_ContentRepository() + public void DeleteContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - var contentType = contentTypeRepository.Get(NodeDto.NodeIdSeed); - var content = new Content("Textpage 2 Child Node", NodeDto.NodeIdSeed + 3, contentType); + var repository = CreateRepository(unitOfWork, out var contentTypeRepository); + var contentType = contentTypeRepository.Get(NodeDto.NodeIdSeed + 1); + var content = new Content("Textpage 2 Child Node", NodeDto.NodeIdSeed + 4, contentType); content.CreatorId = 0; content.WriterId = 0; - // Act repository.AddOrUpdate(content); unitOfWork.Flush(); var id = content.Id; @@ -569,84 +581,76 @@ namespace Umbraco.Tests.Persistence.Repositories unitOfWork.Flush(); var content1 = repository.Get(id); - - // Assert - Assert.That(content1, Is.Null); + Assert.IsNull(content1); } } [Test] - public void Can_Perform_Get_On_ContentRepository() + public void GetContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var content = repository.Get(NodeDto.NodeIdSeed + 3); + var repository = CreateRepository(unitOfWork, out _); + var content = repository.Get(NodeDto.NodeIdSeed + 4); - // Assert - Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 3)); + Assert.AreEqual(NodeDto.NodeIdSeed + 4, content.Id); Assert.That(content.CreateDate, Is.GreaterThan(DateTime.MinValue)); Assert.That(content.UpdateDate, Is.GreaterThan(DateTime.MinValue)); - Assert.That(content.ParentId, Is.Not.EqualTo(0)); - Assert.That(content.Name, Is.EqualTo("Text Page 2")); - //Assert.That(content.SortOrder, Is.EqualTo(1)); - Assert.That(content.Version, Is.Not.EqualTo(Guid.Empty)); - Assert.That(content.ContentTypeId, Is.EqualTo(NodeDto.NodeIdSeed)); + Assert.AreNotEqual(0, content.ParentId); + Assert.AreEqual("Text Page 2", content.Name); + Assert.AreNotEqual(Guid.Empty, content.Version); + Assert.AreEqual(NodeDto.NodeIdSeed + 1, content.ContentTypeId); Assert.That(content.Path, Is.Not.Empty); Assert.That(content.Properties.Any(), Is.True); } } [Test] - public void Can_Perform_GetByQuery_On_ContentRepository() + public void QueryContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); var result = repository.GetByQuery(query); - // Assert - Assert.That(result.Count(), Is.GreaterThanOrEqualTo(2)); + Assert.GreaterOrEqual(2, result.Count()); } } [Test] - public void Can_Perform_Get_All_With_Many_Version() + public void GetAllContentManyVersions() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); + var repository = CreateRepository(unitOfWork, out _); var result = repository.GetAll().ToArray(); + + // save them all foreach (var content in result) { - content.ChangePublishedState(PublishedState.Saving); - repository.AddOrUpdate(content); - } - unitOfWork.Flush(); - foreach (var content in result) - { - content.ChangePublishedState(PublishedState.Publishing); + content.SetValue("title", content.GetValue("title") + "x"); repository.AddOrUpdate(content); } unitOfWork.Flush(); - //re-get + // publish them all + foreach (var content in result) + { + ((Content) content).PublishAllValues(); + ((Content) content).PublishedState = PublishedState.Publishing; + repository.AddOrUpdate(content); + } + unitOfWork.Flush(); + // get them all again var result2 = repository.GetAll().ToArray(); - Assert.AreEqual(result.Count(), result2.Count()); + Assert.AreEqual(result.Length, result2.Length); } } @@ -665,24 +669,21 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_Sorting_On_Custom_Property() + public void GetPagedResultsByQuery_CustomPropertySort() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var query = unitOfWork.SqlContext.Query().Where(x => x.Name.Contains("Text")); - long totalRecords; try { unitOfWork.Database.AsUmbracoDatabase().EnableSqlTrace = true; unitOfWork.Database.AsUmbracoDatabase().EnableSqlCount = true; - var result = repository.GetPagedResultsByQuery(query, 0, 2, out totalRecords, "title", Direction.Ascending, false); + var result = repository.GetPagedResultsByQuery(query, 0, 2, out var totalRecords, "title", Direction.Ascending, false); Assert.AreEqual(3, totalRecords); Assert.AreEqual(2, result.Count()); @@ -700,25 +701,22 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_ForFirstPage_On_ContentRepository() + public void GetPagedResultsByQuery_FirstPage() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; try { unitOfWork.Database.AsUmbracoDatabase().EnableSqlTrace = true; unitOfWork.Database.AsUmbracoDatabase().EnableSqlCount = true; - var result = repository.GetPagedResultsByQuery(query, 0, 1, out totalRecords, "Name", Direction.Ascending, true); - // Assert + var result = repository.GetPagedResultsByQuery(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true); + Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); Assert.That(result.First().Name, Is.EqualTo("Text Page 1")); @@ -732,20 +730,16 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_ForSecondPage_On_ContentRepository() + public void GetPagedResultsByQuery_SecondPage() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var result = repository.GetPagedResultsByQuery(query, 1, 1, out totalRecords, "Name", Direction.Ascending, true); + var repository = CreateRepository(unitOfWork, out _); + + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); + var result = repository.GetPagedResultsByQuery(query, 1, 1, out var totalRecords, "Name", Direction.Ascending, true); - // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); Assert.That(result.First().Name, Is.EqualTo("Text Page 2")); @@ -753,20 +747,16 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithSinglePage_On_ContentRepository() + public void GetPagedResultsByQuery_SinglePage() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var result = repository.GetPagedResultsByQuery(query, 0, 2, out totalRecords, "Name", Direction.Ascending, true); + var repository = CreateRepository(unitOfWork, out _); + + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); + var result = repository.GetPagedResultsByQuery(query, 0, 2, out var totalRecords, "Name", Direction.Ascending, true); - // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(2)); Assert.That(result.First().Name, Is.EqualTo("Text Page 1")); @@ -774,20 +764,16 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithDescendingOrder_On_ContentRepository() + public void GetPagedResultsByQuery_DescendingOrder() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var result = repository.GetPagedResultsByQuery(query, 0, 1, out totalRecords, "Name", Direction.Descending, true); + var repository = CreateRepository(unitOfWork, out _); + + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); + var result = repository.GetPagedResultsByQuery(query, 0, 1, out var totalRecords, "Name", Direction.Descending, true); - // Assert Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); Assert.That(result.First().Name, Is.EqualTo("Text Page 2")); @@ -795,23 +781,18 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithFilterMatchingSome_On_ContentRepository() + public void GetPagedResultsByQuery_FilterMatchingSome() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var filterQuery = unitOfWork.SqlContext.Query().Where(x => x.Name.Contains("Page 2")); - var result = repository.GetPagedResultsByQuery(query, 0, 1, out totalRecords, "Name", Direction.Ascending, true, filterQuery); + var result = repository.GetPagedResultsByQuery(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true, filterQuery); - // Assert Assert.That(totalRecords, Is.EqualTo(1)); Assert.That(result.Count(), Is.EqualTo(1)); Assert.That(result.First().Name, Is.EqualTo("Text Page 2")); @@ -819,23 +800,18 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithFilterMatchingAll_On_ContentRepository() + public void GetPagedResultsByQuery_FilterMatchingAll() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); - long totalRecords; - var filterQuery = unitOfWork.SqlContext.Query().Where(x => x.Name.Contains("text")); - var result = repository.GetPagedResultsByQuery(query, 0, 1, out totalRecords, "Name", Direction.Ascending, true, filterQuery); + var result = repository.GetPagedResultsByQuery(query, 0, 1, out var totalRecords, "Name", Direction.Ascending, true, filterQuery); - // Assert Assert.That(totalRecords, Is.EqualTo(2)); Assert.That(result.Count(), Is.EqualTo(1)); Assert.That(result.First().Name, Is.EqualTo("Text Page 1")); @@ -843,18 +819,16 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetAll_By_Param_Ids_On_ContentRepository() + public void GetAllContentByIds() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var contents = repository.GetAll(NodeDto.NodeIdSeed + 2, NodeDto.NodeIdSeed + 3); - // Assert + Assert.That(contents, Is.Not.Null); Assert.That(contents.Any(), Is.True); Assert.That(contents.Count(), Is.EqualTo(2)); @@ -862,18 +836,15 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetAll_On_ContentRepository() + public void GetAllContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act + var repository = CreateRepository(unitOfWork, out _); + var contents = repository.GetAll(); - // Assert Assert.That(contents, Is.Not.Null); Assert.That(contents.Any(), Is.True); Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(4)); @@ -888,87 +859,51 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(contents.Any(), Is.True); Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(4)); } - - } [Test] - public void Can_Perform_Exists_On_ContentRepository() + public void ExistContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var exists = repository.Exists(NodeDto.NodeIdSeed + 1); + var repository = CreateRepository(unitOfWork, out _); + + var exists = repository.Exists(NodeDto.NodeIdSeed + 2); - // Assert Assert.That(exists, Is.True); } - - } [Test] - public void Can_Perform_Count_On_ContentRepository() + public void CountContent() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - int level = 2; - var query = unitOfWork.SqlContext.Query().Where(x => x.Level == level); + var repository = CreateRepository(unitOfWork, out _); + + var query = unitOfWork.SqlContext.Query().Where(x => x.Level == 2); var result = repository.Count(query); - // Assert Assert.That(result, Is.GreaterThanOrEqualTo(2)); } } [Test] - public void Can_Verify_Keys_Set() + public void QueryContentByUniqueId() { - // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act - var textpage = repository.Get(NodeDto.NodeIdSeed + 1); - var subpage = repository.Get(NodeDto.NodeIdSeed + 2); - var trashed = repository.Get(NodeDto.NodeIdSeed + 4); + var repository = CreateRepository(unitOfWork, out _); - // Assert - Assert.That(textpage.Key.ToString().ToUpper(), Is.EqualTo("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0")); - Assert.That(subpage.Key.ToString().ToUpper(), Is.EqualTo("FF11402B-7E53-4654-81A7-462AC2108059")); - Assert.That(trashed.Key, Is.Not.EqualTo(Guid.Empty)); - } - } - - [Test] - public void Can_Get_Content_By_Guid_Key() - { - // Arrange - var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); - using (var unitOfWork = provider.CreateUnitOfWork()) - { - ContentTypeRepository contentTypeRepository; - var repository = CreateRepository(unitOfWork, out contentTypeRepository); - // Act var query = unitOfWork.SqlContext.Query().Where(x => x.Key == new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0")); var content = repository.GetByQuery(query).SingleOrDefault(); - // Assert - Assert.That(content, Is.Not.Null); - Assert.That(content.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1)); + Assert.IsNotNull(content); + Assert.AreEqual(NodeDto.NodeIdSeed + 2, content.Id); } - } public void CreateTestData() @@ -976,6 +911,7 @@ namespace Umbraco.Tests.Persistence.Repositories //Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed) ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); //Create and Save Content "Homepage" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 1) diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 5cf3c09535..50fafcc7b0 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -294,7 +294,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreNotEqual(propertyType.Key, Guid.Empty); } - TestHelper.AssertAllPropertyValuesAreEquals(contentType, fetched, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new [] { "DefaultTemplate", "AllowedTemplates", "UpdateDate" }); + TestHelper.AssertPropertyValuesAreEqual(contentType, fetched, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new [] { "DefaultTemplate", "AllowedTemplates", "UpdateDate" }); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 1e5f86bc6d..3c1ba02532 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -346,7 +346,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(dataTypeDefinition.HasIdentity, Is.True); Assert.That(exists, Is.True); - TestHelper.AssertAllPropertyValuesAreEquals(dataTypeDefinition, fetched, "yyyy-MM-dd HH:mm:ss"); + TestHelper.AssertPropertyValuesAreEqual(dataTypeDefinition, fetched, "yyyy-MM-dd HH:mm:ss"); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 35653095aa..6f74dffadb 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Cache_Active_By_Int_And_Guid() + public void CacheActiveForIntsAndGuids() { MediaTypeRepository mediaTypeRepository; @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Add_On_MediaRepository() + public void SaveMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -111,12 +111,12 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(mediaType.HasIdentity, Is.True); Assert.That(image.HasIdentity, Is.True); - TestHelper.AssertAllPropertyValuesAreEquals(image, fetched, "yyyy-MM-dd HH:mm:ss"); + TestHelper.AssertPropertyValuesAreEqual(image, fetched, "yyyy-MM-dd HH:mm:ss"); } } [Test] - public void Can_Perform_Multiple_Adds_On_MediaRepository() + public void SaveMediaMultiple() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -147,38 +147,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Multiple_Adds_On_MediaRepository_With_RepositoryResolver() - { - // Arrange - var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); - using (var unitOfWork = provider.CreateUnitOfWork()) - { - MediaTypeRepository mediaTypeRepository; - var repository = CreateRepository(unitOfWork, out mediaTypeRepository); - - var mediaType = mediaTypeRepository.Get(1032); - var file = MockedMedia.CreateMediaFile(mediaType, -1); - - // Act - repository.AddOrUpdate(file); - unitOfWork.Flush(); - - var image = MockedMedia.CreateMediaImage(mediaType, -1); - repository.AddOrUpdate(image); - unitOfWork.Flush(); - - // Assert - Assert.That(file.HasIdentity, Is.True); - Assert.That(image.HasIdentity, Is.True); - Assert.That(file.Name, Is.EqualTo("Test File")); - Assert.That(image.Name, Is.EqualTo("Test Image")); - Assert.That(file.ContentTypeId, Is.EqualTo(mediaType.Id)); - Assert.That(image.ContentTypeId, Is.EqualTo(mediaType.Id)); - } - } - - [Test] - public void Can_Verify_Fresh_Entity_Is_Not_Dirty() + public void GetMediaIsNotDirty() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -197,7 +166,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Update_On_MediaRepository() + public void UpdateMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -221,7 +190,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Delete_On_MediaRepository() + public void DeleteMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -245,7 +214,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Get_On_MediaRepository() + public void GetMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -272,7 +241,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetByQuery_On_MediaRepository() + public void QueryMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -291,7 +260,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetByQuery_On_MediaRepository_With_ContentType_Id_Filter() + public void QueryMedia_ContentTypeIdFilter() { // Arrange var folderMediaType = ServiceContext.MediaTypeService.Get(1031); @@ -317,10 +286,13 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Ignore("We could allow this to work but it requires an extra join on the query used which currently we don't absolutely need so leaving this out for now")] + [Ignore("Unsupported feature.")] [Test] - public void Can_Perform_GetByQuery_On_MediaRepository_With_ContentType_Alias_Filter() + public void QueryMedia_ContentTypeAliasFilter() { + // we could support this, but it would require an extra join on the query, + // and we don't absolutely need it now, so leaving it out for now + // Arrange var folderMediaType = ServiceContext.MediaTypeService.Get(1031); var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -346,7 +318,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_ForFirstPage_On_MediaRepository() + public void GetPagedResultsByQuery_FirstPage() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -367,7 +339,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_ForSecondPage_On_MediaRepository() + public void GetPagedResultsByQuery_SecondPage() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -389,7 +361,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithSinglePage_On_MediaRepository() + public void GetPagedResultsByQuery_SinglePage() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -411,7 +383,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithDescendingOrder_On_MediaRepository() + public void GetPagedResultsByQuery_DescendingOrder() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -433,7 +405,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WitAlternateOrder_On_MediaRepository() + public void GetPagedResultsByQuery_AlternateOrder() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -455,7 +427,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithFilterMatchingSome_On_MediaRepository() + public void GetPagedResultsByQuery_FilterMatchingSome() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -479,7 +451,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetPagedResultsByQuery_WithFilterMatchingAll_On_MediaRepository() + public void GetPagedResultsByQuery_FilterMatchingAll() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -503,7 +475,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetAll_By_Param_Ids_On_MediaRepository() + public void GetAllMediaByIds() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -523,7 +495,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_GetAll_On_MediaRepository() + public void GetAllMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -553,7 +525,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Exists_On_MediaRepository() + public void ExistMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -575,7 +547,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Perform_Count_On_MediaRepository() + public void CountMedia() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index 0cbd7637d7..6b42a14f01 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(contentType.Path.Contains(","), Is.True); Assert.That(contentType.SortOrder, Is.GreaterThan(0)); - TestHelper.AssertAllPropertyValuesAreEquals(contentType, fetched, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new[] { "UpdateDate" }); + TestHelper.AssertPropertyValuesAreEqual(contentType, fetched, "yyyy-MM-dd HH:mm:ss", ignoreProperties: new[] { "UpdateDate" }); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index a2530cdd4d..b83294255d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Get_Member_By_Id() + public void GetMember() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) @@ -54,7 +54,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Get_Members_By_Ids() + public void GetMembers() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) @@ -77,7 +77,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Get_All_Members() + public void GetAllMembers() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) @@ -102,7 +102,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Perform_GetByQuery_With_Key() + public void QueryMember() { // Arrange var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -126,7 +126,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Persist_Member() + public void SaveMember() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) @@ -141,18 +141,17 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(sut, Is.Not.Null); Assert.That(sut.HasIdentity, Is.True); - Assert.That(sut.Properties.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); Assert.That(sut.Name, Is.EqualTo("Johnny Hefty")); Assert.That(sut.Email, Is.EqualTo("johnny@example.com")); Assert.That(sut.RawPasswordValue, Is.EqualTo("123")); Assert.That(sut.Username, Is.EqualTo("hefty")); - TestHelper.AssertAllPropertyValuesAreEquals(sut, member, "yyyy-MM-dd HH:mm:ss"); + TestHelper.AssertPropertyValuesAreEqual(sut, member, "yyyy-MM-dd HH:mm:ss"); } } [Test] - public void New_Member_Has_Built_In_Properties_By_Default() + public void MemberHasBuiltinProperties() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); using (var unitOfWork = provider.CreateUnitOfWork()) @@ -174,7 +173,6 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(sut.ContentType.PropertyGroups.Count(), Is.EqualTo(2)); Assert.That(sut.ContentType.PropertyTypes.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count)); Assert.That(sut.Properties.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count)); - Assert.That(sut.Properties.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); var grp = sut.PropertyGroups.FirstOrDefault(x => x.Name == Constants.Conventions.Member.StandardPropertiesGroupName); Assert.IsNotNull(grp); var aliases = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); @@ -186,7 +184,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Does_Not_Replace_Password_When_Null() + public void SavingPreservesPassword() { IMember sut; var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -216,7 +214,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void MemberRepository_Can_Update_Email_And_Login_When_Changed() + public void SavingUpdatesNameAndEmail() { IMember sut; var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); @@ -247,7 +245,7 @@ namespace Umbraco.Tests.Persistence.Repositories } [Test] - public void Can_Create_Correct_Subquery() + public void QueryMember_WithSubQuery() { var provider = TestObjects.GetScopeUnitOfWorkProvider(Logger); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index a4c7944d49..12cbaca7cf 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -46,7 +46,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(sut.PropertyGroups.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); Assert.That(sut.PropertyTypes.Any(x => x.HasIdentity == false || x.Id == 0), Is.False); - TestHelper.AssertAllPropertyValuesAreEquals(sut, memberType, "yyyy-MM-dd HH:mm:ss"); + TestHelper.AssertPropertyValuesAreEqual(sut, memberType, "yyyy-MM-dd HH:mm:ss"); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs index 4c5b4f67db..3a62c0c53f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs @@ -197,6 +197,7 @@ namespace Umbraco.Tests.Persistence.Repositories //Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed) var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); contentType.Key = Guid.NewGuid(); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); //Create and Save Content "Homepage" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 1) diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs index c04e702461..c7b6a6a959 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs @@ -118,8 +118,8 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(relation, Is.Not.Null); Assert.That(relation.HasIdentity, Is.True); - Assert.That(relation.ChildId, Is.EqualTo(NodeDto.NodeIdSeed + 2)); - Assert.That(relation.ParentId, Is.EqualTo(NodeDto.NodeIdSeed + 1)); + Assert.That(relation.ChildId, Is.EqualTo(NodeDto.NodeIdSeed + 3)); + Assert.That(relation.ParentId, Is.EqualTo(NodeDto.NodeIdSeed + 2)); Assert.That(relation.RelationType.Alias, Is.EqualTo("relateContentOnCopy")); } } @@ -197,7 +197,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(unitOfWork, out repositoryType); // Act - var query = unitOfWork.SqlContext.Query().Where(x => x.ParentId == NodeDto.NodeIdSeed + 1); + var query = unitOfWork.SqlContext.Query().Where(x => x.ParentId == NodeDto.NodeIdSeed + 2); int count = repository.Count(query); // Assert @@ -237,7 +237,7 @@ namespace Umbraco.Tests.Persistence.Repositories RelationTypeRepository repositoryType; var repository = CreateRepository(unitOfWork, out repositoryType); - var content = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2); + var content = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 3); ServiceContext.ContentService.Delete(content, 0); // Act @@ -273,6 +273,7 @@ namespace Umbraco.Tests.Persistence.Repositories //Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed) ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); //Create and Save Content "Homepage" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 1) diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 279a693238..fd5075999a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -92,6 +92,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -123,6 +124,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -163,6 +165,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -206,6 +209,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -247,6 +251,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -284,6 +289,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content = MockedContent.CreateSimpleContent(contentType); @@ -329,6 +335,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -374,6 +381,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -420,6 +428,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -456,6 +465,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -497,6 +507,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -542,6 +553,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -588,6 +600,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -633,6 +646,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -681,6 +695,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -740,6 +755,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -792,6 +808,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); var content1 = MockedContent.CreateSimpleContent(contentType); @@ -833,6 +850,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); @@ -918,6 +936,7 @@ namespace Umbraco.Tests.Persistence.Repositories //create data to relate to var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); unitOfWork.Flush(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 5ff945f1d4..fb7c2ede99 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -407,8 +407,9 @@ namespace Umbraco.Tests.Persistence.Repositories var contentRepo = new ContentRepository(unitOfWork, DisabledCache, Logger, contentTypeRepository, templateRepository, tagRepository, Mock.Of()); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); - var textpage = MockedContent.CreateSimpleContent(contentType); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! contentTypeRepository.AddOrUpdate(contentType); + var textpage = MockedContent.CreateSimpleContent(contentType); contentRepo.AddOrUpdate(textpage); unitOfWork.Flush(); diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 5ee1dbaf90..1870efc38d 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -185,6 +185,7 @@ namespace Umbraco.Tests.Publishing //Create and Save ContentType "umbTextpage" -> 1045 ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); var mandatoryType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true); ServiceContext.ContentTypeService.Save(mandatoryType); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 6d6ba2e071..b917e399ce 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -64,10 +64,13 @@ namespace Umbraco.Tests.Services public void Deleting_Content_Type_With_Hierarchy_Of_Content_Items_Moves_Orphaned_Content_To_Recycle_Bin() { IContentType contentType1 = MockedContentTypes.CreateSimpleContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContentType contentType2 = MockedContentTypes.CreateSimpleContentType("test2", "Test2"); + ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType2); IContentType contentType3 = MockedContentTypes.CreateSimpleContentType("test3", "Test3"); + ServiceContext.FileService.SaveTemplate(contentType3.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType3); var contentTypes = new[] { contentType1, contentType2, contentType3 }; @@ -163,10 +166,13 @@ namespace Umbraco.Tests.Services try { IContentType contentType1 = MockedContentTypes.CreateSimpleContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContentType contentType2 = MockedContentTypes.CreateSimpleContentType("test2", "Test2"); + ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType2); IContentType contentType3 = MockedContentTypes.CreateSimpleContentType("test3", "Test3"); + ServiceContext.FileService.SaveTemplate(contentType3.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType3); var contentTypes = new[] { contentType1, contentType2, contentType3 }; @@ -203,10 +209,13 @@ namespace Umbraco.Tests.Services try { IContentType contentType1 = MockedContentTypes.CreateSimpleContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContentType contentType2 = MockedContentTypes.CreateSimpleContentType("test2", "Test2"); + ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType2); IContentType contentType3 = MockedContentTypes.CreateSimpleContentType("test3", "Test3"); + ServiceContext.FileService.SaveTemplate(contentType3.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType3); var root = MockedContent.CreateSimpleContent(contentType1, "Root", -1); @@ -247,6 +256,7 @@ namespace Umbraco.Tests.Services public void Deleting_PropertyType_Removes_The_Property_From_Content() { IContentType contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContent contentItem = MockedContent.CreateTextpageContent(contentType1, "Testing", -1); ServiceContext.ContentService.SaveAndPublishWithStatus(contentItem); @@ -268,7 +278,9 @@ namespace Umbraco.Tests.Services public void Rebuild_Content_Xml_On_Alias_Change() { var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "Test2"); + ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); ServiceContext.ContentTypeService.Save(contentType2); var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); @@ -305,6 +317,7 @@ namespace Umbraco.Tests.Services public void Rebuild_Content_Xml_On_Property_Removal() { var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); foreach (var x in contentItems1) diff --git a/src/Umbraco.Tests/Services/TestWithSomeContentBase.cs b/src/Umbraco.Tests/Services/TestWithSomeContentBase.cs index afdbeb7c5d..1f9dd42dae 100644 --- a/src/Umbraco.Tests/Services/TestWithSomeContentBase.cs +++ b/src/Umbraco.Tests/Services/TestWithSomeContentBase.cs @@ -22,6 +22,7 @@ namespace Umbraco.Tests.Services //Create and Save ContentType "umbTextpage" -> 1060 ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"); + ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); //Create and Save Content "Homepage" based on "umbTextpage" -> 1061 @@ -32,7 +33,6 @@ namespace Umbraco.Tests.Services //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1062 Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); subpage.ReleaseDate = DateTime.Now.AddMinutes(-5); - subpage.ChangePublishedState(PublishedState.Saving); ServiceContext.ContentService.Save(subpage, 0); //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1063 diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 370e9b13d1..892cd93796 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -10,7 +10,9 @@ using System.Threading; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using File = System.IO.File; namespace Umbraco.Tests.TestHelpers { @@ -95,87 +97,86 @@ namespace Umbraco.Tests.TestHelpers File.Delete(umbracoSettingsFile); } - public static void AssertAllPropertyValuesAreEquals(object actual, object expected, string dateTimeFormat = null, Func sorter = null, string[] ignoreProperties = null) + // fixme obsolete the dateTimeFormat thing and replace with dateDelta + public static void AssertPropertyValuesAreEqual(object actual, object expected, string dateTimeFormat = null, Func sorter = null, string[] ignoreProperties = null) { + const int dateDeltaMilliseconds = 500; // .5s + var properties = expected.GetType().GetProperties(); foreach (var property in properties) { - //ignore properties that are attributed with this + // ignore properties that are attributed with EditorBrowsableState.Never var att = property.GetCustomAttribute(false); if (att != null && att.State == EditorBrowsableState.Never) continue; + // ignore explicitely ignored properties if (ignoreProperties != null && ignoreProperties.Contains(property.Name)) continue; - var expectedValue = property.GetValue(expected, null); var actualValue = property.GetValue(actual, null); + var expectedValue = property.GetValue(expected, null); - if (((actualValue is string) == false) && actualValue is IEnumerable) - { - AssertListsAreEquals(property, (IEnumerable)actualValue, (IEnumerable)expectedValue, dateTimeFormat, sorter); - } - else if (dateTimeFormat.IsNullOrWhiteSpace() == false && actualValue is DateTime) - { - // round to second else in some cases tests can fail ;-( - var expectedDateTime = (DateTime) expectedValue; - expectedDateTime = expectedDateTime.AddTicks(-(expectedDateTime.Ticks%TimeSpan.TicksPerSecond)); - var actualDateTime = (DateTime) actualValue; - actualDateTime = actualDateTime.AddTicks(-(actualDateTime.Ticks % TimeSpan.TicksPerSecond)); - - Assert.AreEqual(expectedDateTime.ToString(dateTimeFormat), actualDateTime.ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); - } - else - { - Assert.AreEqual(expectedValue, actualValue, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); - } + AssertAreEqual(property, expectedValue, actualValue, sorter, dateDeltaMilliseconds); } } - private static void AssertListsAreEquals(PropertyInfo property, IEnumerable actualList, IEnumerable expectedList, string dateTimeFormat, Func sorter) + private static void AssertAreEqual(PropertyInfo property, object expected, object actual, Func sorter = null, int dateDeltaMilliseconds = 0) + { + if (!(expected is string) && expected is IEnumerable) + { + // compare lists + AssertListsAreEqual(property, (IEnumerable) actual, (IEnumerable) expected, sorter, dateDeltaMilliseconds); + } + else if (expected is DateTime expectedDateTime) + { + // compare date & time with delta + var actualDateTime = (DateTime) actual; + var delta = (actualDateTime - expectedDateTime).TotalMilliseconds; + Assert.IsTrue(Math.Abs(delta) <= dateDeltaMilliseconds, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expected, actual); + } + else if (expected is Property expectedProperty) + { + // compare values + var actualProperty = (Property) actual; + var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); + var actualPropertyValues = actualProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); + if (expectedPropertyValues.Length != actualPropertyValues.Length) + Assert.Fail($"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}."); + for (var i = 0; i < expectedPropertyValues.Length; i++) + { + Assert.AreEqual(expectedPropertyValues[i].DraftValue, actualPropertyValues[i].DraftValue, $"{property.DeclaringType.Name}.{property.Name}: Expected draft value \"{expectedPropertyValues[i].DraftValue}\" but got \"{actualPropertyValues[i].DraftValue}\"."); + Assert.AreEqual(expectedPropertyValues[i].PublishedValue, actualPropertyValues[i].PublishedValue, $"{property.DeclaringType.Name}.{property.Name}: Expected published value \"{expectedPropertyValues[i].DraftValue}\" but got \"{actualPropertyValues[i].DraftValue}\"."); + } + } + else + { + // directly compare values + Assert.AreEqual(expected, actual, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expected, actual); + } + } + + private static void AssertListsAreEqual(PropertyInfo property, IEnumerable expected, IEnumerable actual, Func sorter = null, int dateDeltaMilliseconds = 0) { if (sorter == null) { - //this is pretty hackerific but saves us some code to write + // this is pretty hackerific but saves us some code to write sorter = enumerable => { - //semi-generic way of ensuring any collection of IEntity are sorted by Ids for comparison + // semi-generic way of ensuring any collection of IEntity are sorted by Ids for comparison var entities = enumerable.OfType().ToList(); - if (entities.Count > 0) - { - return entities.OrderBy(x => x.Id); - } - else - { - return enumerable; - } + return entities.Count > 0 ? (IEnumerable) entities.OrderBy(x => x.Id) : entities; }; } - var actualListEx = sorter(actualList).Cast().ToList(); - var expectedListEx = sorter(expectedList).Cast().ToList(); + var expectedListEx = sorter(expected).Cast().ToList(); + var actualListEx = sorter(actual).Cast().ToList(); if (actualListEx.Count != expectedListEx.Count) Assert.Fail("Collection {0}.{1} does not match. Expected IEnumerable containing {2} elements but was IEnumerable containing {3} elements", property.PropertyType.Name, property.Name, expectedListEx.Count, actualListEx.Count); - for (int i = 0; i < actualListEx.Count; i++) - { - var actualValue = actualListEx[i]; - var expectedValue = expectedListEx[i]; - - if (((actualValue is string) == false) && actualValue is IEnumerable) - { - AssertListsAreEquals(property, (IEnumerable)actualValue, (IEnumerable)expectedValue, dateTimeFormat, sorter); - } - else if (dateTimeFormat.IsNullOrWhiteSpace() == false && actualValue is DateTime) - { - Assert.AreEqual(((DateTime)expectedValue).ToString(dateTimeFormat), ((DateTime)actualValue).ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); - } - else - { - Assert.AreEqual(expectedValue, actualValue, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue); - } - } + for (var i = 0; i < actualListEx.Count; i++) + AssertAreEqual(property, expectedListEx[i], actualListEx[i], sorter, dateDeltaMilliseconds); } public static void DeleteDirectory(string path) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 7c28c01ed2..44720c6909 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -464,7 +464,6 @@ - diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index 0164eb9eff..66f0042231 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -497,12 +497,13 @@ AND (umbracoNode.id=@id)"; public XmlNode GetPreviewXmlNode(int contentId) { + // fixme - probably borked const string sql = @"SELECT umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.Level, cmsPreviewXml.xml, cmsDocument.published FROM umbracoNode JOIN cmsPreviewXml ON (cmsPreviewXml.nodeId=umbracoNode.id) JOIN cmsDocument ON (cmsDocument.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType AND cmsDocument.newest=1 +WHERE umbracoNode.nodeObjectType = @nodeObjectType AND (umbracoNode.id=@id)"; XmlDto xmlDto; @@ -1749,9 +1750,8 @@ WHERE cmsContentXml.nodeId IN ( long total; do { - // make sure we do NOT add (cmsDocument.newest = 1) to the query - // because we already have the condition on the content being published - var descendants = repository.GetPagedResultsByQuery(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true, newest: false); + var descendants = repository.GetPagedResultsByQuery(query, pageIndex++, groupSize, out total, "Path", Direction.Ascending, true); + // fixme serializing published or draft values?! var items = descendants.Select(c => new ContentXmlDto { NodeId = c.Id, Xml = _xmlContentSerializer(c).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length;