diff --git a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs
new file mode 100644
index 0000000000..11f123f2d0
--- /dev/null
+++ b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Umbraco.Core.Collections
+{
+ ///
+ /// Represents a composite key of (string, string) for fast dictionaries.
+ ///
+ ///
+ /// The string parts of the key are case-insensitive.
+ /// Null is a valid value for both parts.
+ ///
+ public struct CompositeStringStringKey : IEquatable
+ {
+ private readonly string _key1;
+ private readonly string _key2;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ public CompositeStringStringKey(string key1, string key2)
+ {
+ _key1 = key1?.ToLowerInvariant() ?? "NULL";
+ _key2 = key2?.ToLowerInvariant() ?? "NULL";
+ }
+
+ public bool Equals(CompositeStringStringKey other)
+ => _key2 == other._key2 && _key1 == other._key1;
+
+ public override bool Equals(object obj)
+ => obj is CompositeStringStringKey other && _key2 == other._key2 && _key1 == other._key1;
+
+ public override int GetHashCode()
+ => _key2.GetHashCode() * 31 + _key1.GetHashCode();
+
+ public static bool operator ==(CompositeStringStringKey key1, CompositeStringStringKey key2)
+ => key1._key2 == key2._key2 && key1._key1 == key2._key1;
+
+ public static bool operator !=(CompositeStringStringKey key1, CompositeStringStringKey key2)
+ => key1._key2 != key2._key2 || key1._key1 != key2._key1;
+ }
+}
diff --git a/src/Umbraco.Core/EnumExtensions.cs b/src/Umbraco.Core/EnumExtensions.cs
new file mode 100644
index 0000000000..58e14bcadf
--- /dev/null
+++ b/src/Umbraco.Core/EnumExtensions.cs
@@ -0,0 +1,22 @@
+using Umbraco.Core.Models;
+
+namespace Umbraco.Core
+{
+ ///
+ /// Provides extension methods for various enumerations.
+ ///
+ public static class EnumExtensions
+ {
+ ///
+ /// Determines whether a variation has all flags set.
+ ///
+ public static bool Has(this ContentVariation variation, ContentVariation values)
+ => (variation & values) == values;
+
+ ///
+ /// Determines whether a variation has at least a flag set.
+ ///
+ public static bool HasAny(this ContentVariation variation, ContentVariation values)
+ => (variation & values) != ContentVariation.Unknown;
+ }
+}
diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs
index b96b0d54bc..5534936a04 100644
--- a/src/Umbraco.Core/IO/MediaFileSystem.cs
+++ b/src/Umbraco.Core/IO/MediaFileSystem.cs
@@ -335,35 +335,35 @@ namespace Umbraco.Core.IO
// fixme - what's below belongs to the upload property editor, not the media filesystem!
- public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null)
+ public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
{
var property = GetProperty(content, propertyTypeAlias);
- var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null;
+ var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null;
var filepath = StoreFile(content, property.PropertyType, filename, filestream, oldpath);
- property.SetValue(GetUrl(filepath), languageId, segment);
- SetUploadFile(content, property, filepath, filestream, languageId, segment);
+ property.SetValue(GetUrl(filepath), culture, segment);
+ SetUploadFile(content, property, filepath, filestream, culture, segment);
}
- public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, int? languageId = null, string segment = null)
+ public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, string culture = null, string segment = null)
{
var property = GetProperty(content, propertyTypeAlias);
// fixme delete?
- var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null;
+ var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null;
if (string.IsNullOrWhiteSpace(oldpath) == false && oldpath != filepath)
DeleteFile(oldpath);
- property.SetValue(GetUrl(filepath), languageId, segment);
+ property.SetValue(GetUrl(filepath), culture, segment);
using (var filestream = OpenFile(filepath))
{
- SetUploadFile(content, property, filepath, filestream, languageId, segment);
+ SetUploadFile(content, property, filepath, filestream, culture, segment);
}
}
// sets a file for the FileUpload property editor
// ie generates thumbnails and populates autofill properties
- private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, int? languageId = null, string segment = null)
+ private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, string culture = null, string segment = null)
{
// will use filepath for extension, and filestream for length
- UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, languageId, segment);
+ UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, culture, segment);
}
#endregion
diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs
index 3fdaf7b3d3..4ab2bf7a7c 100644
--- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs
+++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs
@@ -41,9 +41,9 @@ namespace Umbraco.Core.Media
///
/// The content item.
/// The property type alias.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Reset(IContentBase content, string propertyTypeAlias, int? languageId, string segment)
+ public void Reset(IContentBase content, string propertyTypeAlias, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias));
@@ -53,7 +53,7 @@ namespace Umbraco.Core.Media
if (autoFillConfig == null) return; // nothing
// reset
- Reset(content, autoFillConfig, languageId, segment);
+ Reset(content, autoFillConfig, culture, segment);
}
///
@@ -61,14 +61,14 @@ namespace Umbraco.Core.Media
///
/// The content item.
/// The auto-fill configuration.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment)
+ public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
- ResetProperties(content, autoFillConfig, languageId, segment);
+ ResetProperties(content, autoFillConfig, culture, segment);
}
///
@@ -77,9 +77,9 @@ namespace Umbraco.Core.Media
/// The content item.
/// The property type alias.
/// The filesystem-relative filepath, or null to clear properties.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Populate(IContentBase content, string propertyTypeAlias, string filepath, int? languageId, string segment)
+ public void Populate(IContentBase content, string propertyTypeAlias, string filepath, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias));
@@ -92,7 +92,7 @@ namespace Umbraco.Core.Media
if (autoFillConfig == null) return; // nothing
// populate
- Populate(content, autoFillConfig, filepath, languageId, segment);
+ Populate(content, autoFillConfig, filepath, culture, segment);
}
///
@@ -102,9 +102,9 @@ namespace Umbraco.Core.Media
/// The property type alias.
/// The filesystem-relative filepath, or null to clear properties.
/// The stream containing the file data.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, int? languageId, string segment)
+ public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias));
@@ -117,7 +117,7 @@ namespace Umbraco.Core.Media
if (autoFillConfig == null) return; // nothing
// populate
- Populate(content, autoFillConfig, filepath, filestream, languageId, segment);
+ Populate(content, autoFillConfig, filepath, filestream, culture, segment);
}
///
@@ -127,9 +127,9 @@ namespace Umbraco.Core.Media
/// The auto-fill configuration.
/// The filesystem path to the uploaded file.
/// The parameter is the path relative to the filesystem.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, int? languageId, string segment)
+ public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
@@ -137,7 +137,7 @@ namespace Umbraco.Core.Media
// no file = reset, file = auto-fill
if (filepath.IsNullOrWhiteSpace())
{
- ResetProperties(content, autoFillConfig, languageId, segment);
+ ResetProperties(content, autoFillConfig, culture, segment);
}
else
{
@@ -148,13 +148,13 @@ namespace Umbraco.Core.Media
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
var size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null;
- SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment);
+ SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
}
}
catch (Exception ex)
{
_logger.Error(typeof(UploadAutoFillProperties), $"Could not populate upload auto-fill properties for file \"{filepath}\".", ex);
- ResetProperties(content, autoFillConfig, languageId, segment);
+ ResetProperties(content, autoFillConfig, culture, segment);
}
}
}
@@ -166,9 +166,9 @@ namespace Umbraco.Core.Media
///
/// The filesystem-relative filepath, or null to clear properties.
/// The stream containing the file data.
- /// Variation language.
+ /// Variation language.
/// Variation segment.
- public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, int? languageId, string segment)
+ public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
@@ -176,50 +176,50 @@ namespace Umbraco.Core.Media
// no file = reset, file = auto-fill
if (filepath.IsNullOrWhiteSpace() || filestream == null)
{
- ResetProperties(content, autoFillConfig, languageId, segment);
+ ResetProperties(content, autoFillConfig, culture, segment);
}
else
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
var size = _mediaFileSystem.IsImageFile(extension) ? (Size?)_mediaFileSystem.GetDimensions(filestream) : null;
- SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment);
+ SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment);
}
}
- private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, int? languageId, string segment)
+ private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
- content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment);
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
- content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment);
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
- content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, languageId, segment);
+ content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment);
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
- content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, languageId, segment);
+ content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment);
}
- private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment)
+ private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig));
if (content.Properties.Contains(autoFillConfig.WidthFieldAlias))
- content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, culture, segment);
if (content.Properties.Contains(autoFillConfig.HeightFieldAlias))
- content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, culture, segment);
if (content.Properties.Contains(autoFillConfig.LengthFieldAlias))
- content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, culture, segment);
if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias))
- content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, languageId, segment);
+ content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, culture, segment);
}
}
}
diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
index 6ce300845c..7d4058a0e4 100644
--- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
+++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs
@@ -82,7 +82,8 @@ namespace Umbraco.Core.Migrations.Install
typeof (UserLoginDto),
typeof (ConsentDto),
typeof (AuditEntryDto),
- typeof (ContentVersionCultureVariationDto)
+ typeof (ContentVersionCultureVariationDto),
+ typeof (DocumentCultureVariationDto)
};
///
diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
index 8bec74173e..4954742908 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
@@ -116,10 +116,11 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain("{9DF05B77-11D1-475C-A00A-B656AF7E0908}");
Chain("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}");
Chain("{7F59355A-0EC9-4438-8157-EB517E6D2727}");
- Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}");
+ Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}");
+ Chain("{49506BAE-CEBB-4431-A1A6-24AD6EBBBC57}");
// must chain to v8 final state (see at end of file)
- Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}");
+ Chain("{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}");
// UPGRADE FROM 7, MORE RECENT
@@ -201,12 +202,17 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain("{79591E91-01EA-43F7-AC58-7BD286DB1E77}");
// 8.0.0
- Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}");
+ // AddVariationTables1 has been superceeded by AddVariationTables2
+ //Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}");
+ Chain("{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}");
+
+ // however, need to take care of ppl in post-AddVariationTables1 state
+ Add("{941B2ABA-2D06-4E04-81F5-74224F1DB037}", "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}");
// FINAL STATE - MUST MATCH LAST ONE ABOVE !
// whenever this changes, update all references in this file!
- Add(string.Empty, "{941B2ABA-2D06-4E04-81F5-74224F1DB037}");
+ Add(string.Empty, "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}");
}
}
}
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs
new file mode 100644
index 0000000000..0fce88da46
--- /dev/null
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs
@@ -0,0 +1,47 @@
+using System;
+using Umbraco.Core.Persistence.Dtos;
+
+namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
+{
+ public class AddVariationTables1A : MigrationBase
+ {
+ public AddVariationTables1A(IMigrationContext context)
+ : base(context)
+ { }
+
+ // note - original AddVariationTables1 just did
+ // Create.Table().Do();
+ //
+ // this is taking care of ppl left in this state
+
+ public override void Migrate()
+ {
+ // note - original AddVariationTables1 just did
+ // Create.Table().Do();
+ //
+ // it's been deprecated, not part of the main upgrade path,
+ // but we need to take care of ppl caught into the state
+
+ // was not used
+ Delete.Column("available").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do();
+
+ // was not used
+ Delete.Column("availableDate").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do();
+
+ //special trick to add the column without constraints and return the sql to add them later
+ AddColumn("date", out var sqls);
+ //now we need to update the new column with some values because this column doesn't allow NULL values
+ Update.Table(ContentVersionCultureVariationDto.TableName).Set(new {date = DateTime.Now}).AllRows().Do();
+ //now apply constraints (NOT NULL) to new table
+ foreach (var sql in sqls) Database.Execute(sql);
+
+ // name, languageId are now non-nullable
+ AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "name");
+ AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "languageId");
+
+ Create.Table().Do();
+
+ // fixme - data migration?
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs
similarity index 54%
rename from src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs
rename to src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs
index efd0f33e99..5b6c913195 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs
@@ -1,16 +1,18 @@
-using Umbraco.Core.Persistence.Dtos;
+using System;
+using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
- public class AddContentVariationTable : MigrationBase
+ public class AddVariationTables2 : MigrationBase
{
- public AddContentVariationTable(IMigrationContext context)
+ public AddVariationTables2(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
Create.Table().Do();
+ Create.Table().Do();
// fixme - data migration?
}
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index f5a38a96cb..4ead233d6a 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -20,7 +20,8 @@ namespace Umbraco.Core.Models
private PublishedState _publishedState;
private DateTime? _releaseDate;
private DateTime? _expireDate;
- private Dictionary _publishNames;
+ private Dictionary _publishInfos;
+ private HashSet _edited;
private static readonly Lazy Ps = new Lazy();
@@ -195,73 +196,109 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
public ITemplate PublishTemplate { get; internal set; }
-
+
[IgnoreDataMember]
public string PublishName { get; internal set; }
- ///
- [IgnoreDataMember]
- public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames;
-
- ///
- public string GetPublishName(int? languageId)
- {
- if (languageId == null) return PublishName;
- if (_publishNames == null) return null;
- return _publishNames.TryGetValue(languageId.Value, out var name) ? name : null;
- }
-
- // sets a publish name
+ // sets publish infos
// internal for repositories
- internal void SetPublishName(int? languageId, string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullOrEmptyException(nameof(name));
+ // clear by clearing name
+ internal void SetPublishInfos(string culture, string name, DateTime date)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullOrEmptyException(nameof(name));
- if (languageId == null)
+ if (culture == null)
{
PublishName = name;
+ PublishDate = date;
return;
}
// private method, assume that culture is valid
- if (_publishNames == null)
- _publishNames = new Dictionary();
+ if (_publishInfos == null)
+ _publishInfos = new Dictionary(StringComparer.OrdinalIgnoreCase);
- _publishNames[languageId.Value] = name;
+ _publishInfos[culture] = (name, date);
}
-
- // clears a publish name
- private void ClearPublishName(int? languageId)
+
+ ///
+ [IgnoreDataMember]
+ //public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames;
+ public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames;
+
+ ///
+ public string GetPublishName(string culture)
{
- if (languageId == null)
+ if (culture == null) return PublishName;
+ if (_publishInfos == null) return null;
+ return _publishInfos.TryGetValue(culture, out var infos) ? infos.Name : null;
+ }
+
+ // clears a publish name
+ private void ClearPublishName(string culture)
+ {
+ if (culture == null)
{
PublishName = null;
return;
}
- if (_publishNames == null) return;
- _publishNames.Remove(languageId.Value);
- if (_publishNames.Count == 0)
- _publishNames = null;
+ if (_publishInfos == null) return;
+ _publishInfos.Remove(culture);
+ if (_publishInfos.Count == 0)
+ _publishInfos = null;
}
-
+
// clears all publish names
private void ClearPublishNames()
{
PublishName = null;
- _publishNames = null;
+ _publishInfos = null;
}
///
- public bool IsCultureAvailable(int? languageId)
- => !string.IsNullOrWhiteSpace(GetName(languageId));
-
+ public bool IsCulturePublished(string culture)
+ => !string.IsNullOrWhiteSpace(GetPublishName(culture));
+
///
- public bool IsCulturePublished(int? languageId)
- => !string.IsNullOrWhiteSpace(GetPublishName(languageId));
-
+ public DateTime GetDateCulturePublished(string culture)
+ {
+ if (_publishInfos != null && _publishInfos.TryGetValue(culture, out var infos))
+ return infos.Date;
+ throw new InvalidOperationException($"Culture \"{culture}\" is not published.");
+ }
+
+ ///
+ public IEnumerable PublishedCultures => _publishInfos.Keys;
+
+ ///
+ public bool IsCultureEdited(string culture)
+ {
+ return string.IsNullOrWhiteSpace(GetPublishName(culture)) || (_edited != null && _edited.Contains(culture));
+ }
+
+ // sets a publish edited
+ internal void SetCultureEdited(string culture)
+ {
+ if (_edited == null)
+ _edited = new HashSet(StringComparer.OrdinalIgnoreCase);
+ _edited.Add(culture);
+ }
+
+ // sets all publish edited
+ internal void SetCultureEdited(IEnumerable cultures)
+ {
+ _edited = new HashSet(cultures, StringComparer.OrdinalIgnoreCase);
+ }
+
+ ///
+ public IEnumerable EditedCultures => Names.Keys.Where(IsCultureEdited);
+
+ ///
+ public IEnumerable AvailableCultures => Names.Keys;
+
[IgnoreDataMember]
public int PublishedVersionId { get; internal set; }
@@ -280,73 +317,67 @@ namespace Umbraco.Core.Models
if (string.IsNullOrWhiteSpace(Name))
throw new InvalidOperationException($"Cannot publish invariant culture without a name.");
PublishName = Name;
- foreach (var (languageId, name) in Names)
+ var now = DateTime.Now;
+ foreach (var (culture, name) in Names)
{
if (string.IsNullOrWhiteSpace(name))
- throw new InvalidOperationException($"Cannot publish {languageId} culture without a name.");
- SetPublishName(languageId, name);
+ throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name.");
+ SetPublishInfos(culture, name, now);
}
-
// property.PublishAllValues only deals with supported variations (if any)
foreach (var property in Properties)
- 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.
- PublishName = Name;
- foreach (var (languageId, name) in Names)
- SetPublishName(languageId, name);
+ property.PublishAllValues();
_publishedState = PublishedState.Publishing;
return true;
}
///
- public virtual bool PublishValues(int? languageId = null, string segment = null)
- {
- // the variation should be supported by the content type
- ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true);
-
+ public virtual bool PublishValues(string culture = null, string segment = null)
+ {
+ // the variation should be supported by the content type
+ ContentType.ValidateVariation(culture, segment, throwIfInvalid: true);
+
// the values we want to publish should be valid
- if (Validate(languageId, segment).Any())
+ if (Validate(culture, segment).Any())
return false;
// Name and PublishName are managed by the repository, but Names and PublishNames
// must be managed here as they depend on the existing / supported variations.
if (segment == null)
{
- var name = GetName(languageId);
+ var name = GetName(culture);
if (string.IsNullOrWhiteSpace(name))
- throw new InvalidOperationException($"Cannot publish {languageId?.ToString() ?? "invariant"} culture without a name.");
- SetPublishName(languageId, name);
+ throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name.");
+ SetPublishInfos(culture, name, DateTime.Now);
}
-
+
// 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);
+ foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false)))
+ property.PublishValue(culture, segment);
_publishedState = PublishedState.Publishing;
return true;
}
///
- public virtual bool PublishCultureValues(int? languageId = null)
- {
+ public virtual bool PublishCultureValues(string culture = null)
+ {
// the values we want to publish should be valid
- if (ValidateCulture(languageId).Any())
+ if (ValidateCulture(culture).Any())
return false;
// Name and PublishName are managed by the repository, but Names and PublishNames
// must be managed here as they depend on the existing / supported variations.
- var name = GetName(languageId);
+ var name = GetName(culture);
if (string.IsNullOrWhiteSpace(name))
- throw new InvalidOperationException($"Cannot publish {languageId?.ToString() ?? "invariant"} culture without a name.");
- SetPublishName(languageId, name);
-
+ throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name.");
+ SetPublishInfos(culture, name, DateTime.Now);
+
// property.PublishCultureValues only deals with supported variations (if any)
foreach (var property in Properties)
- property.PublishCultureValues(languageId);
+ property.PublishCultureValues(culture);
_publishedState = PublishedState.Publishing;
return true;
@@ -361,42 +392,42 @@ namespace Umbraco.Core.Models
// 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();
-
+ ClearPublishNames();
+
_publishedState = PublishedState.Publishing;
}
///
- public virtual void ClearPublishedValues(int? languageId = null, string segment = null)
+ public virtual void ClearPublishedValues(string culture = null, string segment = null)
{
// the variation should be supported by the content type
- ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true);
-
+ ContentType.ValidateVariation(culture, 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);
+ foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false)))
+ property.ClearPublishedValue(culture, segment);
// 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);
+ ClearPublishName(culture);
_publishedState = PublishedState.Publishing;
}
///
- public virtual void ClearCulturePublishedValues(int? languageId = null)
+ public virtual void ClearCulturePublishedValues(string culture = null)
{
// property.ClearPublishedCultureValues only deals with supported variations (if any)
foreach (var property in Properties)
- property.ClearPublishedCultureValues(languageId);
+ property.ClearPublishedCultureValues(culture);
// 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);
-
+ ClearPublishName(culture);
+
_publishedState = PublishedState.Publishing;
}
-
+
private bool CopyingFromSelf(IContent other)
{
// copying from the same Id and VersionPk
@@ -419,8 +450,8 @@ namespace Umbraco.Core.Models
// clear all existing properties
foreach (var property in Properties)
foreach (var pvalue in property.Values)
- if (property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false))
- property.SetValue(null, pvalue.LanguageId, pvalue.Segment);
+ if (property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false))
+ property.SetValue(null, pvalue.Culture, pvalue.Segment);
// copy other properties
var otherProperties = other.Properties;
@@ -429,22 +460,22 @@ namespace Umbraco.Core.Models
var alias = otherProperty.PropertyType.Alias;
foreach (var pvalue in otherProperty.Values)
{
- if (!otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false))
+ if (!otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false))
continue;
var value = published ? pvalue.PublishedValue : pvalue.EditedValue;
- SetValue(alias, value, pvalue.LanguageId, pvalue.Segment);
+ SetValue(alias, value, pvalue.Culture, pvalue.Segment);
}
- }
-
+ }
+
// copy names
- ClearNames();
+ ClearNames();
foreach (var (languageId, name) in other.Names)
SetName(languageId, name);
Name = other.Name;
}
///
- public virtual void CopyValues(IContent other, int? languageId = null, string segment = null)
+ public virtual void CopyValues(IContent other, string culture = null, string segment = null)
{
if (other.ContentTypeId != ContentTypeId)
throw new InvalidOperationException("Cannot copy values from a different content type.");
@@ -459,31 +490,31 @@ namespace Umbraco.Core.Models
// clear all existing properties
foreach (var property in Properties)
{
- if (!property.PropertyType.ValidateVariation(languageId, segment, false))
+ if (!property.PropertyType.ValidateVariation(culture, segment, false))
continue;
foreach (var pvalue in property.Values)
- if (pvalue.LanguageId == languageId && pvalue.Segment == segment)
- property.SetValue(null, pvalue.LanguageId, pvalue.Segment);
+ if (pvalue.Culture.InvariantEquals(culture) && pvalue.Segment.InvariantEquals(segment))
+ property.SetValue(null, pvalue.Culture, pvalue.Segment);
}
// copy other properties
var otherProperties = other.Properties;
foreach (var otherProperty in otherProperties)
{
- if (!otherProperty.PropertyType.ValidateVariation(languageId, segment, false))
+ if (!otherProperty.PropertyType.ValidateVariation(culture, segment, false))
continue;
var alias = otherProperty.PropertyType.Alias;
- SetValue(alias, otherProperty.GetValue(languageId, segment, published), languageId, segment);
- }
+ SetValue(alias, otherProperty.GetValue(culture, segment, published), culture, segment);
+ }
- // copy name
- SetName(languageId, other.GetName(languageId));
+ // copy name
+ SetName(culture, other.GetName(culture));
}
///
- public virtual void CopyCultureValues(IContent other, int? languageId = null)
+ public virtual void CopyCultureValues(IContent other, string culture = null)
{
if (other.ContentTypeId != ContentTypeId)
throw new InvalidOperationException("Cannot copy values from a different content type.");
@@ -495,8 +526,8 @@ namespace Umbraco.Core.Models
// clear all existing properties
foreach (var property in Properties)
foreach (var pvalue in property.Values)
- if (pvalue.LanguageId == languageId && property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false))
- property.SetValue(null, pvalue.LanguageId, pvalue.Segment);
+ if (pvalue.Culture.InvariantEquals(culture) && property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false))
+ property.SetValue(null, pvalue.Culture, pvalue.Segment);
// copy other properties
var otherProperties = other.Properties;
@@ -505,15 +536,15 @@ namespace Umbraco.Core.Models
var alias = otherProperty.PropertyType.Alias;
foreach (var pvalue in otherProperty.Values)
{
- if (pvalue.LanguageId != languageId || !otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false))
+ if (pvalue.Culture != culture || !otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false))
continue;
var value = published ? pvalue.PublishedValue : pvalue.EditedValue;
- SetValue(alias, value, pvalue.LanguageId, pvalue.Segment);
+ SetValue(alias, value, pvalue.Culture, pvalue.Segment);
}
- }
-
- // copy name
- SetName(languageId, other.GetName(languageId));
+ }
+
+ // copy name
+ SetName(culture, other.GetName(culture));
}
///
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 8203cba985..7bc327c2d0 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -18,14 +18,14 @@ namespace Umbraco.Core.Models
[DebuggerDisplay("Id: {Id}, Name: {Name}, ContentType: {ContentTypeBase.Alias}")]
public abstract class ContentBase : TreeEntityBase, IContentBase
{
- protected static readonly Dictionary NoNames = new Dictionary();
+ protected static readonly Dictionary NoNames = new Dictionary();
private static readonly Lazy Ps = new Lazy();
private int _contentTypeId;
protected IContentTypeComposition ContentTypeBase;
private int _writerId;
private PropertyCollection _properties;
- private Dictionary _names;
+ private Dictionary _names;
///
/// Initializes a new instance of the class.
@@ -67,7 +67,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);
- 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)
@@ -121,71 +121,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- [DataMember]
- public virtual IReadOnlyDictionary Names
- {
- get => _names ?? NoNames;
- set
- {
- foreach (var (languageId, name) in value)
- SetName(languageId, name);
- }
- }
-
- ///
- public virtual void SetName(int? languageId, string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- ClearName(languageId);
- return;
- }
-
- if (languageId == null)
- {
- Name = name;
- return;
- }
-
- if ((ContentTypeBase.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) == 0)
- throw new NotSupportedException("Content type does not support varying name by culture.");
-
- if (_names == null)
- _names = new Dictionary();
-
- _names[languageId.Value] = name;
- OnPropertyChanged(Ps.Value.NamesSelector);
- }
-
- private void ClearName(int? languageId)
- {
- if (languageId == null)
- {
- Name = null;
- return;
- }
-
- if (_names == null) return;
- _names.Remove(languageId.Value);
- if (_names.Count == 0)
- _names = null;
- }
-
- protected virtual void ClearNames()
- {
- _names = null;
- OnPropertyChanged(Ps.Value.NamesSelector);
- }
-
- ///
- public virtual string GetName(int? languageId)
- {
- if (languageId == null) return Name;
- if (_names == null) return null;
- return _names.TryGetValue(languageId.Value, out var name) ? name : null;
- }
-
///
/// Gets the enumeration of property groups for the entity.
/// fixme is a proxy, kill this
@@ -199,6 +134,79 @@ namespace Umbraco.Core.Models
///
[IgnoreDataMember]
public IEnumerable PropertyTypes => ContentTypeBase.CompositionPropertyTypes;
+
+ #region Cultures
+
+ ///
+ [DataMember]
+ public virtual IReadOnlyDictionary Names
+ {
+ get => _names ?? NoNames;
+ set
+ {
+ foreach (var (culture, name) in value)
+ SetName(culture, name);
+ }
+ }
+
+ ///
+ public virtual void SetName(string culture, string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ ClearName(culture);
+ return;
+ }
+
+ if (culture == null)
+ {
+ Name = name;
+ return;
+ }
+
+ if (!ContentTypeBase.Variations.HasAny(ContentVariation.CultureNeutral | ContentVariation.CultureSegment))
+ throw new NotSupportedException("Content type does not support varying name by culture.");
+
+ if (_names == null)
+ _names = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ _names[culture] = name;
+ OnPropertyChanged(Ps.Value.NamesSelector);
+ }
+
+ ///
+ public virtual string GetName(string culture)
+ {
+ if (culture == null) return Name;
+ if (_names == null) return null;
+ return _names.TryGetValue(culture, out var name) ? name : null;
+ }
+
+ ///
+ public bool IsCultureAvailable(string culture)
+ => !string.IsNullOrWhiteSpace(GetName(culture));
+
+ private void ClearName(string culture)
+ {
+ if (culture == null)
+ {
+ Name = null;
+ return;
+ }
+
+ if (_names == null) return;
+ _names.Remove(culture);
+ if (_names.Count == 0)
+ _names = null;
+ }
+
+ protected virtual void ClearNames()
+ {
+ _names = null;
+ OnPropertyChanged(Ps.Value.NamesSelector);
+ }
+
+ #endregion
#region Has, Get, Set, Publish Property Value
@@ -207,29 +215,29 @@ namespace Umbraco.Core.Models
=> Properties.Contains(propertyTypeAlias);
///
- public virtual object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false)
+ public virtual object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false)
{
return Properties.TryGetValue(propertyTypeAlias, out var property)
- ? property.GetValue(languageId, segment, published)
+ ? property.GetValue(culture, segment, published)
: null;
}
///
- public virtual TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false)
+ public virtual TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false)
{
if (!Properties.TryGetValue(propertyTypeAlias, out var property))
return default;
- var convertAttempt = property.GetValue(languageId, segment, published).TryConvertTo();
+ var convertAttempt = property.GetValue(culture, segment, published).TryConvertTo();
return convertAttempt.Success ? convertAttempt.Result : default;
}
///
- public virtual void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null)
+ public virtual void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null)
{
if (Properties.Contains(propertyTypeAlias))
{
- Properties[propertyTypeAlias].SetValue(value, languageId, segment);
+ Properties[propertyTypeAlias].SetValue(value, culture, segment);
return;
}
@@ -238,7 +246,7 @@ namespace Umbraco.Core.Models
throw new InvalidOperationException($"No PropertyType exists with the supplied alias \"{propertyTypeAlias}\".");
var property = propertyType.CreateProperty();
- property.SetValue(value, languageId, segment);
+ property.SetValue(value, culture, segment);
Properties.Add(property);
}
@@ -249,17 +257,17 @@ namespace Umbraco.Core.Models
///
/// Sets the posted file value of a property.
///
- public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, int? languageId = null, string segment = null)
+ public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, string culture = null, string segment = null)
{
- ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), languageId, segment);
+ ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), culture, segment);
}
///
/// Sets the posted file value of a property.
///
- public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null)
+ public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null)
{
- ContentExtensions.SetValue(this, propertyTypeAlias, value, languageId, segment);
+ ContentExtensions.SetValue(this, propertyTypeAlias, value, culture, segment);
}
#endregion
@@ -271,14 +279,14 @@ namespace Umbraco.Core.Models
return Properties.Where(x => !x.IsAllValid()).ToArray();
}
- public virtual Property[] Validate(int? languageId = null, string segment = null)
+ public virtual Property[] Validate(string culture = null, string segment = null)
{
- return Properties.Where(x => !x.IsValid(languageId, segment)).ToArray();
+ return Properties.Where(x => !x.IsValid(culture, segment)).ToArray();
}
- public virtual Property[] ValidateCulture(int? languageId = null)
+ public virtual Property[] ValidateCulture(string culture = null)
{
- return Properties.Where(x => !x.IsCultureValid(languageId)).ToArray();
+ return Properties.Where(x => !x.IsCultureValid(culture)).ToArray();
}
#endregion
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 5b7e825d68..0940675346 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -284,7 +284,7 @@ namespace Umbraco.Core.Models
///
/// Sets the posted file value of a property.
///
- public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null)
+ public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null)
{
// ensure we get the filename without the path in IE in intranet mode
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
@@ -303,7 +303,7 @@ namespace Umbraco.Core.Models
if (string.IsNullOrWhiteSpace(filename)) return;
filename = filename.ToLower(); // fixme - er... why?
- MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, languageId, segment);
+ MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, culture, segment);
}
///
@@ -312,7 +312,7 @@ namespace Umbraco.Core.Models
/// This really is for FileUpload fields only, and should be obsoleted. For anything else,
/// you need to store the file by yourself using Store and then figure out
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.
- public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null)
+ public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
{
if (filename == null || filestream == null) return;
@@ -321,7 +321,7 @@ namespace Umbraco.Core.Models
if (string.IsNullOrWhiteSpace(filename)) return;
filename = filename.ToLower(); // fixme - er... why?
- MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, languageId, segment);
+ MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, culture, segment);
}
///
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index b686b97108..952fa0cd88 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -204,10 +204,10 @@ namespace Umbraco.Core.Models
///
/// Validates that a variation is valid for the content type.
///
- public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid)
+ public bool ValidateVariation(string culture, string segment, bool throwIfInvalid)
{
ContentVariation variation;
- if (languageId.HasValue)
+ if (culture != null)
{
variation = segment != null
? ContentVariation.CultureSegment
@@ -221,7 +221,7 @@ namespace Umbraco.Core.Models
{
variation = ContentVariation.InvariantNeutral;
}
- if ((Variations & variation) == 0)
+ if (!Variations.Has(variation))
{
if (throwIfInvalid)
throw new NotSupportedException($"Variation {variation} is invalid for content type \"{Alias}\".");
diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs
index 3f7a335620..25f980a597 100644
--- a/src/Umbraco.Core/Models/IContent.cs
+++ b/src/Umbraco.Core/Models/IContent.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Models
/// Gets the date and time the content was published.
///
DateTime? PublishDate { get; }
-
+
///
/// Gets or sets the date and time the content item should be published.
///
@@ -80,15 +80,6 @@ namespace Umbraco.Core.Models
///
ContentStatus Status { get; }
- ///
- /// Gets a value indicating whether a given culture is available.
- ///
- ///
- /// A culture becomes available whenever the content name for this culture is
- /// non-null, and it becomes unavailable whenever the content name is null.
- ///
- bool IsCultureAvailable(int? languageId);
-
///
/// Gets a value indicating whether a given culture is published.
///
@@ -97,17 +88,31 @@ namespace Umbraco.Core.Models
/// and the content published name for this culture is non-null. It becomes non-published
/// whenever values for this culture are unpublished.
///
- bool IsCulturePublished(int? languageId);
+ bool IsCulturePublished(string culture);
+
+ ///
+ /// Gets the date a culture was published.
+ ///
+ DateTime GetDateCulturePublished(string culture);
+
+ ///
+ /// Gets a value indicated whether a given culture is edited.
+ ///
+ ///
+ /// A culture is edited when it is not published, or when it is published but
+ /// it has changes.
+ ///
+ bool IsCultureEdited(string culture);
///
/// Gets the name of the published version of the content for a given culture.
///
///
/// When editing the content, the name can change, but this will not until the content is published.
- /// When is null, gets the invariant
+ /// When is null, gets the invariant
/// language, which is the value of the property.
///
- string GetPublishName(int? languageId);
+ string GetPublishName(string culture);
///
/// Gets the published names of the content.
@@ -116,7 +121,22 @@ 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; }
+
+ ///
+ /// Gets the available cultures.
+ ///
+ IEnumerable AvailableCultures { get; }
+
+ ///
+ /// Gets the published cultures.
+ ///
+ IEnumerable PublishedCultures { get; }
+
+ ///
+ /// Gets the edited cultures.
+ ///
+ IEnumerable EditedCultures { get; }
// fixme - these two should move to some kind of service
@@ -159,7 +179,7 @@ namespace Umbraco.Core.Models
/// The document must then be published via the content service.
/// Values are not published if they are not valid.
///
- bool PublishValues(int? languageId = null, string segment = null);
+ bool PublishValues(string culture = null, string segment = null);
///
/// Publishes the culture/any values.
@@ -169,7 +189,7 @@ namespace Umbraco.Core.Models
/// The document must then be published via the content service.
/// Values are not published if they are not valie.
///
- bool PublishCultureValues(int? languageId = null);
+ bool PublishCultureValues(string culture = null);
///
/// Clears all published values.
@@ -179,12 +199,12 @@ namespace Umbraco.Core.Models
///
/// Clears published values.
///
- void ClearPublishedValues(int? languageId = null, string segment = null);
+ void ClearPublishedValues(string culture = null, string segment = null);
///
/// Clears the culture/any published values.
///
- void ClearCulturePublishedValues(int? languageId = null);
+ void ClearCulturePublishedValues(string culture = null);
///
/// Copies values from another document.
@@ -194,11 +214,11 @@ namespace Umbraco.Core.Models
///
/// Copies values from another document.
///
- void CopyValues(IContent other, int? languageId = null, string segment = null);
+ void CopyValues(IContent other, string culture = null, string segment = null);
///
/// Copies culture/any values from another document.
///
- void CopyCultureValues(IContent other, int? languageId = null);
+ void CopyCultureValues(IContent other, string culture = null);
}
}
diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs
index aee8acd35e..93a6e82ada 100644
--- a/src/Umbraco.Core/Models/IContentBase.cs
+++ b/src/Umbraco.Core/Models/IContentBase.cs
@@ -31,19 +31,19 @@ namespace Umbraco.Core.Models
/// Sets the name of the content item for a specified language.
///
///
- /// When is null, sets the invariant
+ /// When is null, sets the invariant
/// language, which sets the property.
///
- void SetName(int? languageId, string value);
+ void SetName(string culture, string value);
///
/// Gets the name of the content item for a specified language.
///
///
- /// When is null, gets the invariant
+ /// When is null, gets the invariant
/// language, which is the value of the property.
///
- string GetName(int? languageId);
+ string GetName(string culture);
///
/// Gets or sets the names of the content item.
@@ -51,9 +51,18 @@ 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; }
+ ///
+ /// Gets a value indicating whether a given culture is available.
+ ///
+ ///
+ /// A culture becomes available whenever the content name for this culture is
+ /// non-null, and it becomes unavailable whenever the content name is null.
+ ///
+ bool IsCultureAvailable(string culture);
+
///
/// List of properties, which make up all the data available for this Content object
///
@@ -82,17 +91,17 @@ namespace Umbraco.Core.Models
///
/// Gets the value of a Property
///
- object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false);
+ object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false);
///
/// Gets the typed value of a Property
///
- TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false);
+ TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false);
///
/// Sets the (edited) value of a Property
///
- void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null);
+ void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null);
///
/// Gets a value indicating whether the content and all its properties values are valid.
@@ -102,11 +111,11 @@ namespace Umbraco.Core.Models
///
/// Gets a value indicating whether the content and its properties values are valid.
///
- Property[] Validate(int? languageId = null, string segment = null);
+ Property[] Validate(string culture = null, string segment = null);
///
/// Gets a value indicating whether the content and its culture/any properties values are valid.
///
- Property[] ValidateCulture(int? languageId = null);
+ Property[] ValidateCulture(string culture = null);
}
}
diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs
index 68e5923d66..7c8cc8eafe 100644
--- a/src/Umbraco.Core/Models/IContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/IContentTypeBase.cs
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Models
///
/// Validates that a variation is valid for the content type.
///
- bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid);
+ bool ValidateVariation(string culture, 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/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index 9e3f5fc2e0..709c37daaa 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Models
{
private List _values = new List();
private PropertyValue _pvalue;
- private Dictionary _vvalues;
+ private Dictionary _vvalues;
private static readonly Lazy Ps = new Lazy();
@@ -38,9 +38,14 @@ namespace Umbraco.Core.Models
public class PropertyValue
{
+ private string _culture;
private string _segment;
- public int? LanguageId { get; internal set; }
+ public string Culture
+ {
+ get => _culture;
+ internal set => _culture = value?.ToLowerInvariant();
+ }
public string Segment
{
get => _segment;
@@ -50,7 +55,7 @@ namespace Umbraco.Core.Models
public object PublishedValue { get; internal set; }
public PropertyValue Clone()
- => new PropertyValue { LanguageId = LanguageId, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue };
+ => new PropertyValue { _culture = _culture, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue };
}
// ReSharper disable once ClassNeverInstantiated.Local
@@ -96,10 +101,10 @@ namespace Umbraco.Core.Models
{
// make sure we filter out invalid variations
// make sure we leave _vvalues null if possible
- _values = value.Where(x => PropertyType.ValidateVariation(x.LanguageId, x.Segment, false)).ToList();
- _pvalue = _values.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null);
+ _values = value.Where(x => PropertyType.ValidateVariation(x.Culture, x.Segment, false)).ToList();
+ _pvalue = _values.FirstOrDefault(x => x.Culture == null && x.Segment == null);
_vvalues = _values.Count > (_pvalue == null ? 0 : 1)
- ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeIntStringKey(x.LanguageId, x.Segment), x => x)
+ ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeStringStringKey(x.Culture, x.Segment), x => x)
: null;
}
}
@@ -128,12 +133,12 @@ namespace Umbraco.Core.Models
///
/// Gets the value.
///
- public object GetValue(int? languageId = null, string segment = null, bool published = false)
+ public object GetValue(string culture = null, string segment = null, bool published = false)
{
- if (!PropertyType.ValidateVariation(languageId, segment, false)) return null;
- if (!languageId.HasValue && segment == null) return GetPropertyValue(_pvalue, published);
+ if (!PropertyType.ValidateVariation(culture, segment, false)) return null;
+ if (culture == null && segment == null) return GetPropertyValue(_pvalue, published);
if (_vvalues == null) return null;
- return _vvalues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var pvalue)
+ return _vvalues.TryGetValue(new CompositeStringStringKey(culture, segment), out var pvalue)
? GetPropertyValue(pvalue, published)
: null;
}
@@ -159,7 +164,7 @@ namespace Umbraco.Core.Models
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
PublishPropertyValue(pvalue);
@@ -168,29 +173,29 @@ namespace Umbraco.Core.Models
// internal - must be invoked by the content item
// does *not* validate the value - content item must validate first
- internal void PublishValue(int? languageId = null, string segment = null)
+ internal void PublishValue(string culture = null, string segment = null)
{
- PropertyType.ValidateVariation(languageId, segment, true);
+ PropertyType.ValidateVariation(culture, segment, true);
- (var pvalue, _) = GetPValue(languageId, segment, false);
+ (var pvalue, _) = GetPValue(culture, segment, false);
if (pvalue == null) return;
PublishPropertyValue(pvalue);
}
// internal - must be invoked by the content item
// does *not* validate the value - content item must validate first
- internal void PublishCultureValues(int? languageId = null)
+ internal void PublishCultureValues(string culture = null)
{
// if invariant and invariant-neutral is supported, publish invariant-neutral
- if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false))
+ if (culture == null && PropertyType.ValidateVariation(null, null, false))
PublishPropertyValue(_pvalue);
// publish everything not invariant-neutral that matches the culture and is supported
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => x.Value.LanguageId == languageId)
- .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => x.Value.Culture.InvariantEquals(culture))
+ .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
PublishPropertyValue(pvalue);
@@ -206,7 +211,7 @@ namespace Umbraco.Core.Models
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
ClearPublishedPropertyValue(pvalue);
@@ -214,25 +219,25 @@ namespace Umbraco.Core.Models
}
// internal - must be invoked by the content item
- internal void ClearPublishedValue(int? languageId = null, string segment = null)
+ internal void ClearPublishedValue(string culture = null, string segment = null)
{
- PropertyType.ValidateVariation(languageId, segment, true);
- (var pvalue, _) = GetPValue(languageId, segment, false);
+ PropertyType.ValidateVariation(culture, segment, true);
+ (var pvalue, _) = GetPValue(culture, segment, false);
if (pvalue == null) return;
ClearPublishedPropertyValue(pvalue);
}
// internal - must be invoked by the content item
- internal void ClearPublishedCultureValues(int? languageId = null)
+ internal void ClearPublishedCultureValues(string culture = null)
{
- if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false))
+ if (culture == null && PropertyType.ValidateVariation(null, null, false))
ClearPublishedPropertyValue(_pvalue);
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => x.Value.LanguageId == languageId)
- .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => x.Value.Culture.InvariantEquals(culture))
+ .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
ClearPublishedPropertyValue(pvalue);
@@ -264,10 +269,10 @@ namespace Umbraco.Core.Models
///
/// Sets a value.
///
- public void SetValue(object value, int? languageId = null, string segment = null)
+ public void SetValue(object value, string culture = null, string segment = null)
{
- PropertyType.ValidateVariation(languageId, segment, true);
- (var pvalue, var change) = GetPValue(languageId, segment, true);
+ PropertyType.ValidateVariation(culture, segment, true);
+ (var pvalue, var change) = GetPValue(culture, segment, true);
var origValue = pvalue.EditedValue;
var setValue = PropertyType.ConvertAssignedValue(value);
@@ -278,9 +283,9 @@ namespace Umbraco.Core.Models
}
// bypasses all changes detection and is the *only* way to set the published value
- internal void FactorySetValue(int? languageId, string segment, bool published, object value)
+ internal void FactorySetValue(string culture, string segment, bool published, object value)
{
- (var pvalue, _) = GetPValue(languageId, segment, true);
+ (var pvalue, _) = GetPValue(culture, segment, true);
if (published && PropertyType.IsPublishing)
pvalue.PublishedValue = value;
@@ -301,24 +306,24 @@ namespace Umbraco.Core.Models
return (_pvalue, change);
}
- private (PropertyValue, bool) GetPValue(int? languageId, string segment, bool create)
+ private (PropertyValue, bool) GetPValue(string culture, string segment, bool create)
{
- if (!languageId.HasValue && segment == null)
+ if (culture == null && segment == null)
return GetPValue(create);
var change = false;
if (_vvalues == null)
{
if (!create) return (null, false);
- _vvalues = new Dictionary();
+ _vvalues = new Dictionary();
change = true;
}
- var k = new CompositeIntStringKey(languageId, segment);
+ var k = new CompositeStringStringKey(culture, segment);
if (!_vvalues.TryGetValue(k, out var pvalue))
{
if (!create) return (null, false);
pvalue = _vvalues[k] = new PropertyValue();
- pvalue.LanguageId = languageId;
+ pvalue.Culture = culture;
pvalue.Segment = segment;
_values.Add(pvalue);
change = true;
@@ -343,7 +348,7 @@ namespace Umbraco.Core.Models
if (_vvalues == null) return true;
var pvalues = _vvalues
- .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false))
.Select(x => x.Value)
.ToArray();
@@ -354,11 +359,11 @@ namespace Umbraco.Core.Models
/// Gets a value indicating whether the culture/any values are valid.
///
/// An invalid value can be saved, but only valid values can be published.
- public bool IsCultureValid(int? languageId)
+ public bool IsCultureValid(string culture)
{
// culture-neutral is supported, validate culture-neutral
// includes mandatory validation
- if (PropertyType.ValidateVariation(languageId, null, false) && !IsValidValue(GetValue(languageId)))
+ if (PropertyType.ValidateVariation(culture, null, false) && !IsValidValue(GetValue(culture)))
return false;
// either culture-neutral is not supported, or it is valid
@@ -368,8 +373,8 @@ namespace Umbraco.Core.Models
if (_vvalues == null) return true;
var pvalues = _vvalues
- .Where(x => x.Value.LanguageId == languageId)
- .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => x.Value.Culture.InvariantEquals(culture))
+ .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false))
.Select(x => x.Value)
.ToArray();
@@ -380,10 +385,10 @@ namespace Umbraco.Core.Models
/// Gets a value indicating whether the value is valid.
///
/// An invalid value can be saved, but only valid values can be published.
- public bool IsValid(int? languageId = null, string segment = null)
+ public bool IsValid(string culture = null, string segment = null)
{
// single value -> validates mandatory
- return IsValidValue(GetValue(languageId, segment));
+ return IsValidValue(GetValue(culture, segment));
}
///
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index 8c5e318719..66299e7749 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -226,10 +226,10 @@ namespace Umbraco.Core.Models
///
/// Validates that a variation is valid for the property type.
///
- public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid)
+ public bool ValidateVariation(string culture, string segment, bool throwIfInvalid)
{
ContentVariation variation;
- if (languageId.HasValue)
+ if (culture != null)
{
variation = segment != null
? ContentVariation.CultureSegment
@@ -243,7 +243,7 @@ namespace Umbraco.Core.Models
{
variation = ContentVariation.InvariantNeutral;
}
- if ((Variations & variation) == 0)
+ if (!Variations.Has(variation))
{
if (throwIfInvalid)
throw new NotSupportedException($"Variation {variation} is invalid for property type \"{Alias}\".");
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
index 24c654604c..9d2cca3e6d 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs
@@ -21,7 +21,7 @@
/// Other caches that get their raw value from the database would consider that a property has "no
/// value" if it is missing, null, or an empty string (including whitespace-only).
///
- bool HasValue(int? languageId = null, string segment = null);
+ bool HasValue(string culture = null, string segment = null);
///
/// Gets the source value of the property.
@@ -35,7 +35,7 @@
/// If you're using that value, you're probably wrong, unless you're doing some internal
/// Umbraco stuff.
///
- object GetSourceValue(int? languageId = null, string segment = null);
+ object GetSourceValue(string culture = null, string segment = null);
///
/// Gets the object value of the property.
@@ -45,7 +45,7 @@
/// It can be null, or any type of CLR object.
/// It has been fully prepared and processed by the appropriate converter.
///
- object GetValue(int? languageId = null, string segment = null);
+ object GetValue(string culture = null, string segment = null);
///
/// Gets the XPath value of the property.
@@ -55,6 +55,6 @@
/// It must be either null, or a string, or an XPathNavigator.
/// It has been fully prepared and processed by the appropriate converter.
///
- object GetXPathValue(int? languageId = null, string segment = null);
+ object GetXPathValue(string culture = null, string segment = null);
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
index 918bdb86e4..7e2a5b5498 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
@@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent
public string Alias => PropertyType.Alias;
///
- public abstract bool HasValue(int? languageId = null, string segment = null);
+ public abstract bool HasValue(string culture = null, string segment = null);
///
- public abstract object GetSourceValue(int? languageId = null, string segment = null);
+ public abstract object GetSourceValue(string culture = null, string segment = null);
///
- public abstract object GetValue(int? languageId = null, string segment = null);
+ public abstract object GetValue(string culture = null, string segment = null);
///
- public abstract object GetXPathValue(int? languageId = null, string segment = null);
+ public abstract object GetXPathValue(string culture = null, string segment = null);
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
index f938880060..e20d8cb49c 100644
--- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs
@@ -20,20 +20,20 @@ namespace Umbraco.Core.Models.PublishedContent
private readonly Lazy
internal class LanguageRepository : NPocoRepositoryBase, ILanguageRepository
{
- private readonly Dictionary _codeIdMap = new Dictionary();
+ private readonly Dictionary _codeIdMap = new Dictionary(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary _idCodeMap = new Dictionary();
public LanguageRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
foreach (var language in languages)
{
_codeIdMap[language.IsoCode] = language.Id;
- _idCodeMap[language.Id] = language.IsoCode;
+ _idCodeMap[language.Id] = language.IsoCode.ToLowerInvariant();
}
}
@@ -204,15 +204,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way
var id = GetIdByIsoCode(isoCode, throwOnNotFound: false);
- return id > 0 ? Get(id) : null;
+ return id.HasValue ? Get(id.Value) : null;
}
// fast way of getting an id for an isoCode - avoiding cloning
// _codeIdMap is rebuilt whenever PerformGetAll runs
- public int GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true);
+ public int? GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true);
- private int GetIdByIsoCode(string isoCode, bool throwOnNotFound)
+ private int? GetIdByIsoCode(string isoCode, bool throwOnNotFound)
{
+ if (isoCode == null) return null;
+
TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way
lock (_codeIdMap)
{
@@ -225,14 +227,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// fast way of getting an isoCode for an id - avoiding cloning
// _idCodeMap is rebuilt whenever PerformGetAll runs
- public string GetIsoCodeById(int id) => GetIsoCodeById(id, throwOnNotFound: true);
+ public string GetIsoCodeById(int? id) => GetIsoCodeById(id, throwOnNotFound: true);
- private string GetIsoCodeById(int id, bool throwOnNotFound)
+ private string GetIsoCodeById(int? id, bool throwOnNotFound)
{
+ if (id == null) return null;
+
TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way
lock (_codeIdMap) // yes, we want to lock _codeIdMap
{
- if (_idCodeMap.TryGetValue(id, out var isoCode)) return isoCode;
+ if (_idCodeMap.TryGetValue(id.Value, out var isoCode)) return isoCode;
}
if (throwOnNotFound)
throw new ArgumentException($"Id {id} does not correspond to an existing language.", nameof(id));
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs
index a08ecef98d..982a5bb885 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs
@@ -25,8 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
private readonly ITagRepository _tagRepository;
private readonly MediaByGuidReadRepository _mediaByGuidReadRepository;
- public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection)
- : base(scopeAccessor, cache, logger)
+ public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, ILanguageRepository languageRepository)
+ : base(scopeAccessor, cache, languageRepository, logger)
{
_mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository));
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
@@ -279,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Database.Insert(mediaVersionDto);
// persist the property data
- var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _);
+ var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -336,7 +336,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// replace the property data
var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId);
Database.Execute(deletePropertyDataSql);
- var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _);
+ var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
index 67dbf758db..98c38603b1 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs
@@ -23,8 +23,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
private readonly ITagRepository _tagRepository;
private readonly IMemberGroupRepository _memberGroupRepository;
- public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository)
- : base(scopeAccessor, cache, logger)
+ public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
+ : base(scopeAccessor, cache, languageRepository, logger)
{
_memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository));
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
@@ -306,7 +306,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
Database.Insert(dto);
// persist the property data
- var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _);
+ var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
@@ -371,7 +371,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// replace the property data
var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId);
Database.Execute(deletePropertyDataSql);
- var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _);
+ var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _);
foreach (var propertyDataDto in propertyDataDtos)
Database.Insert(propertyDataDto);
diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs
index ea95f8708f..f235a95aa8 100644
--- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs
+++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs
@@ -272,16 +272,16 @@ namespace Umbraco.Core.PropertyEditors
///
///
///
- ///
+ ///
///
///
///
/// The object returned will automatically be serialized into json notation. For most property editors
/// the value returned is probably just a string but in some cases a json structure will be returned.
///
- public virtual object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public virtual object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
if (val == null) return string.Empty;
switch (ValueTypes.ToStorageType(ValueType))
@@ -343,12 +343,8 @@ namespace Umbraco.Core.PropertyEditors
continue;
var xElement = new XElement(nodeName);
- if (pvalue.LanguageId.HasValue)
- {
- var language = localizationService.GetLanguageById(pvalue.LanguageId.Value);
- if (language == null) continue; // uh?
- xElement.Add(new XAttribute("lang", language.IsoCode));
- }
+ if (pvalue.Culture != null)
+ xElement.Add(new XAttribute("lang", pvalue.Culture));
if (pvalue.Segment != null)
xElement.Add(new XAttribute("segment", pvalue.Segment));
diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs
index 9e31f94121..b5ed7c5917 100644
--- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs
+++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs
@@ -61,7 +61,7 @@ namespace Umbraco.Core.PropertyEditors
///
/// Converts a property value to a value for the editor.
///
- object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null);
+ object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null);
// fixme - editing - document or remove these
// why property vs propertyType? services should be injected! etc...
diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs
index cb56b39e2c..abbab0ef39 100644
--- a/src/Umbraco.Core/Services/IContentService.cs
+++ b/src/Umbraco.Core/Services/IContentService.cs
@@ -354,7 +354,7 @@ namespace Umbraco.Core.Services
///
/// Saves and publishes a document branch.
///
- IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0);
+ IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0);
///
/// Saves and publishes a document branch.
diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs
index 4f8406ebe4..d6eb3f24b4 100644
--- a/src/Umbraco.Core/Services/ILocalizationService.cs
+++ b/src/Umbraco.Core/Services/ILocalizationService.cs
@@ -113,7 +113,7 @@ namespace Umbraco.Core.Services
///
/// Gets a language identifier by its iso code.
///
- int GetLanguageIdByIsoCode(string isoCode);
+ int? GetLanguageIdByIsoCode(string isoCode);
///
/// Gets all available languages
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index bfa6c5916d..84e616d4d7 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -1103,14 +1103,14 @@ namespace Umbraco.Core.Services.Implement
}
///
- public IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0)
+ public IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0)
{
segment = segment?.ToLowerInvariant();
- bool IsEditing(IContent c, int? l, string s)
- => c.Properties.Any(x => x.Values.Where(y => y.LanguageId == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue));
+ bool IsEditing(IContent c, string l, string s)
+ => c.Properties.Any(x => x.Values.Where(y => y.Culture == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue));
- return SaveAndPublishBranch(content, force, document => IsEditing(document, languageId, segment), document => document.PublishValues(languageId, segment), userId);
+ return SaveAndPublishBranch(content, force, document => IsEditing(document, culture, segment), document => document.PublishValues(culture, segment), userId);
}
///
diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs
index 040996ca53..976c65e3e8 100644
--- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs
+++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs
@@ -304,7 +304,7 @@ namespace Umbraco.Core.Services.Implement
}
///
- public int GetLanguageIdByIsoCode(string isoCode)
+ public int? GetLanguageIdByIsoCode(string isoCode)
{
using (ScopeProvider.CreateScope(autoComplete: true))
{
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index a8640d4c84..4d30318a15 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -137,6 +137,7 @@
+
@@ -319,6 +320,7 @@
+
@@ -335,7 +337,8 @@
-
+
+
@@ -370,6 +373,7 @@
+
diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs
index d0af5ee2a4..5bfd3f2de5 100644
--- a/src/Umbraco.Tests/Models/VariationTests.cs
+++ b/src/Umbraco.Tests/Models/VariationTests.cs
@@ -65,6 +65,8 @@ namespace Umbraco.Tests.Models
var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" };
var prop = new Property(propertyType);
+ const string langFr = "fr-FR";
+
// can set value
// and get edited and published value
// because non-publishing
@@ -84,8 +86,8 @@ namespace Umbraco.Tests.Models
Assert.IsNull(prop.GetValue(published: true));
// cannot set non-supported variation value
- Assert.Throws(() => prop.SetValue("x", 1));
- Assert.IsNull(prop.GetValue(1));
+ Assert.Throws(() => prop.SetValue("x", langFr));
+ Assert.IsNull(prop.GetValue(langFr));
// can publish value
// and get edited and published values
@@ -109,41 +111,41 @@ namespace Umbraco.Tests.Models
// can set value
// and get values
- prop.SetValue("c", 1);
+ prop.SetValue("c", langFr);
Assert.AreEqual("b", prop.GetValue());
Assert.IsNull(prop.GetValue(published: true));
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.IsNull(prop.GetValue(1, published: true));
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.IsNull(prop.GetValue(langFr, published: true));
// can publish value
// and get edited and published values
- prop.PublishValue(1);
+ prop.PublishValue(langFr);
Assert.AreEqual("b", prop.GetValue());
Assert.IsNull(prop.GetValue(published: true));
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.AreEqual("c", prop.GetValue(1, published: true));
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.AreEqual("c", prop.GetValue(langFr, published: true));
// can clear all
prop.ClearPublishedAllValues();
Assert.AreEqual("b", prop.GetValue());
Assert.IsNull(prop.GetValue(published: true));
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.IsNull(prop.GetValue(1, published: true));
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.IsNull(prop.GetValue(langFr, published: true));
// can publish all
prop.PublishAllValues();
Assert.AreEqual("b", prop.GetValue());
Assert.AreEqual("b", prop.GetValue(published: true));
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.AreEqual("c", prop.GetValue(1, published: true));
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.AreEqual("c", prop.GetValue(langFr, published: true));
// same for culture
- prop.ClearPublishedCultureValues(1);
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.IsNull(prop.GetValue(1, published: true));
- prop.PublishCultureValues(1);
- Assert.AreEqual("c", prop.GetValue(1));
- Assert.AreEqual("c", prop.GetValue(1, published: true));
+ prop.ClearPublishedCultureValues(langFr);
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.IsNull(prop.GetValue(langFr, published: true));
+ prop.PublishCultureValues(langFr);
+ Assert.AreEqual("c", prop.GetValue(langFr));
+ Assert.AreEqual("c", prop.GetValue(langFr, published: true));
prop.ClearPublishedCultureValues();
Assert.AreEqual("b", prop.GetValue());
@@ -159,25 +161,29 @@ namespace Umbraco.Tests.Models
var contentType = new ContentType(-1) { Alias = "contentType" };
var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 };
- const int langFr = 1;
- const int langUk = 2;
+ const string langFr = "fr-FR";
+ const string langUk = "en-UK";
+ // throws if the content type does not support the variation
Assert.Throws(() => content.SetName(langFr, "name-fr"));
+ // now it will work
contentType.Variations = ContentVariation.CultureNeutral;
+ // invariant name works
content.Name = "name";
Assert.AreEqual("name", content.GetName(null));
content.SetName(null, "name2");
Assert.AreEqual("name2", content.Name);
Assert.AreEqual("name2", content.GetName(null));
+ // variant names work
content.SetName(langFr, "name-fr");
content.SetName(langUk, "name-uk");
-
Assert.AreEqual("name-fr", content.GetName(langFr));
Assert.AreEqual("name-uk", content.GetName(langUk));
+ // variant dictionary of names work
Assert.AreEqual(2, content.Names.Count);
Assert.IsTrue(content.Names.ContainsKey(langFr));
Assert.AreEqual("name-fr", content.Names[langFr]);
@@ -186,8 +192,10 @@ namespace Umbraco.Tests.Models
}
[Test]
- public void ContentTests()
+ public void ContentPublishValues()
{
+ const string langFr = "fr-FR";
+
var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" };
var contentType = new ContentType(-1) { Alias = "contentType" };
contentType.AddPropertyType(propertyType);
@@ -202,8 +210,8 @@ namespace Umbraco.Tests.Models
Assert.IsNull(content.GetValue("prop", published: true));
// cannot set non-supported variation value
- Assert.Throws(() => content.SetValue("prop", "x", 1));
- Assert.IsNull(content.GetValue("prop", 1));
+ Assert.Throws(() => content.SetValue("prop", "x", langFr));
+ Assert.IsNull(content.GetValue("prop", langFr));
// can publish value
// and get edited and published values
@@ -228,43 +236,43 @@ namespace Umbraco.Tests.Models
// can set value
// and get values
- content.SetValue("prop", "c", 1);
+ content.SetValue("prop", "c", langFr);
Assert.AreEqual("b", content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.IsNull(content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.IsNull(content.GetValue("prop", langFr, published: true));
// can publish value
// and get edited and published values
- Assert.Throws(() => content.PublishValues(1)); // no name
- content.SetName(1, "name-fr");
- content.PublishValues(1);
+ Assert.Throws(() => content.PublishValues(langFr)); // no name
+ content.SetName(langFr, "name-fr");
+ content.PublishValues(langFr);
Assert.AreEqual("b", content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.AreEqual("c", content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
// can clear all
content.ClearAllPublishedValues();
Assert.AreEqual("b", content.GetValue("prop"));
Assert.IsNull(content.GetValue("prop", published: true));
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.IsNull(content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.IsNull(content.GetValue("prop", langFr, published: true));
// can publish all
content.PublishAllValues();
Assert.AreEqual("b", content.GetValue("prop"));
Assert.AreEqual("b", content.GetValue("prop", published: true));
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.AreEqual("c", content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
// same for culture
- content.ClearCulturePublishedValues(1);
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.IsNull(content.GetValue("prop", 1, published: true));
- content.PublishCultureValues(1);
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.AreEqual("c", content.GetValue("prop", 1, published: true));
+ content.ClearCulturePublishedValues(langFr);
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.IsNull(content.GetValue("prop", langFr, published: true));
+ content.PublishCultureValues(langFr);
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
content.ClearCulturePublishedValues();
Assert.AreEqual("b", content.GetValue("prop"));
@@ -275,21 +283,76 @@ namespace Umbraco.Tests.Models
var other = new Content("other", -1, contentType) { Id = 2, VersionId = 1 };
other.SetValue("prop", "o");
- other.SetValue("prop", "o1", 1);
+ other.SetValue("prop", "o1", langFr);
// can copy other's edited value
content.CopyAllValues(other);
Assert.AreEqual("o", content.GetValue("prop"));
Assert.AreEqual("b", content.GetValue("prop", published: true));
- Assert.AreEqual("o1", content.GetValue("prop", 1));
- Assert.AreEqual("c", content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("o1", content.GetValue("prop", langFr));
+ Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
// can copy self's published value
content.CopyAllValues(content);
Assert.AreEqual("b", content.GetValue("prop"));
Assert.AreEqual("b", content.GetValue("prop", published: true));
- Assert.AreEqual("c", content.GetValue("prop", 1));
- Assert.AreEqual("c", content.GetValue("prop", 1, published: true));
+ Assert.AreEqual("c", content.GetValue("prop", langFr));
+ Assert.AreEqual("c", content.GetValue("prop", langFr, published: true));
+ }
+
+ [Test]
+ public void ContentPublishVariations()
+ {
+ const string langFr = "fr-FR";
+ const string langUk = "en-UK";
+ const string langEs = "es-ES";
+
+ var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" };
+ var contentType = new ContentType(-1) { Alias = "contentType" };
+ contentType.AddPropertyType(propertyType);
+
+ var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 };
+
+ contentType.Variations |= ContentVariation.CultureNeutral;
+ propertyType.Variations |= ContentVariation.CultureNeutral;
+
+ content.SetValue("prop", "a");
+ content.SetValue("prop", "a-fr", langFr);
+ content.SetValue("prop", "a-uk", langUk);
+ content.SetValue("prop", "a-es", langEs);
+
+ // cannot publish without a name
+ Assert.Throws(() => content.PublishValues(langFr));
+
+ // works with a name
+ // and then FR is available, and published
+ content.SetName(langFr, "name-fr");
+ content.PublishValues(langFr);
+
+ // now UK is available too
+ content.SetName(langUk, "name-uk");
+
+ // test available, published
+ Assert.IsTrue(content.IsCultureAvailable(langFr));
+ Assert.IsTrue(content.IsCulturePublished(langFr));
+ Assert.AreEqual("name-fr", content.GetPublishName(langFr));
+ Assert.AreNotEqual(DateTime.MinValue, content.GetDateCulturePublished(langFr));
+ Assert.IsFalse(content.IsCultureEdited(langFr)); // once published, edited is *wrong* until saved
+
+ Assert.IsTrue(content.IsCultureAvailable(langUk));
+ Assert.IsFalse(content.IsCulturePublished(langUk));
+ Assert.IsNull(content.GetPublishName(langUk));
+ Assert.Throws(() => content.GetDateCulturePublished(langUk)); // not published!
+ Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited
+
+ Assert.IsFalse(content.IsCultureAvailable(langEs));
+ Assert.IsFalse(content.IsCulturePublished(langEs));
+ Assert.IsNull(content.GetPublishName(langEs));
+ Assert.Throws(() => content.GetDateCulturePublished(langEs)); // not published!
+ Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited
+
+ // cannot test IsCultureEdited here - as that requires the content service and repository
+ // see: ContentServiceTests.Can_SaveRead_Variations
}
[Test]
diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
index 2e72a2e14c..53985b7897 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs
@@ -36,7 +36,7 @@ namespace Umbraco.Tests.Persistence.Repositories
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, cacheHelper, Logger);
var tagRepository = new TagRepository(scopeAccessor, cacheHelper, Logger);
- var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of());
+ var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of());
return repository;
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
index 4f901935dc..d07d2dda9a 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs
@@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence.Repositories
memberTypeRepository = new MemberTypeRepository(accessor, DisabledCache, Logger);
memberGroupRepository = new MemberGroupRepository(accessor, DisabledCache, Logger);
var tagRepo = new TagRepository(accessor, DisabledCache, Logger);
- var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo);
+ var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of());
return repository;
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
index 216fb08ecd..90230e15c7 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs
@@ -4,6 +4,7 @@ using NUnit.Framework;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Scoping;
using Umbraco.Tests.TestHelpers;
@@ -964,7 +965,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var accessor = (IScopeAccessor) provider;
var tagRepository = new TagRepository(accessor, DisabledCache, Logger);
mediaTypeRepository = new MediaTypeRepository(accessor, DisabledCache, Logger);
- var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of());
+ var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of());
return repository;
}
}
diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
index 56d5bfbc0c..2688629c5c 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var accessor = (IScopeAccessor) provider;
mediaTypeRepository = new MediaTypeRepository(accessor, CacheHelper, Mock.Of());
var tagRepository = new TagRepository(accessor, CacheHelper, Mock.Of());
- var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of());
+ var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of());
return repository;
}
diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs
index b097358fc3..28e103ad7c 100644
--- a/src/Umbraco.Tests/Published/NestedContentTests.cs
+++ b/src/Umbraco.Tests/Published/NestedContentTests.cs
@@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published
_owner = owner;
}
- public override bool HasValue(int? languageId = null, string segment = null) => _hasValue;
- public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue;
- public override object GetValue(int? languageId = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview);
- public override object GetXPathValue(int? languageId = null, string segment = null) => throw new WontImplementException();
+ public override bool HasValue(string culture = null, string segment = null) => _hasValue;
+ public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
+ public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview);
+ public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException();
}
class TestPublishedContent : PublishedContentBase
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
index aa6f62cf46..9e416015b4 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
@@ -258,10 +258,10 @@ namespace Umbraco.Tests.PublishedContent
public bool SolidHasValue { get; set; }
public object SolidXPathValue { get; set; }
- public object GetSourceValue(int? languageId = null, string segment = null) => SolidSourceValue;
- public object GetValue(int? languageId = null, string segment = null) => SolidValue;
- public object GetXPathValue(int? languageId = null, string segment = null) => SolidXPathValue;
- public bool HasValue(int? languageId = null, string segment = null) => SolidHasValue;
+ public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue;
+ public object GetValue(string culture = null, string segment = null) => SolidValue;
+ public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue;
+ public bool HasValue(string culture = null, string segment = null) => SolidHasValue;
}
[PublishedModel("ContentType2")]
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index 0e726064f4..04ed54d81c 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -356,7 +356,7 @@ namespace Umbraco.Tests.PublishedContent
var result = doc.Ancestors().OrderBy(x => x.Level)
.Single()
.Descendants()
- .FirstOrDefault(x => x.Value("selectedNodes", "").Split(',').Contains("1173"));
+ .FirstOrDefault(x => x.Value("selectedNodes", defaultValue: "").Split(',').Contains("1173"));
Assert.IsNotNull(result);
}
diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs
index 79929ff29d..c45d9be5f3 100644
--- a/src/Umbraco.Tests/Services/ContentServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs
@@ -2475,210 +2475,274 @@ namespace Umbraco.Tests.Services
}
[Test]
- public void Can_SaveAndRead_Names()
+ public void Can_SaveRead_Variations()
{
var languageService = ServiceContext.LocalizationService;
var langFr = new Language("fr-FR");
var langUk = new Language("en-UK");
- var langDe = new Language("de-DE");
+ var langDe = new Language("de-DE");
+
languageService.Save(langFr);
languageService.Save(langUk);
- languageService.Save(langDe);
+ languageService.Save(langDe);
var contentTypeService = ServiceContext.ContentTypeService;
-
- // fixme
- // contentType.Variations is InvariantNeutral | CultureNeutral
- // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything
- // (at least, we should validate this)
- // but then,
- // if the contentType supports InvariantNeutral | CultureNeutral,
- // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral?
- // but does this mean that CultureNeutral implies InvariantNeutral?
- // can a contentType *not* support InvariantNeutral?
-
- var contentType = contentTypeService.Get("umbTextpage");
+
+ // fixme
+ // contentType.Variations is InvariantNeutral | CultureNeutral
+ // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything
+ // (at least, we should validate this)
+ // but then,
+ // if the contentType supports InvariantNeutral | CultureNeutral,
+ // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral?
+ // but does this mean that CultureNeutral implies InvariantNeutral?
+ // can a contentType *not* support InvariantNeutral?
+
+ var contentType = contentTypeService.Get("umbTextpage");
contentType.Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral;
contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar, "prop") { Variations = ContentVariation.CultureNeutral });
- contentTypeService.Save(contentType);
-
+ contentTypeService.Save(contentType);
+
var contentService = ServiceContext.ContentService;
- var content = contentService.Create("Home US", - 1, "umbTextpage");
-
- // act
+ var content = contentService.Create("Home US", - 1, "umbTextpage");
- content.SetValue("author", "Barack Obama");
- content.SetValue("prop", "value-fr1", langFr.Id);
- content.SetValue("prop", "value-uk1", langUk.Id);
- content.SetName(langFr.Id, "name-fr");
- content.SetName(langUk.Id, "name-uk");
+ // act
+
+ content.SetValue("author", "Barack Obama");
+ content.SetValue("prop", "value-fr1", langFr.IsoCode);
+ content.SetValue("prop", "value-uk1", langUk.IsoCode);
+ content.SetName(langFr.IsoCode, "name-fr");
+ content.SetName(langUk.IsoCode, "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(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetName(langUk.Id));
-
- Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id));
- Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true));
- Assert.IsNull(content2.GetValue("prop", langUk.Id, published: true));
+ // 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(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode));
+
+ Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode));
+ Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true));
Assert.IsNull(content2.PublishName);
- Assert.IsNull(content2.GetPublishName(langFr.Id));
- Assert.IsNull(content2.GetPublishName(langUk.Id));
-
- Assert.IsTrue(content.IsCultureAvailable(langFr.Id));
- Assert.IsTrue(content.IsCultureAvailable(langUk.Id));
- Assert.IsFalse(content.IsCultureAvailable(langDe.Id));
-
- Assert.IsFalse(content.IsCulturePublished(langFr.Id));
- Assert.IsFalse(content.IsCulturePublished(langUk.Id));
-
- // act
+ Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
+ Assert.IsNull(content2.GetPublishName(langUk.IsoCode));
- content.PublishValues(langFr.Id);
- content.PublishValues(langUk.Id);
- contentService.SaveAndPublish(content);
-
- // both FR and UK have been published,
- // and content has been published,
+ // only fr and uk have a name, and are available
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // nothing has been published yet
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false));
+
+ // not published => must be edited
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+
+ // act
+
+ content.PublishValues(langFr.IsoCode);
+ content.PublishValues(langUk.IsoCode);
+ 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(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetName(langUk.Id));
+ Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode));
Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral
- Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id));
+ Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
- Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id));
- Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true));
+ Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode));
+ Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // fr and uk have been published now
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // fr and uk, published without changes, not edited
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true));
- Assert.IsTrue(content.IsCulturePublished(langFr.Id));
- Assert.IsTrue(content.IsCulturePublished(langUk.Id));
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw
+ // note that content and content2 culture published dates might be slightly different due to roundtrip to database
+
+
// act
content.PublishValues();
- contentService.SaveAndPublish(content);
-
+ contentService.SaveAndPublish(content);
+
// now it has publish name for invariant neutral
content2 = contentService.GetById(content.Id);
+
+ Assert.AreEqual("Home US", content2.PublishName);
- Assert.AreEqual("Home US", content2.PublishName);
-
- // act
-
+
+ // act
+
content.SetName(null, "Home US2");
- content.SetName(langFr.Id, "name-fr2");
- content.SetName(langUk.Id, "name-uk2");
+ content.SetName(langFr.IsoCode, "name-fr2");
+ content.SetName(langUk.IsoCode, "name-uk2");
content.SetValue("author", "Barack Obama2");
- content.SetValue("prop", "value-fr2", langFr.Id);
- content.SetValue("prop", "value-uk2", langUk.Id);
- contentService.Save(content);
-
- // content has been saved,
+ content.SetValue("prop", "value-fr2", langFr.IsoCode);
+ content.SetValue("prop", "value-uk2", langUk.IsoCode);
+ 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(langFr.Id));
- Assert.AreEqual("name-uk2", content2.GetName(langUk.Id));
+ Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode));
+ Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode));
Assert.AreEqual("Home US", content2.PublishName);
- Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id));
-
+ Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
+
Assert.AreEqual("Barack Obama2", content2.GetValue("author"));
Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true));
- Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id));
- Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true));
+ Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
+ Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false));
- Assert.IsTrue(content.IsCulturePublished(langFr.Id));
- Assert.IsTrue(content.IsCulturePublished(langUk.Id));
+ // we have changed values so now fr and uk are edited
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw
+
// act
// cannot just 'save' since we are changing what's published!
-
- content.ClearPublishedValues(langFr.Id);
+
+ content.ClearPublishedValues(langFr.IsoCode);
contentService.SaveAndPublish(content);
-
- // content has been published,
- // the french culture is gone
+
+ // content has been published,
+ // the french culture is gone
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("name-fr2", content2.GetName(langFr.IsoCode));
+ Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode));
Assert.AreEqual("Home US", content2.PublishName);
- Assert.IsNull(content2.GetPublishName(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id));
+ Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
- Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id));
- Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true));
+ Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
+ Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
- Assert.IsFalse(content.IsCulturePublished(langFr.Id));
- Assert.IsTrue(content.IsCulturePublished(langUk.Id));
+ Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
+ Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // fr is not published anymore
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+
+ // and so, fr has to be edited
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
- // act
-
- contentService.Unpublish(content);
- // content has been unpublished,
- // but properties, names, etc. retain their 'published' values so the content
- // can be re-published in its exact original state (before being unpublished)
- //
- // BEWARE!
- // in order for a content to be unpublished as a whole, and then republished in
- // its exact previous state, properties and names etc. retain their published
- // values even though the content is not published - hence many things being
- // non-null or true below - always check against content.Published to be sure
+ // act
+
+ contentService.Unpublish(content);
+
+ // content has been unpublished,
+ // but properties, names, etc. retain their 'published' values so the content
+ // can be re-published in its exact original state (before being unpublished)
+ //
+ // BEWARE!
+ // in order for a content to be unpublished as a whole, and then republished in
+ // its exact previous state, properties and names etc. retain their published
+ // values even though the content is not published - hence many things being
+ // non-null or true below - always check against content.Published to be sure
+
+ content2 = contentService.GetById(content.Id);
- content2 = contentService.GetById(content.Id);
-
Assert.IsFalse(content2.Published);
Assert.AreEqual("Home US2", content2.Name);
- Assert.AreEqual("name-fr2", content2.GetName(langFr.Id));
- Assert.AreEqual("name-uk2", content2.GetName(langUk.Id));
+ Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode));
+ Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode));
Assert.AreEqual("Home US", content2.PublishName); // not null, see note above
- Assert.IsNull(content2.GetPublishName(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); // not null, see note above
+ Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); // not null, see note above
- Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id));
- Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); // has value, see note above
-
- Assert.IsFalse(content.IsCulturePublished(langFr.Id));
- Assert.IsTrue(content.IsCulturePublished(langUk.Id)); // still true, see note above
-
- // act
+ Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
+ Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above
- contentService.SaveAndPublish(content);
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // fr is not published anymore - uk still is, see note above
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+
+ // and so, fr has to be edited - uk still is
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
- // content has been re-published,
+
+ // act
+
+ contentService.SaveAndPublish(content);
+
+ // content has been re-published,
// everything is back to what it was before being unpublished
content2 = contentService.GetById(content.Id);
@@ -2686,20 +2750,76 @@ namespace Umbraco.Tests.Services
Assert.IsTrue(content2.Published);
Assert.AreEqual("Home US2", content2.Name);
- Assert.AreEqual("name-fr2", content2.GetName(langFr.Id));
- Assert.AreEqual("name-uk2", content2.GetName(langUk.Id));
+ Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode));
+ Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode));
Assert.AreEqual("Home US", content2.PublishName);
- Assert.IsNull(content2.GetPublishName(langFr.Id));
- Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id));
+ Assert.IsNull(content2.GetPublishName(langFr.IsoCode));
+ Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode));
- Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id));
- Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id));
- Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true));
- Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true));
+ Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode));
+ Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode));
+ Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true));
+ Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // no change, back to published
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+
+ // no change, back to published
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true));
+
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
- Assert.IsFalse(content.IsCulturePublished(langFr.Id));
- Assert.IsTrue(content.IsCulturePublished(langUk.Id));
+
+ // act
+
+ content.PublishValues(langUk.IsoCode);
+ contentService.SaveAndPublish(content);
+
+ content2 = contentService.GetById(content.Id);
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false));
+
+ // no change
+ AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+ AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false));
+
+ // now, uk is no more edited
+ AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true));
+ AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true));
+
+ AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
+ AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw
+
+
+ // act
+
+ content.SetName(langUk.IsoCode, "name-uk3");
+ contentService.Save(content);
+
+ content2 = contentService.GetById(content.Id);
+
+ // changing the name = edited!
+ Assert.IsTrue(content.IsCultureEdited(langUk.IsoCode));
+ Assert.IsTrue(content2.IsCultureEdited(langUk.IsoCode));
+ }
+
+ private void AssertPerCulture(IContent item, Func getter, params (ILanguage Language, bool Result)[] testCases)
+ {
+ foreach (var testCase in testCases)
+ {
+ var value = getter(item, testCase.Language.IsoCode);
+ Assert.AreEqual(testCase.Result, value, $"Expected {testCase.Result} and got {value} for culture {testCase.Language.IsoCode}.");
+ }
}
private IEnumerable CreateContentHierarchy()
diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs
index 124c77846d..8d8e127131 100644
--- a/src/Umbraco.Tests/Services/EntityServiceTests.cs
+++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs
@@ -457,8 +457,8 @@ namespace Umbraco.Tests.Services
ServiceContext.ContentTypeService.Save(contentType);
var c1 = MockedContent.CreateSimpleContent(contentType, "Test", -1);
- c1.SetName(_langFr.Id, "Test - FR");
- c1.SetName(_langEs.Id, "Test - ES");
+ c1.SetName(_langFr.IsoCode, "Test - FR");
+ c1.SetName(_langEs.IsoCode, "Test - ES");
ServiceContext.ContentService.Save(c1);
var result = service.Get(c1.Id, UmbracoObjectTypes.Document);
@@ -486,8 +486,8 @@ namespace Umbraco.Tests.Services
var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
if (i % 2 == 0)
{
- c1.SetName(_langFr.Id, "Test " + i + " - FR");
- c1.SetName(_langEs.Id, "Test " + i + " - ES");
+ c1.SetName(_langFr.IsoCode, "Test " + i + " - FR");
+ c1.SetName(_langEs.IsoCode, "Test " + i + " - ES");
}
ServiceContext.ContentService.Save(c1);
}
diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs
index 47a847ad43..25ac6314e2 100644
--- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs
@@ -140,8 +140,8 @@ namespace Umbraco.Tests.TestHelpers
{
// 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();
+ var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray();
+ var actualPropertyValues = actualProperty.Values.OrderBy(x => x.Culture).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++)
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 6b1601fac2..6ce5f049e8 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -265,8 +265,8 @@ namespace Umbraco.Web.Editors
HandleContentNotFound(id);
return null;//irrelevant since the above throws
}
-
- var content = MapToDisplay(foundContent, languageId);
+
+ var content = MapToDisplay(foundContent, GetLanguageCulture(languageId));
return content;
}
@@ -606,7 +606,7 @@ namespace Umbraco.Web.Editors
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the modelstate to the outgoing object and throw a validation message
- var forDisplay = MapToDisplay(contentItem.PersistedContent, contentItem.LanguageId);
+ var forDisplay = MapToDisplay(contentItem.PersistedContent, GetLanguageCulture(contentItem.LanguageId));
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
@@ -643,14 +643,14 @@ namespace Umbraco.Web.Editors
else
{
//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
+ contentItem.PersistedContent.PublishValues(GetLanguageCulture(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();
foreach (var publishVariation in variantsToValidate)
{
- if (!contentItem.PersistedContent.PublishValues(publishVariation.LanguageId))
+ if (!contentItem.PersistedContent.PublishValues(GetLanguageCulture(publishVariation.LanguageId)))
{
var errMsg = Services.TextService.Localize("speechBubbles/contentLangValidationError", new[]{allLangs.First(x => x.Id == publishVariation.LanguageId).CultureName});
ModelState.AddModelError("publish_variant_" + publishVariation.LanguageId + "_", errMsg);
@@ -664,7 +664,7 @@ namespace Umbraco.Web.Editors
.Where(x => x.Mandatory);
foreach (var lang in mandatoryLangs)
{
- if (contentItem.PersistedContent.Validate(lang.Id).Length > 0)
+ if (contentItem.PersistedContent.Validate(GetLanguageCulture(lang.Id)).Length > 0)
{
var errMsg = Services.TextService.Localize("speechBubbles/contentReqLangValidationError", new[]{allLangs.First(x => x.Id == lang.Id).CultureName});
ModelState.AddModelError("publish_variant_" + lang.Id + "_", errMsg);
@@ -676,7 +676,7 @@ namespace Umbraco.Web.Editors
}
//get the updated model
- var display = MapToDisplay(contentItem.PersistedContent, contentItem.LanguageId);
+ var display = MapToDisplay(contentItem.PersistedContent, GetLanguageCulture(contentItem.LanguageId));
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
HandleInvalidModelState(display);
@@ -956,7 +956,8 @@ namespace Umbraco.Web.Editors
//set the name according to the culture settings
if (contentItem.LanguageId.HasValue && contentItem.PersistedContent.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral))
{
- contentItem.PersistedContent.SetName(contentItem.LanguageId, contentItem.Name);
+ var culture = Services.LocalizationService.GetLanguageById(contentItem.LanguageId.Value).IsoCode;
+ contentItem.PersistedContent.SetName(culture, contentItem.Name);
}
else
{
@@ -989,8 +990,8 @@ namespace Umbraco.Web.Editors
base.MapPropertyValues(
contentItem,
- (save, property) => property.GetValue(save.LanguageId), //get prop val
- (save, property, v) => property.SetValue(v, save.LanguageId)); //set prop val
+ (save, property) => property.GetValue(GetLanguageCulture(save.LanguageId)), //get prop val
+ (save, property, v) => property.SetValue(v, GetLanguageCulture(save.LanguageId))); //set prop val
}
///
@@ -1181,23 +1182,28 @@ namespace Umbraco.Web.Editors
/// Used to map an instance to a and ensuring a language is present if required
///
///
- ///
+ ///
///
- private ContentItemDisplay MapToDisplay(IContent content, int? languageId = null)
+ private ContentItemDisplay MapToDisplay(IContent content, string culture = null)
{
//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.HasPropertyTypeVaryingByCulture())
+ if (culture == null && content.HasPropertyTypeVaryingByCulture())
{
- languageId = Services.LocalizationService.GetDefaultVariantLanguage().Id;
+ culture = Services.LocalizationService.GetDefaultVariantLanguage().IsoCode;
}
var display = ContextMapper.Map(content, UmbracoContext,
- new Dictionary { { ContextMapper.LanguageKey, languageId } });
+ new Dictionary { { ContextMapper.CultureKey, culture } });
return display;
}
+ private string GetLanguageCulture(int? languageId)
+ {
+ if (languageId == null) return null;
+ return Core.Composing.Current.Services.LocalizationService.GetLanguageById(languageId.Value).IsoCode; // fixme optimize!
+ }
}
}
diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs
index 0712e2503d..d6700769a9 100644
--- a/src/Umbraco.Web/Models/ContentExtensions.cs
+++ b/src/Umbraco.Web/Models/ContentExtensions.cs
@@ -103,6 +103,5 @@ namespace Umbraco.Web.Models
var defaultLanguage = localizationService.GetDefaultVariantLanguage();
return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode);
}
-
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs
index 125db912be..41383764bb 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayNameResolver.cs
@@ -12,14 +12,8 @@ namespace Umbraco.Web.Models.Mapping
{
public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context)
{
- var langId = context.GetLanguageId();
- if (langId.HasValue && source.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral))
- {
- //return the culture name being requested
- return source.GetName(langId);
- }
-
- return source.Name;
+ var culture = context.GetCulture();
+ return source.GetName(culture);
}
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
index 41146d7a8e..07b64ac309 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
+using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
@@ -32,19 +33,19 @@ namespace Umbraco.Web.Models.Mapping
{
Language = x,
Mandatory = x.Mandatory,
- Name = source.GetName(x.Id),
- Exists = source.IsCultureAvailable(x.Id), // segments ??
+ Name = source.GetName(x.IsoCode),
+ Exists = source.IsCultureAvailable(x.IsoCode), // segments ??
PublishedState = source.PublishedState.ToString(),
//Segment = ?? We'll need to populate this one day when we support segments
}).ToList();
- var langId = context.GetLanguageId();
+ var culture = context.GetCulture();
//set the current variant being edited to the one found in the context or the default if nothing matches
var foundCurrent = false;
foreach (var variant in variants)
{
- if (langId.HasValue && langId.Value == variant.Language.Id)
+ if (culture.InvariantEquals(variant.Language.IsoCode))
{
variant.IsCurrent = true;
foundCurrent = true;
diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs
index 5f7cc0e052..10fec19b38 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs
@@ -46,9 +46,9 @@ namespace Umbraco.Web.Models.Mapping
editor = _propertyEditors[Constants.PropertyEditors.Aliases.NoEdit];
}
- var languageId = context.GetLanguageId();
+ var culture = context.GetCulture();
- if (!languageId.HasValue && property.PropertyType.Variations == ContentVariation.CultureNeutral)
+ if (culture == null && property.PropertyType.Variations == ContentVariation.CultureNeutral)
{
//a language Id needs to be set for a property type that can be varried by language
throw new InvalidOperationException($"No languageId found in mapping operation when one is required for the culture neutral property type {property.PropertyType.Alias}");
@@ -74,7 +74,7 @@ namespace Umbraco.Web.Models.Mapping
}
// if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return.
- result.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, languageId);
+ result.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture);
return result;
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs
index 83345988be..1348dbb8a9 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentTypeProfileExtensions.cs
@@ -181,7 +181,6 @@ namespace Umbraco.Web.Models.Mapping
// ignore, composition is managed in AfterMapContentTypeSaveToEntity
.ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore());
-
// ignore for members
mapping = typeof(TDestination) == typeof(IMemberType)
? mapping.ForMember(dto => dto.Variations, opt => opt.Ignore())
diff --git a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs b/src/Umbraco.Web/Models/Mapping/ContextMapper.cs
index b46cd219d8..5aa8af7668 100644
--- a/src/Umbraco.Web/Models/Mapping/ContextMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContextMapper.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Web.Models.Mapping
internal static class ContextMapper
{
public const string UmbracoContextKey = "ContextMapper.UmbracoContext";
- public const string LanguageKey = "ContextMapper.LanguageId";
+ public const string CultureKey = "ContextMapper.Culture";
public static TDestination Map(TSource obj, UmbracoContext umbracoContext)
=> Mapper.Map(obj, opt => opt.Items[UmbracoContextKey] = umbracoContext);
@@ -77,12 +77,12 @@ namespace Umbraco.Web.Models.Mapping
///
///
///
- public static int? GetLanguageId(this ResolutionContext resolutionContext)
+ public static string GetCulture(this ResolutionContext resolutionContext)
{
- if (!resolutionContext.Options.Items.TryGetValue(LanguageKey, out var obj)) return null;
+ if (!resolutionContext.Options.Items.TryGetValue(CultureKey, out var obj)) return null;
- if (obj is int i)
- return i;
+ if (obj is string s)
+ return s;
return null;
}
diff --git a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs
index 31425fdadd..a03ad4aa00 100644
--- a/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/DateValueEditor.cs
@@ -18,9 +18,9 @@ namespace Umbraco.Web.PropertyEditors
Validators.Add(new DateTimeValidator());
}
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture= null, string segment = null)
{
- var date = property.GetValue(languageId, segment).TryConvertTo();
+ var date = property.GetValue(culture, segment).TryConvertTo();
if (date.Success == false || date.Result == null)
{
return String.Empty;
diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs
index e969d2e45b..0d5f6efaf5 100644
--- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs
@@ -93,11 +93,11 @@ namespace Umbraco.Web.PropertyEditors
//copy each of the property values (variants, segments) to the destination
foreach (var propertyValue in property.Values)
{
- var propVal = property.GetValue(propertyValue.LanguageId, propertyValue.Segment);
+ var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) continue;
var sourcePath = _mediaFileSystem.GetRelativePath(str);
var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath);
- args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.LanguageId, propertyValue.Segment);
+ args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment);
isUpdated = true;
}
}
@@ -153,11 +153,11 @@ namespace Umbraco.Web.PropertyEditors
foreach (var pvalue in property.Values)
{
- var svalue = property.GetValue(pvalue.LanguageId, pvalue.Segment) as string;
+ var svalue = property.GetValue(pvalue.Culture, pvalue.Segment) as string;
if (string.IsNullOrWhiteSpace(svalue))
- _mediaFileSystem.UploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.LanguageId, pvalue.Segment);
+ _mediaFileSystem.UploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment);
else
- _mediaFileSystem.UploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.LanguageId, pvalue.Segment);
+ _mediaFileSystem.UploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment);
}
}
}
diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs
index 6d3c5b4f61..6183641aaf 100644
--- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs
@@ -148,13 +148,13 @@ namespace Umbraco.Web.PropertyEditors
//copy each of the property values (variants, segments) to the destination by using the edited value
foreach (var propertyValue in property.Values)
{
- var propVal = property.GetValue(propertyValue.LanguageId, propertyValue.Segment);
+ var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
var src = GetFileSrcFromPropertyValue(propVal, out var jo);
if (src == null) continue;
var sourcePath = _mediaFileSystem.GetRelativePath(src);
var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath);
jo["src"] = _mediaFileSystem.GetUrl(copyPath);
- args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.LanguageId, propertyValue.Segment);
+ args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment);
isUpdated = true;
}
}
@@ -209,10 +209,10 @@ namespace Umbraco.Web.PropertyEditors
foreach (var pvalue in property.Values)
{
- var svalue = property.GetValue(pvalue.LanguageId, pvalue.Segment) as string;
+ var svalue = property.GetValue(pvalue.Culture, pvalue.Segment) as string;
if (string.IsNullOrWhiteSpace(svalue))
{
- _autoFillProperties.Reset(model, autoFillConfig, pvalue.LanguageId, pvalue.Segment);
+ _autoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment);
continue;
}
diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs
index bce989fb18..98e8346441 100644
--- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs
@@ -32,9 +32,9 @@ namespace Umbraco.Web.PropertyEditors
/// This is called to merge in the prevalue crops with the value that is saved - similar to the property value converter for the front-end
///
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
if (val == null) return null;
ImageCropperValue value;
diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs
index a6373bd87d..645296a354 100644
--- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs
@@ -85,9 +85,9 @@ namespace Umbraco.Web.PropertyEditors
///
/// The legacy property editor saved this data as new line delimited! strange but we have to maintain that.
///
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
return val?.ToString().Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => JObject.FromObject(new {value = x})) ?? new JObject[] { };
diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
index 52933e5846..4cb8fde97a 100644
--- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
@@ -139,9 +139,9 @@ namespace Umbraco.Web.PropertyEditors
// note: there is NO variant support here
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
if (val == null || string.IsNullOrWhiteSpace(val.ToString()))
return string.Empty;
diff --git a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs
index 6cd9675f67..25a1c25284 100644
--- a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs
@@ -70,12 +70,12 @@ namespace Umbraco.Web.PropertyEditors
///
///
///
- ///
+ ///
///
///
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var delimited = base.ToEditor(property, dataTypeService, languageId, segment).ToString();
+ var delimited = base.ToEditor(property, dataTypeService, culture, segment).ToString();
return delimited.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
index 181471b315..797d0eaf59 100644
--- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
@@ -61,12 +61,12 @@ namespace Umbraco.Web.PropertyEditors
///
///
///
- ///
+ ///
///
///
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
if (val == null)
return null;
diff --git a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs
index f860a5abe7..754cef1f31 100644
--- a/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/TextOnlyValueEditor.cs
@@ -20,15 +20,15 @@ namespace Umbraco.Web.PropertyEditors
///
///
///
- ///
+ ///
///
///
///
/// The object returned will always be a string and if the database type is not a valid string type an exception is thrown
///
- public override object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null)
+ public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null)
{
- var val = property.GetValue(languageId, segment);
+ var val = property.GetValue(culture, segment);
if (val == null) return string.Empty;
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs
index 0b4d391a3a..7d8b869218 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/BTree.DictionaryOfPropertyDataSerializer.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
pdatas.Add(pdata);
// everything that can be null is read/written as object
- pdata.LanguageId = ReadIntObject(stream);
+ pdata.Culture = ReadStringObject(stream);
pdata.Segment = ReadStringObject(stream);
pdata.Value = ReadObject(stream);
}
@@ -61,7 +61,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
foreach (var pdata in values)
{
// everything that can be null is read/written as object
- WriteObject(pdata.LanguageId, stream);
+ WriteObject(pdata.Culture, stream);
WriteObject(pdata.Segment, stream);
WriteObject(pdata.Value, stream);
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs
index ab9d3cdcac..ddb9607575 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/PropertyData.cs
@@ -4,8 +4,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
{
internal class PropertyData
{
- [JsonProperty("lang")]
- public int? LanguageId { get; set; }
+ [JsonProperty("culture")]
+ public string Culture { get; set; }
[JsonProperty("seg")]
public string Segment { get; set; }
@@ -13,4 +13,4 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
[JsonProperty("val")]
public object Value { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
index 9a74b66897..7fe898b080 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
@@ -28,7 +28,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private object _interValue;
// the variant source and inter values
- private Dictionary _sourceValues;
+ private Dictionary _sourceValues;
// the variant and non-variant object values
private CacheValues _cacheValues;
@@ -49,16 +49,16 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
foreach (var sourceValue in sourceValues)
{
- if (sourceValue.LanguageId == null && sourceValue.Segment == null)
+ if (sourceValue.Culture == null && sourceValue.Segment == null)
{
_sourceValue = sourceValue.Value;
}
else
{
if (_sourceValues == null)
- _sourceValues = new Dictionary();
- _sourceValues[new CompositeIntStringKey(sourceValue.LanguageId, sourceValue.Segment)]
- = new SourceInterValue { LanguageId = sourceValue.LanguageId, Segment = sourceValue.Segment, SourceValue = sourceValue.Value };
+ _sourceValues = new Dictionary();
+ _sourceValues[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)]
+ = new SourceInterValue { Culture = sourceValue.Culture, Segment = sourceValue.Segment, SourceValue = sourceValue.Value };
}
}
}
@@ -84,7 +84,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_publishedSnapshotAccessor = origin._publishedSnapshotAccessor;
}
- public override bool HasValue(int? languageId = null, string segment = null) => _sourceValue != null
+ public override bool HasValue(string culture = null, string segment = null) => _sourceValue != null
&& (!(_sourceValue is string) || string.IsNullOrWhiteSpace((string) _sourceValue) == false);
// used to cache the recursive *property* for this property
@@ -143,9 +143,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
// this is always invoked from within a lock, so does not require its own lock
- private object GetInterValue(int? languageId, string segment)
+ private object GetInterValue(string culture, string segment)
{
- if (languageId == null && segment == null)
+ if (culture == null && segment == null)
{
if (_interInitialized) return _interValue;
_interValue = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing);
@@ -154,11 +154,11 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
if (_sourceValues == null)
- _sourceValues = new Dictionary();
+ _sourceValues = new Dictionary();
- var k = new CompositeIntStringKey(languageId, segment);
+ var k = new CompositeStringStringKey(culture, segment);
if (!_sourceValues.TryGetValue(k, out var vvalue))
- _sourceValues[k] = vvalue = new SourceInterValue { LanguageId = languageId, Segment = segment };
+ _sourceValues[k] = vvalue = new SourceInterValue { Culture = culture, Segment = segment };
if (vvalue.InterInitialized) return vvalue.InterValue;
vvalue.InterValue = PropertyType.ConvertSourceToInter(_content, vvalue.SourceValue, _isPreviewing);
@@ -166,45 +166,45 @@ namespace Umbraco.Web.PublishedCache.NuCache
return vvalue.InterValue;
}
- public override object GetSourceValue(int? languageId = null, string segment = null)
+ public override object GetSourceValue(string culture = null, string segment = null)
{
- if (languageId == null && segment == null)
+ if (culture == null && segment == null)
return _sourceValue;
lock (_locko)
{
if (_sourceValues == null) return null;
- return _sourceValues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var sourceValue) ? sourceValue.SourceValue : null;
+ return _sourceValues.TryGetValue(new CompositeStringStringKey(culture, segment), out var sourceValue) ? sourceValue.SourceValue : null;
}
}
- public override object GetValue(int? languageId = null, string segment = null)
+ public override object GetValue(string culture = null, string segment = null)
{
lock (_locko)
{
- var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment);
+ var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue;
- cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing);
+ cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.ObjectInitialized = true;
return cacheValues.ObjectValue;
}
}
- public override object GetXPathValue(int? languageId = null, string segment = null)
+ public override object GetXPathValue(string culture = null, string segment = null)
{
lock (_locko)
{
- var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment);
+ var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.XPathInitialized) return cacheValues.XPathValue;
- cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing);
+ cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.XPathInitialized = true;
return cacheValues.XPathValue;
}
@@ -222,18 +222,18 @@ namespace Umbraco.Web.PublishedCache.NuCache
private class CacheValues : CacheValue
{
- private Dictionary _values;
+ private Dictionary _values;
// this is always invoked from within a lock, so does not require its own lock
- public CacheValue For(int? languageId, string segment)
+ public CacheValue For(string culture, string segment)
{
- if (languageId == null && segment == null)
+ if (culture == null && segment == null)
return this;
if (_values == null)
- _values = new Dictionary();
+ _values = new Dictionary();
- var k = new CompositeIntStringKey(languageId, segment);
+ var k = new CompositeStringStringKey(culture, segment);
if (!_values.TryGetValue(k, out var value))
_values[k] = value = new CacheValue();
@@ -243,9 +243,14 @@ namespace Umbraco.Web.PublishedCache.NuCache
private class SourceInterValue
{
+ private string _culture;
private string _segment;
- public int? LanguageId { get; set; }
+ public string Culture
+ {
+ get => _culture;
+ internal set => _culture = value?.ToLowerInvariant();
+ }
public string Segment
{
get => _segment;
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
index faff5e9793..1d83dcaae3 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
@@ -1187,7 +1187,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
{
var value = published ? pvalue.PublishedValue : pvalue.EditedValue;
if (value != null)
- pdatas.Add(new PropertyData { LanguageId = pvalue.LanguageId, Segment = pvalue.Segment, Value = value });
+ pdatas.Add(new PropertyData { Culture = pvalue.Culture, Segment = pvalue.Segment, Value = value });
//Core.Composing.Current.Logger.Debug($"{content.Id} {prop.Alias} [{pvalue.LanguageId},{pvalue.Segment}] {value} {(published?"pub":"edit")}");
@@ -1216,15 +1216,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
var cultureData = new Dictionary();
if (content.Names != null)
{
- //fixme - when this stores a culture name reference we don't need this lookup anymore
- var keys = content.Names.Keys;
- var langs = _serviceContext.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x.IsoCode);
foreach (var name in content.Names)
{
- if (langs.TryGetValue(name.Key, out var found))
- {
- cultureData[found] = new CultureVariation { Name = name.Value };
- }
+ cultureData[name.Key] = new CultureVariation { Name = name.Value };
}
}
diff --git a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
index 2c5b93f219..d8db937ca8 100644
--- a/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
+++ b/src/Umbraco.Web/PublishedCache/PublishedElementPropertyBase.cs
@@ -36,7 +36,7 @@ namespace Umbraco.Web.PublishedCache
IsMember = propertyType.ContentType.ItemType == PublishedItemType.Member;
}
- public override bool HasValue(int? languageId = null, string segment = null)
+ public override bool HasValue(string culture = null, string segment = null)
=> _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s));
// used to cache the CacheValues of this property
@@ -136,9 +136,9 @@ namespace Umbraco.Web.PublishedCache
return _interValue;
}
- public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue;
+ public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
- public override object GetValue(int? languageId = null, string segment = null)
+ public override object GetValue(string culture = null, string segment = null)
{
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
@@ -152,7 +152,7 @@ namespace Umbraco.Web.PublishedCache
}
}
- public override object GetXPathValue(int? languageId = null, string segment = null)
+ public override object GetXPathValue(string culture = null, string segment = null)
{
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
index 632698c37e..ea8ab925c6 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs
@@ -27,13 +27,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
///
/// Gets the raw value of the property.
///
- public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue;
+ public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
// in the Xml cache, everything is a string, and to have a value
// you want to have a non-null, non-empty string.
- public override bool HasValue(int? languageId = null, string segment = null) => _sourceValue.Trim().Length > 0;
+ public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0;
- public override object GetValue(int? languageId = null, string segment = null)
+ public override object GetValue(string culture = null, string segment = null)
{
// NOT caching the source (intermediate) value since we'll never need it
// everything in Xml cache is per-request anyways
@@ -48,7 +48,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return _objectValue;
}
- public override object GetXPathValue(int? languageId = null, string segment = null) { throw new NotImplementedException(); }
+ public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); }
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData)
: this(propertyType, content, isPreviewing)
diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs
index 3314bc09fc..6d397ffaa3 100644
--- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs
+++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs
@@ -10,24 +10,24 @@ namespace Umbraco.Web
{
#region Value
- public static T Value(this IPublishedProperty property, int? languageId = null, string segment = null)
+ public static T Value(this IPublishedProperty property, string culture = null, string segment = null)
{
- return property.Value(false, default(T), languageId, segment);
+ return property.Value(false, default(T), culture, segment);
}
- public static T Value(this IPublishedProperty property, T defaultValue, int? languageId = null, string segment = null)
+ public static T Value(this IPublishedProperty property, T defaultValue, string culture = null, string segment = null)
{
- return property.Value(true, defaultValue, languageId, segment);
+ return property.Value(true, defaultValue, culture, segment);
}
- internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, int? languageId = null, string segment = null)
+ internal static T Value(this IPublishedProperty property, bool withDefaultValue, T defaultValue, string culture = null, string segment = null)
{
- if (property.HasValue(languageId, segment) == false && withDefaultValue) return defaultValue;
+ if (property.HasValue(culture, segment) == false && withDefaultValue) return defaultValue;
// else we use .Value so we give the converter a chance to handle the default value differently
// eg for IEnumerable it may return Enumerable.Empty instead of null
- var value = property.GetValue(languageId, segment);
+ var value = property.GetValue(culture, segment);
// if value is null (strange but why not) it still is OK to call TryConvertTo
// because it's an extension method (hence no NullRef) which will return a
diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs
index c2a818a926..bde74c0ab1 100644
--- a/src/Umbraco.Web/PublishedElementExtensions.cs
+++ b/src/Umbraco.Web/PublishedElementExtensions.cs
@@ -48,10 +48,10 @@ namespace Umbraco.Web
/// Gets a value indicating whether the content has a value for a property identified by its alias.
///
/// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is true.
- public static bool HasValue(this IPublishedElement content, string alias, int? languageId = null, string segment = null)
+ public static bool HasValue(this IPublishedElement content, string alias, string culture = null, string segment = null)
{
var prop = content.GetProperty(alias);
- return prop != null && prop.HasValue(languageId, segment);
+ return prop != null && prop.HasValue(culture, segment);
}
///
@@ -63,8 +63,7 @@ namespace Umbraco.Web
/// The value to return if the content has no value for the property.
/// Either or depending on whether the content
/// has a value for the property identified by the alias.
- public static IHtmlString HasValue(this IPublishedElement content, string alias,
- string valueIfTrue, string valueIfFalse = null)
+ public static IHtmlString IfHasValue(this IPublishedElement content, string alias, string valueIfTrue, string valueIfFalse = null)
{
return content.HasValue(alias)
? new HtmlString(valueIfTrue)
@@ -80,7 +79,7 @@ namespace Umbraco.Web
///
/// The content.
/// The property alias.
- /// The variation language.
+ /// The variation language.
/// The variation segment.
/// The value of the content's property identified by the alias.
///
@@ -89,10 +88,31 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static object Value(this IPublishedElement content, string alias, int? languageId = null, string segment = null)
+ public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null)
{
var property = content.GetProperty(alias);
- return property?.GetValue(languageId, segment);
+ return property?.GetValue(culture, segment);
+ }
+
+ ///
+ /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
+ ///
+ /// The content.
+ /// The property alias.
+ /// The default value.
+ /// The variation language.
+ /// The variation segment.
+ /// The value of the content's property identified by the alias, if it exists, otherwise a default value.
+ ///
+ /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
+ /// If no property with the specified alias exists, or if the property has no value, returns .
+ /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
+ /// The alias is case-insensitive.
+ ///
+ public static object Value(this IPublishedElement content, string alias, string defaultValue, string culture = null, string segment = null) // fixme - kill
+ {
+ var property = content.GetProperty(alias);
+ return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment);
}
///
@@ -110,31 +130,10 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static object Value(this IPublishedElement content, string alias, string defaultValue, int? languageId = null, string segment = null) // fixme - kill
+ public static object Value(this IPublishedElement content, string alias, object defaultValue, string culture = null, string segment = null)
{
var property = content.GetProperty(alias);
- return property == null || property.HasValue(languageId, segment) == false ? defaultValue : property.GetValue(languageId, segment);
- }
-
- ///
- /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
- ///
- /// The content.
- /// The property alias.
- /// The default value.
- /// The variation language.
- /// The variation segment.
- /// The value of the content's property identified by the alias, if it exists, otherwise a default value.
- ///
- /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
- /// If no property with the specified alias exists, or if the property has no value, returns .
- /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
- /// The alias is case-insensitive.
- ///
- public static object Value(this IPublishedElement content, string alias, object defaultValue, int? languageId = null, string segment = null)
- {
- var property = content.GetProperty(alias);
- return property == null || property.HasValue(languageId, segment) == false ? defaultValue : property.GetValue(languageId, segment);
+ return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue(culture, segment);
}
#endregion
@@ -147,7 +146,7 @@ namespace Umbraco.Web
/// The target property type.
/// The content.
/// The property alias.
- /// The variation language.
+ /// The variation language.
/// The variation segment.
/// The value of the content's property identified by the alias, converted to the specified type.
///
@@ -156,9 +155,9 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static T Value(this IPublishedElement content, string alias, int? languageId = null, string segment = null)
+ public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null)
{
- return content.Value(alias, false, default(T), languageId, segment);
+ return content.Value(alias, false, default(T), culture, segment);
}
///
@@ -168,7 +167,7 @@ namespace Umbraco.Web
/// The content.
/// The property alias.
/// The default value.
- /// The variation language.
+ /// The variation language.
/// The variation segment.
/// The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value.
///
@@ -177,17 +176,17 @@ namespace Umbraco.Web
/// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
/// The alias is case-insensitive.
///
- public static T Value(this IPublishedElement content, string alias, T defaultValue, int? languageId = null, string segment = null)
+ public static T Value(this IPublishedElement content, string alias, T defaultValue, string culture = null, string segment = null)
{
- return content.Value(alias, true, defaultValue, languageId, segment);
+ return content.Value(alias, true, defaultValue, culture, segment);
}
- internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, int? languageId = null, string segment = null) // fixme uh?
+ internal static T Value(this IPublishedElement content, string alias, bool withDefaultValue, T defaultValue, string culture = null, string segment = null) // fixme uh?
{
var property = content.GetProperty(alias);
if (property == null) return defaultValue;
- return property.Value(withDefaultValue, defaultValue, languageId, segment);
+ return property.Value(withDefaultValue, defaultValue, culture, segment);
}
#endregion
diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
index 900af87931..67e2d56fcb 100644
--- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
@@ -97,18 +97,14 @@ namespace Umbraco.Web.Routing
else
{
var result = new List();
- var languageIsoCodesToIds = _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x.Id);
foreach(var domainUri in domainUris)
- {
- if (languageIsoCodesToIds.TryGetValue(domainUri.Culture.Name, out var langId))
- {
- var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, languageId: langId);
- if (!string.IsNullOrWhiteSpace(umbracoUrlName))
- {
- var path = "/" + umbracoUrlName;
- var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
- result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
- }
+ {
+ var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name);
+ if (!string.IsNullOrWhiteSpace(umbracoUrlName))
+ {
+ var path = "/" + umbracoUrlName;
+ var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
+ result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
}
}
diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
index 0a399c24c5..525b1fcf2c 100644
--- a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
+++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
@@ -34,7 +34,7 @@ namespace Umbraco.Web.WebApi.Binders
{
return ContextMapper.Map>(content, new Dictionary
{
- [ContextMapper.LanguageKey] = languageId
+ [ContextMapper.CultureKey] = languageId
});
}
}
diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs
index bc650b5a78..274ea83214 100644
--- a/src/Umbraco.Web/umbraco.presentation/page.cs
+++ b/src/Umbraco.Web/umbraco.presentation/page.cs
@@ -374,17 +374,17 @@ namespace umbraco
_content = content;
}
- public override bool HasValue(int? languageId = null, string segment = null)
+ public override bool HasValue(string culture = null, string segment = null)
{
return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false);
}
- public override object GetSourceValue(int? languageId = null, string segment = null)
+ public override object GetSourceValue(string culture = null, string segment = null)
{
return _sourceValue;
}
- public override object GetValue(int? languageId = null, string segment = null)
+ public override object GetValue(string culture = null, string segment = null)
{
// isPreviewing is true here since we want to preview anyway...
const bool isPreviewing = true;
@@ -392,7 +392,7 @@ namespace umbraco
return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing);
}
- public override object GetXPathValue(int? languageId = null, string segment = null)
+ public override object GetXPathValue(string culture = null, string segment = null)
{
throw new NotImplementedException();
}
@@ -482,14 +482,9 @@ namespace umbraco
if (_cultureNames == null)
{
var d = new Dictionary();
- //fixme this will not be necessary when the IContentBase.Names is a string based dictionary
- var langs = Current.Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x.IsoCode);
foreach (var c in _inner.Names)
{
- if (langs.TryGetValue(c.Key, out var lang))
- {
- d[lang] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment());
- }
+ d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment());
}
_cultureNames = d;
}