diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 00e5258953..39c4610cd8 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; -using Umbraco.Core.Composing; namespace Umbraco.Core.Models { @@ -20,10 +19,9 @@ namespace Umbraco.Core.Models private PublishedState _publishedState; private DateTime? _releaseDate; private DateTime? _expireDate; - private HashSet _cultures; - private Dictionary _publishNames; + private Dictionary _publishNames; - private static readonly Dictionary NoPublishNames = new Dictionary(); + private static readonly Dictionary NoPublishNames = new Dictionary(); private static readonly Lazy Ps = new Lazy(); /// @@ -203,19 +201,19 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] - public IReadOnlyDictionary PublishNames => _publishNames ?? NoPublishNames; + public IReadOnlyDictionary PublishNames => _publishNames ?? NoPublishNames; /// - public string GetPublishName(string languageId) + public string GetPublishName(int? languageId) { if (languageId == null) return PublishName; if (_publishNames == null) return null; - return _publishNames.TryGetValue(languageId, out var name) ? name : null; + return _publishNames.TryGetValue(languageId.Value, out var name) ? name : null; } // sets a publish name // internal for repositories - internal void SetPublishName(string languageId, string name) + internal void SetPublishName(int? languageId, string name) { if (languageId == null) { @@ -226,13 +224,13 @@ namespace Umbraco.Core.Models // private method, assume that culture is valid if (_publishNames == null) - _publishNames = new Dictionary(); + _publishNames = new Dictionary(); - _publishNames[languageId] = name; + _publishNames[languageId.Value] = name; } // clears a publish name - private void ClearPublishName(string languageId) + private void ClearPublishName(int? languageId) { if (languageId == null) { @@ -240,38 +238,23 @@ namespace Umbraco.Core.Models return; } - _publishNames.Remove(languageId); + _publishNames.Remove(languageId.Value); if (_publishNames.Count == 0) _publishNames = null; + } + + // clears all publish names + private void ClearPublishNames() + { + _publishNames = null; } /// - public bool IsCultureAvailable(string languageId) + public bool IsCulturePublished(int? languageId) { - return _cultures != null && _cultures.Contains(languageId); + return !string.IsNullOrWhiteSpace(GetPublishName(languageId)); } - // fixme but what's setting it? - // fixme a culture is available when published = when setting the publish name (merge?) - // internal for repositories - internal void SetCultureAvailability(string languageId, bool available) - { - if (available) - { - if (_cultures == null) _cultures = new HashSet(StringComparer.OrdinalIgnoreCase); - - // private method, assume that culture is valid - _cultures.Add(languageId); - } - else - { - if (_cultures == null) return; - - _cultures.Remove(languageId); - if (_cultures.Count == 0) _cultures = null; - } - } - [IgnoreDataMember] public int PublishedVersionId { get; internal set; } @@ -281,11 +264,19 @@ namespace Umbraco.Core.Models /// public virtual bool PublishAllValues() { + // the values we want to publish should be valid if (ValidateAll().Any()) return false; + // property.PublishAllValues only deals with supported variations (if any) foreach (var property in Properties) - property.PublishAllValues(); + property.PublishAllValues(); + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + foreach (var (languageId, name) in Names) + SetPublishName(languageId, name); + _publishedState = PublishedState.Publishing; return true; } @@ -293,22 +284,20 @@ namespace Umbraco.Core.Models /// public virtual bool PublishValues(int? languageId = null, string segment = null) { - // fixme MUST refactor it all here! // the variation should be supported by the content type + ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); - //ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); - - //if (Validate(languageId, segment).Any()) - // return false; - + // the values we want to publish should be valid + if (Validate(languageId, segment).Any()) + return false; + + // property.PublishValue throws on invalid variation, so filter them out foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) property.PublishValue(languageId, segment); - - // Name and PublishName are managed in the repository - // but Names and PublishNames must be managed here, - // as they depend on the variation - var languageIsoCode = Current.Services.LocalizationService.GetLanguageById(languageId.Value).IsoCode; - SetPublishName(languageIsoCode, GetName(languageIsoCode)); + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + SetPublishName(languageId, GetName(languageId)); _publishedState = PublishedState.Publishing; return true; @@ -316,12 +305,19 @@ namespace Umbraco.Core.Models /// public virtual bool PublishCultureValues(int? languageId = null) - { + { + // the values we want to publish should be valid if (ValidateCulture(languageId).Any()) return false; - + + // property.PublishCultureValues only deals with supported variations (if any) foreach (var property in Properties) property.PublishCultureValues(languageId); + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + SetPublishName(languageId, GetName(languageId)); + _publishedState = PublishedState.Publishing; return true; } @@ -329,22 +325,30 @@ namespace Umbraco.Core.Models /// public virtual void ClearAllPublishedValues() { + // property.ClearPublishedAllValues only deals with supported variations (if any) foreach (var property in Properties) property.ClearPublishedAllValues(); + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + ClearPublishNames(); + _publishedState = PublishedState.Publishing; } /// public virtual void ClearPublishedValues(int? languageId = null, string segment = null) { + // the variation should be supported by the content type + ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); + + // property.ClearPublishedValue throws on invalid variation, so filter them out foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) property.ClearPublishedValue(languageId, segment); - // Name and PublishName are managed in the repository - // but Names and PublishNames must be managed here, - // as they depend on the variation - var languageIsoCode = Current.Services.LocalizationService.GetLanguageById(languageId.Value).IsoCode; - ClearPublishName(languageIsoCode); + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + ClearPublishName(languageId); _publishedState = PublishedState.Publishing; } @@ -352,11 +356,17 @@ namespace Umbraco.Core.Models /// public virtual void ClearCulturePublishedValues(int? languageId = null) { + // property.ClearPublishedCultureValues only deals with supported variations (if any) foreach (var property in Properties) property.ClearPublishedCultureValues(languageId); + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + ClearPublishName(languageId); + _publishedState = PublishedState.Publishing; } - + private bool CopyingFromSelf(IContent other) { // copying from the same Id and VersionPk @@ -394,7 +404,12 @@ namespace Umbraco.Core.Models var value = published ? pvalue.PublishedValue : pvalue.EditedValue; SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); } - } + } + + // copy names + ClearNames(); + foreach (var (languageId, name) in other.Names) + SetName(languageId, name); } /// @@ -430,7 +445,10 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; SetValue(alias, otherProperty.GetValue(languageId, segment, published), languageId, segment); - } + } + + // copy name + SetName(languageId, other.GetName(languageId)); } /// @@ -461,7 +479,10 @@ namespace Umbraco.Core.Models var value = published ? pvalue.PublishedValue : pvalue.EditedValue; SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); } - } + } + + // copy name + SetName(languageId, other.GetName(languageId)); } /// diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index dc9b76dc9c..8ecc7f0039 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Models protected IContentTypeComposition ContentTypeBase; private int _writerId; private PropertyCollection _properties; - private Dictionary _names; + private Dictionary _names; /// /// Initializes a new instance of the class. @@ -66,8 +66,7 @@ namespace Umbraco.Core.Models 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); - // fixme how can that work / comparison? - public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); + public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); } protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -123,7 +122,7 @@ namespace Umbraco.Core.Models /// [DataMember] - public virtual IReadOnlyDictionary Names + public virtual IReadOnlyDictionary Names { get => _names; set @@ -134,7 +133,7 @@ namespace Umbraco.Core.Models } /// - public virtual void SetName(string languageId, string name) + public virtual void SetName(int? languageId, string name) { if (languageId == null) { @@ -143,23 +142,27 @@ namespace Umbraco.Core.Models } if ((ContentTypeBase.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) == 0) - throw new NotSupportedException("Content type does not support varying name by culture."); - - // fixme validate language? + throw new NotSupportedException("Content type does not support varying name by culture."); if (_names == null) - _names = new Dictionary(); + _names = new Dictionary(); - _names[languageId] = name; + _names[languageId.Value] = name; + OnPropertyChanged(Ps.Value.NamesSelector); + } + + protected virtual void ClearNames() + { + _names = null; OnPropertyChanged(Ps.Value.NamesSelector); } /// - public virtual string GetName(string languageId) + public virtual string GetName(int? languageId) { if (languageId == null) return Name; if (_names == null) return null; - return _names.TryGetValue(languageId, out var name) ? name : null; + return _names.TryGetValue(languageId.Value, out var name) ? name : null; } /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 047b2fa74f..64144748e0 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -160,6 +160,32 @@ namespace Umbraco.Core.Models } #endregion + #region Variants + + /// + /// Returns true if the content has any property type that allows language variants + /// + public static bool HasPropertyTypeVaryingByCulture(this IContent content) + { + // fixme - what about CultureSegment? what about content.ContentType.Variations? + return content.PropertyTypes.Any(x => x.Variations == ContentVariation.CultureNeutral); + } + + /// + /// Returns true if the content has a variation for the language/segment combination + /// + /// + /// + /// + /// + public static bool HasVariation(this IContent content, int langId, string segment = null) + { + // fixme - wire - but: purpose? are we looking for a 'published' variation? what is this? + return false; + } + + #endregion + /// /// Removes characters that are not valide XML characters from all entity properties /// of type string. See: http://stackoverflow.com/a/961504/5018 diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index ec7eddbaf6..b686b97108 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -201,6 +201,35 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _variations, Ps.Value.VaryBy); } + /// + /// Validates that a variation is valid for the content type. + /// + public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid) + { + ContentVariation variation; + if (languageId.HasValue) + { + variation = segment != null + ? ContentVariation.CultureSegment + : ContentVariation.CultureNeutral; + } + else if (segment != null) + { + variation = ContentVariation.InvariantSegment; + } + else + { + variation = ContentVariation.InvariantNeutral; + } + if ((Variations & variation) == 0) + { + if (throwIfInvalid) + throw new NotSupportedException($"Variation {variation} is invalid for content type \"{Alias}\"."); + return false; + } + return true; + } + /// /// List of PropertyGroups available on this ContentType /// diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 6f2fba0da9..a294040dbc 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -86,9 +86,8 @@ namespace Umbraco.Core.Models /// /// A culture becomes available whenever values for this culture are published, /// and it becomes unavailable whenever values for this culture are unpublished. - /// fixme - what's setting this? /// - bool IsCultureAvailable(string languageId); + bool IsCulturePublished(int? languageId); /// /// Gets the name of the published version of the content for a given culture. @@ -97,9 +96,8 @@ namespace Umbraco.Core.Models /// When editing the content, the name can change, but this will not until the content is published. /// When is null, gets the invariant /// language, which is the value of the property. - /// fixme - what's setting this? /// - string GetPublishName(string languageId); + string GetPublishName(int? languageId); /// /// Gets the published names of the content. @@ -108,7 +106,7 @@ namespace Umbraco.Core.Models /// Because a dictionary key cannot be null this cannot get the invariant /// name, which must be get via the property. /// - IReadOnlyDictionary PublishNames { get; } + IReadOnlyDictionary PublishNames { get; } // fixme - these two should move to some kind of service diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index 1ef94a2d89..aee8acd35e 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Models /// When is null, sets the invariant /// language, which sets the property. /// - void SetName(string languageId, string value); + void SetName(int? languageId, string value); /// /// Gets the name of the content item for a specified language. @@ -43,7 +43,7 @@ namespace Umbraco.Core.Models /// When is null, gets the invariant /// language, which is the value of the property. /// - string GetName(string languageId); + string GetName(int? languageId); /// /// Gets or sets the names of the content item. @@ -52,7 +52,7 @@ namespace Umbraco.Core.Models /// Because a dictionary key cannot be null this cannot get nor set the invariant /// name, which must be get or set via the property. /// - IReadOnlyDictionary Names { get; set; } + IReadOnlyDictionary Names { get; set; } /// /// List of properties, which make up all the data available for this Content object diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index 72b8df8de9..68e5923d66 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -48,6 +48,11 @@ namespace Umbraco.Core.Models /// ContentVariation Variations { get; set; } + /// + /// Validates that a variation is valid for the content type. + /// + bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid); + /// /// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType /// diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs index 524d402ecc..104a707669 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -23,8 +23,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("languageId")] [ForeignKey(typeof(LanguageDto))] [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] - [NullSetting(NullSetting = NullSettings.Null)] - public int? LanguageId { get; set; } + public int LanguageId { get; set; } [Column("name")] [NullSetting(NullSetting = NullSettings.Null)] @@ -36,7 +35,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("availableDate")] [NullSetting(NullSetting = NullSettings.Null)] public DateTime? AvailableDate { get; set; } - + [Column("availableUserId")] // [ForeignKey(typeof(UserDto))] -- there is no foreign key so we can delete users without deleting associated content //[NullSetting(NullSetting = NullSettings.Null)] diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index fc0653d2db..2a1aa5c403 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -910,7 +910,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement foreach (var v in variation) { content.SetPublishName(v.LanguageId, v.Name); - content.SetCultureAvailability(v.LanguageId, v.Available); } } @@ -926,7 +925,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } if (versions.Count == 0) return new Dictionary>(); - // fixme split content (name) vs document (availability) ? var dtos = Database.FetchByGroups(versions, 2000, batch => Sql() .Select() @@ -942,7 +940,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement variation.Add(new CultureVariation { - LanguageId = dto.LanguageId.HasValue ? _languageRepository.GetIsoCodeById(dto.LanguageId.Value) : null, + LanguageId = dto.LanguageId, Name = dto.Name, Available = dto.Available }); @@ -953,13 +951,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private IEnumerable GetVariationDtos(IContent content, bool publishing) { - // fixme what about availability? - foreach (var (culture, name) in content.Names) yield return new ContentVersionCultureVariationDto { VersionId = content.VersionId, - LanguageId = _languageRepository.GetIdByIsoCode(culture), + LanguageId = culture, Name = name }; @@ -969,14 +965,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement yield return new ContentVersionCultureVariationDto { VersionId = content.PublishedVersionId, - LanguageId = _languageRepository.GetIdByIsoCode(culture), + LanguageId = culture, Name = name }; } private class CultureVariation { - public string LanguageId { get; set; } + public int LanguageId { get; set; } public string Name { get; set; } public bool Available { get; set; } } diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 70fbea5b4d..951f30d8f1 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -159,7 +159,10 @@ namespace Umbraco.Tests.Models var contentType = new ContentType(-1) { Alias = "contentType" }; var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; - Assert.Throws(() => content.SetName("fr-FR", "name-fr")); + const int langFr = 1; + const int langUk = 2; + + Assert.Throws(() => content.SetName(langFr, "name-fr")); contentType.Variations = ContentVariation.CultureNeutral; @@ -169,17 +172,17 @@ namespace Umbraco.Tests.Models Assert.AreEqual("name2", content.Name); Assert.AreEqual("name2", content.GetName(null)); - content.SetName("fr-FR", "name-fr"); - content.SetName("en-UK", "name-uk"); + content.SetName(langFr, "name-fr"); + content.SetName(langUk, "name-uk"); - Assert.AreEqual("name-fr", content.GetName("fr-FR")); - Assert.AreEqual("name-uk", content.GetName("en-UK")); + Assert.AreEqual("name-fr", content.GetName(langFr)); + Assert.AreEqual("name-uk", content.GetName(langUk)); Assert.AreEqual(2, content.Names.Count); - Assert.IsTrue(content.Names.ContainsKey("fr-FR")); - Assert.AreEqual("name-fr", content.Names["fr-FR"]); - Assert.IsTrue(content.Names.ContainsKey("en-UK")); - Assert.AreEqual("name-uk", content.Names["en-UK"]); + Assert.IsTrue(content.Names.ContainsKey(langFr)); + Assert.AreEqual("name-fr", content.Names[langFr]); + Assert.IsTrue(content.Names.ContainsKey(langUk)); + Assert.AreEqual("name-uk", content.Names[langUk]); } [Test] diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 20d68fabf9..f613fcc653 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2492,48 +2492,137 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var content = contentService.Create("Home US", - 1, "umbTextpage"); + + // act content.SetValue("author", "Barack Obama"); - content.SetName("fr-FR", "name-fr"); - content.SetName("en-UK", "name-uk"); - + content.SetName(langFr.Id, "name-fr"); + content.SetName(langUk.Id, "name-uk"); contentService.Save(content); + + // content has been saved, + // it has names, but no publishNames, and no published cultures var content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName("fr-FR")); - Assert.AreEqual("name-uk", content2.GetName("en-UK")); + Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); + + Assert.IsNull(content2.PublishName); + Assert.IsNull(content2.GetPublishName(langFr.Id)); + Assert.IsNull(content2.GetPublishName(langUk.Id)); + + Assert.IsFalse(content.IsCulturePublished(langFr.Id)); + Assert.IsFalse(content.IsCulturePublished(langUk.Id)); + + // act content.PublishValues(langFr.Id); content.PublishValues(langUk.Id); - contentService.SaveAndPublish(content); + contentService.SaveAndPublish(content); + + // both FR and UK have been published, + // and content has been published, + // it has names, publishNames, and published cultures content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName("fr-FR")); - Assert.AreEqual("name-uk", content2.GetName("en-UK")); + Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("name-fr", content2.GetPublishName("fr-FR")); - Assert.AreEqual("name-uk", content2.GetPublishName("en-UK")); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + + Assert.IsTrue(content.IsCulturePublished(langFr.Id)); + Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + + // act content.SetName(null, "Home US2"); - content.SetName("fr-FR", "name-fr2"); - content.SetName("en-UK", "name-uk2"); - - contentService.Save(content); + content.SetName(langFr.Id, "name-fr2"); + content.SetName(langUk.Id, "name-uk2"); + contentService.Save(content); + + // content has been saved, + // it has updated names, unchanged publishNames, and published cultures content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName("fr-FR")); - Assert.AreEqual("name-uk2", content2.GetName("en-UK")); + Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("name-fr", content2.GetPublishName("fr-FR")); - Assert.AreEqual("name-uk", content2.GetPublishName("en-UK")); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + + Assert.IsTrue(content.IsCulturePublished(langFr.Id)); + Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + + // act + // cannot just 'save' since we are changing what's published! + + content.ClearPublishedValues(langFr.Id); + contentService.SaveAndPublish(content); + + // content has been published, + // it has -- THIS IS WHERE IT BECOMES WEIRD -- FIXME + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + + Assert.AreEqual("Home US2", content2.PublishName); // fixme why? + Assert.IsNull(content2.GetPublishName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + + Assert.IsFalse(content.IsCulturePublished(langFr.Id)); + Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + + // act + + contentService.Unpublish(content); + + // content has been unpublished, + // ... + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + + Assert.IsNotNull(content2.PublishName); // fixme not null?! why? + Assert.IsNull(content2.GetPublishName(langFr.Id)); + Assert.IsNotNull(content2.GetPublishName(langUk.Id)); // fixme not null?! why? because the 'published' values are not going away! + + Assert.IsFalse(content.IsCulturePublished(langFr.Id)); + Assert.IsTrue(content.IsCulturePublished(langUk.Id)); // fixme ?? + + // act + + contentService.SaveAndPublish(content); + + // content has been re-published, + // ... + + content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US2", content2.Name); + Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + + Assert.AreEqual("Home US2", content2.PublishName); // fixme US2? see above + Assert.IsNull(content2.GetPublishName(langFr.Id)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + + Assert.IsFalse(content.IsCulturePublished(langFr.Id)); + Assert.IsTrue(content.IsCulturePublished(langUk.Id)); } private IEnumerable CreateContentHierarchy() diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 0bcb218fb0..ab52cc875f 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -445,7 +445,7 @@ namespace Umbraco.Web.Editors { var permissions = Services.UserService .GetPermissions(Security.CurrentUser, nodeIds); - + var permissionsDictionary = new Dictionary(); foreach (var nodeId in nodeIds) { @@ -557,7 +557,7 @@ namespace Umbraco.Web.Editors } return contentItemDisplay; } - + private ContentItemDisplay PostSaveInternal(ContentItemSave contentItem, Func saveMethod) { //If we've reached here it means: @@ -618,7 +618,7 @@ namespace Umbraco.Web.Editors { //publish the item and check if it worked, if not we will show a diff msg below contentItem.PersistedContent.PublishValues(contentItem.LanguageId); //we are not checking for a return value here because we've alraedy pre-validated the property values - + //check if we are publishing other variants and validate them var allLangs = Services.LocalizationService.GetAllLanguages().ToList(); var variantsToValidate = contentItem.PublishVariations.Where(x => x.LanguageId != contentItem.LanguageId).ToList(); @@ -651,7 +651,7 @@ namespace Umbraco.Web.Editors //get the updated model var display = MapToDisplay(contentItem.PersistedContent, contentItem.LanguageId); - + //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403 HandleInvalidModelState(display); @@ -1150,7 +1150,7 @@ namespace Umbraco.Web.Editors //a languageId must exist in the mapping context if this content item has any property type that can be varied by language //otherwise the property validation will fail since it's expecting to be get/set with a language ID. If a languageId is not explicitly //sent up, then it means that the user is editing the default variant language. - if (!languageId.HasValue && content.HasLanguageVariantPropertyType()) + if (!languageId.HasValue && content.HasPropertyTypeVaryingByCulture()) { languageId = Services.LocalizationService.GetDefaultVariantLanguage().Id; } @@ -1160,6 +1160,6 @@ namespace Umbraco.Web.Editors return display; } - + } } diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs index befada9020..4a016a895b 100644 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ b/src/Umbraco.Web/Models/ContentExtensions.cs @@ -11,30 +11,6 @@ namespace Umbraco.Web.Models { public static class ContentExtensions { - /// - /// Returns true if the content has any property type that allows language variants - /// - /// - /// - public static bool HasLanguageVariantPropertyType(this IContent content) - { - return content.PropertyTypes.Any(x => x.Variations == ContentVariation.CultureNeutral); - } - - /// - /// Returns true if the content has a variation for the language/segment combination - /// - /// - /// - /// - /// - public static bool HasVariation(this IContent content, int langId, string segment = null) - { - //TODO: Wire up with new APIs - return false; - //return content.Languages.FirstOrDefault(x => x == langId); - } - /// /// Gets the culture that would be selected to render a specified content, /// within the context of a specified current request. diff --git a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs b/src/Umbraco.Web/Models/Mapping/VariationResolver.cs index 07ddd7363b..7106b58fbd 100644 --- a/src/Umbraco.Web/Models/Mapping/VariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/VariationResolver.cs @@ -29,9 +29,8 @@ namespace Umbraco.Web.Models.Mapping { Language = x, Mandatory = x.Mandatory, - //fixme these all need to the variant values but we need to wait for the db/service changes - Name = source.GetName(x.IsoCode), - Exists = source.HasVariation(x.Id), //TODO: This needs to be wired up with new APIs when they are ready + Name = source.GetName(x.Id), + Exists = source.HasVariation(x.Id), // fixme - what's the purpose? "exists" or "published"? exists is a new thing? PublishedState = source.PublishedState.ToString(), //Segment = ?? We'll need to populate this one day when we support segments }).ToList();