diff --git a/LICENSE.md b/LICENSE.md index 69dd108..e3bd86b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,5 @@ -Copyright © 2015 2015 The Outfield, Our Umbraco and other contributors +Copyright © 2018 UMCO, Our Umbraco and other contributors +Copyright © 2015 The Outfield Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 394b5b5..390d2b8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ Vorto ===== -[![Build status](https://img.shields.io/appveyor/ci/mattbrailsford/umbraco-vorto.svg)](https://ci.appveyor.com/project/mattbrailsford/umbraco-vorto) +[![Build status](https://img.shields.io/appveyor/ci/UMCO/umbraco-vorto.svg)](https://ci.appveyor.com/project/UMCO/umbraco-vorto) [![NuGet release](https://img.shields.io/nuget/v/Our.Umbraco.Vorto.svg)](https://www.nuget.org/packages/Our.Umbraco.Vorto) [![Our Umbraco project page](https://img.shields.io/badge/our-umbraco-orange.svg)](https://our.umbraco.org/projects/backoffice-extensions/vorto) -[![Chat on Gitter](https://img.shields.io/badge/gitter-join_chat-green.svg)](https://gitter.im/mattbrailsford/umbraco-vorto) 1:1 multilingual property editor wrapper for Umbraco diff --git a/appveyor.yml b/appveyor.yml index 270bf15..4e2aa04 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,35 +1,39 @@ os: Visual Studio 2015 # version format -version: 1.6.0.{build} +version: 1.6.1.{build} # UMBRACO_PACKAGE_PRERELEASE_SUFFIX if a rtm release build this should be blank, otherwise if empty will default to alpha # example UMBRACO_PACKAGE_PRERELEASE_SUFFIX=beta init: - set UMBRACO_PACKAGE_PRERELEASE_SUFFIX= +cache: + - src\packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified + build_script: -- build-appveyor.cmd + - build-appveyor.cmd artifacts: - path: artifacts\*.nupkg - path: artifacts\*.zip deploy: - # MyGet Deployment for builds & releases + # MyGet Deployment for builds & releases - provider: NuGet - server: https://www.myget.org/F/umbraco-vorto/ - symbol_server: https://nuget.symbolsource.org/MyGet/umbraco-vorto + server: https://www.myget.org/F/umbraco-packages/api/v2/package + # symbol_server: https://www.myget.org/F/umbraco-packages/symbols/api/v2/package + skip_symbols: true api_key: - secure: gHDTL46KZcLzj6J8m//TJgaCOJCl9ixR//rXjO18HRlCsfPYYz7dU81u2D5zd+ZN + secure: 36/Ax5O+e6wENlhoTwgvoEBZV3FG4XjF429SNTej2qsGTAL+cdfA1kT/tm1St8vx artifact: /.*\.nupkg/ on: branch: develop - # GitHub Deployment for releases + # GitHub Deployment for releases - provider: GitHub auth_token: - secure: GyY9va9/RfmO6mK7xMhaciO9mtep3HYW0DEPeFVUBbYvZGRjk8enQ/tW6id6yE7D + secure: yDxrRTveSScJA35MQTOaLYVjoPKFKl2bHBkG+JMZjiN0r7AfuUCxVU3CgW8Imu4h artifact: /.*\.zip/ # upload all Zip packages to release assets draft: false prerelease: false @@ -37,11 +41,12 @@ deploy: branch: master appveyor_repo_tag: true # deploy on tag push only - # NuGet Deployment for releases + # NuGet Deployment for releases - provider: NuGet server: + skip_symbols: true api_key: - secure: q2Aov00i+eWTpbwWHB1JN8EAGw4o8FaOC2lj2MolyS6TGkUUFTJK/vQTQzaf/EQ5 + secure: vEophXSqus5F60LRBY4/j1l6K/S5+n3/yYpiID3O7JJW1gyj+0q0enuHhN3tgdhl artifact: /.*\.nupkg/ on: branch: master diff --git a/build/package.proj b/build/package.proj index c58708a..64cb050 100644 --- a/build/package.proj +++ b/build/package.proj @@ -207,7 +207,7 @@ OutputDirectory="$(ArtifactsDir)" Files="@(PackageFiles)" /> - - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) + $([System.IO.Path]::Combine($(SolutionDir), "..", "build", "tools", "NuGet")) - $(SolutionDir).nuget + $(SolutionDir)../build/tools/NuGet diff --git a/src/.nuget/NuGet.exe b/src/.nuget/NuGet.exe deleted file mode 100644 index 324daa8..0000000 Binary files a/src/.nuget/NuGet.exe and /dev/null differ diff --git a/src/Our.Umbraco.Vorto.sln b/src/Our.Umbraco.Vorto.sln index 212d991..da774f4 100644 --- a/src/Our.Umbraco.Vorto.sln +++ b/src/Our.Umbraco.Vorto.sln @@ -1,17 +1,10 @@  -Microsoft Visual Studio Solution File, Format Version 14.00 -# Visual Studio 2015 -VisualStudioVersion = 14.0.22823.1 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Our.Umbraco.Vorto", "Our.Umbraco.Vorto\Our.Umbraco.Vorto.csproj", "{B26558EC-3502-4879-A2CB-1E668CB43D09}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{17DAAEDF-EF03-46AF-BF4A-D9A4D2365A06}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,4 +19,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {20D4A4FE-ECA9-4D6A-A35F-436AAEBF1919} + EndGlobalSection EndGlobal diff --git a/src/Our.Umbraco.Vorto/Constants.cs b/src/Our.Umbraco.Vorto/Constants.cs index a750e59..08f0140 100644 --- a/src/Our.Umbraco.Vorto/Constants.cs +++ b/src/Our.Umbraco.Vorto/Constants.cs @@ -3,5 +3,6 @@ internal static class Constants { public const string CacheKey_GetTargetDataTypeDefinition = "Vorto_GetTargetDataTypeDefinition_"; - } + public const string CacheKey_GetInnerPublishedPropertyType = "Vorto_GetInnerPublishedPropertyType_"; + } } diff --git a/src/Our.Umbraco.Vorto/Converters/VortoValueConverter.cs b/src/Our.Umbraco.Vorto/Converters/VortoValueConverter.cs index e3e1a56..3272df0 100644 --- a/src/Our.Umbraco.Vorto/Converters/VortoValueConverter.cs +++ b/src/Our.Umbraco.Vorto/Converters/VortoValueConverter.cs @@ -1,6 +1,9 @@ using System; +using System.Reflection; using Newtonsoft.Json; +using Our.Umbraco.Vorto.Helpers; using Our.Umbraco.Vorto.Models; +using System.Linq; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; @@ -8,9 +11,7 @@ namespace Our.Umbraco.Vorto.Converters { - [PropertyValueType(typeof(VortoValue))] - [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)] - public class VortoValueConverter : PropertyValueConverterBase + public class VortoValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta { public override bool IsConverter(PublishedPropertyType propertyType) { @@ -21,10 +22,24 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o { try { - if (source != null && !source.ToString().IsNullOrWhiteSpace()) + if (source == null || source.ToString().IsNullOrWhiteSpace()) + return null; + + var model = JsonConvert.DeserializeObject(source.ToString()); + if (model.Values == null) + return null; + + var innerPropType = VortoHelper.GetInnerPublishedPropertyType(propertyType); + if (innerPropType == null) + return null; + + var modelKeys = model.Values.Keys.ToArray(); + foreach (var key in modelKeys) { - return JsonConvert.DeserializeObject(source.ToString()); + model.Values[key] = innerPropType.ConvertDataToSource(model.Values[key], preview); } + + return model; } catch (Exception e) { @@ -33,5 +48,59 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o return null; } + + public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) + { + var vortoValue = source as VortoValue; + if (vortoValue != null) + { + var innerPropType = VortoHelper.GetInnerPublishedPropertyType(propertyType); + if (innerPropType != null) { + + var type = GetPropertyValueType(propertyType); + var model = Activator.CreateInstance(type); + + var dtdGuidProp = type.GetProperty("DtdGuid", BindingFlags.Instance | BindingFlags.Public); + if (dtdGuidProp != null && dtdGuidProp.CanWrite) dtdGuidProp.SetValue(model, vortoValue.DtdGuid); + + var valuesProp = type.GetProperty("Values", BindingFlags.Instance | BindingFlags.Public); + var valuesAdd = valuesProp.PropertyType.GetMethod("Add", new[] { typeof(string), innerPropType.ClrType }); + + var modelKeys = vortoValue.Values.Keys.ToArray(); + foreach (var key in modelKeys) + { + var value = innerPropType.ConvertSourceToObject(vortoValue.Values[key], preview); + if (innerPropType.ClrType.IsAssignableFrom(value.GetType())) + { + valuesAdd.Invoke(valuesProp.GetValue(model), new[] { key, value }); + } + else + { + var attempt = value.TryConvertTo(innerPropType.ClrType); + if (attempt.Success) + valuesAdd.Invoke(valuesProp.GetValue(model), new[] { key, attempt.Result }); + } + } + + return model; + } + } + + return base.ConvertSourceToObject(propertyType, source, preview); + } + + public Type GetPropertyValueType(PublishedPropertyType propertyType) + { + var innerPropType = VortoHelper.GetInnerPublishedPropertyType(propertyType); + + return innerPropType != null + ? typeof(VortoValue<>).MakeGenericType(innerPropType.ClrType) + : typeof(VortoValue); + } + + public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue) + { + return PropertyCacheLevel.Content; + } } } diff --git a/src/Our.Umbraco.Vorto/Extensions/IPublishedContentExtensions.cs b/src/Our.Umbraco.Vorto/Extensions/IPublishedContentExtensions.cs index 83bb21b..845bc76 100644 --- a/src/Our.Umbraco.Vorto/Extensions/IPublishedContentExtensions.cs +++ b/src/Our.Umbraco.Vorto/Extensions/IPublishedContentExtensions.cs @@ -1,11 +1,10 @@ -using System.Threading; +using System.Collections.Generic; +using System.Threading; using Newtonsoft.Json; -using Our.Umbraco.Vorto.Helpers; using Our.Umbraco.Vorto.Models; using Our.Umbraco.Vorto.Web.PropertyEditors; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; namespace Our.Umbraco.Vorto.Extensions @@ -14,58 +13,51 @@ public static class IPublishedContentExtensions { #region HasValue - private static bool DoInnerHasVortoValue(this IPublishedContent content, string propertyAlias, - string cultureName = null, bool recursive = false) - { - if (content.HasValue(propertyAlias)) - { - var prop = content.GetProperty(propertyAlias); - var dataValue = prop.DataValue; - if (dataValue == null) - { - return false; - } - - VortoValue vortoModel; - - try - { - // We purposfully parse the raw data value ourselves bypassing the property - // value converters so that we don't require an UmbracoContext during a - // HasValue check. As we won't actually use the value, this is ok here. - vortoModel = JsonConvert.DeserializeObject(dataValue.ToString()); - } - catch - { - return false; - } - - if (vortoModel?.Values != null) - { - var bestMatchCultureName = vortoModel.FindBestMatchCulture(cultureName); - if (!bestMatchCultureName.IsNullOrWhiteSpace() - && vortoModel.Values.ContainsKey(bestMatchCultureName) - && vortoModel.Values[bestMatchCultureName] != null - && !vortoModel.Values[bestMatchCultureName].ToString().IsNullOrWhiteSpace()) - return true; - } - } - - return recursive && content.Parent != null - ? content.Parent.DoInnerHasVortoValue(propertyAlias, cultureName, recursive) - : false; - } - private static bool DoHasVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, bool recursive = false) { - if (cultureName == null) - cultureName = Thread.CurrentThread.CurrentUICulture.Name; + if (content.HasValue(propertyAlias)) + { + var prop = content.GetProperty(propertyAlias); + if (prop == null) + { + return false; + } - if (!content.HasValue(propertyAlias, recursive)) - return false; + var dataValue = prop.DataValue; + if (dataValue == null) + { + return false; + } + + VortoValue vortoModel; + + try + { + // We purposfully parse the raw data value ourselves bypassing the property + // value converters so that we don't require an UmbracoContext during a + // HasValue check. As we won't actually use the value, this is ok here. + vortoModel = JsonConvert.DeserializeObject(dataValue.ToString()); + } + catch + { + return false; + } - return content.DoInnerHasVortoValue(propertyAlias, cultureName, recursive); + if (vortoModel?.Values != null) + { + var bestMatchCultureName = vortoModel.FindBestMatchCulture(cultureName); + if (!bestMatchCultureName.IsNullOrWhiteSpace() + && vortoModel.Values.ContainsKey(bestMatchCultureName) + && vortoModel.Values[bestMatchCultureName] != null + && !vortoModel.Values[bestMatchCultureName].ToString().IsNullOrWhiteSpace()) + return true; + } + } + + return recursive && content.Parent != null + ? content.Parent.DoHasVortoValue(propertyAlias, cultureName, recursive) + : false; } /// @@ -81,7 +73,13 @@ public static bool HasVortoValue(this IPublishedContent content, string property string cultureName = null, bool recursive = false, string fallbackCultureName = null) { - var hasValue = content.DoHasVortoValue(propertyAlias, cultureName, recursive); + if (cultureName.IsNullOrWhiteSpace()) + cultureName = Thread.CurrentThread.CurrentUICulture.Name; + + if (fallbackCultureName.IsNullOrWhiteSpace()) + fallbackCultureName = Vorto.DefaultFallbackCultureName; + + var hasValue = content.DoHasVortoValue(propertyAlias, cultureName, recursive); if (!hasValue && !string.IsNullOrEmpty(fallbackCultureName) && !fallbackCultureName.Equals(cultureName)) hasValue = content.DoHasVortoValue(propertyAlias, fallbackCultureName, recursive); return hasValue; @@ -91,112 +89,67 @@ public static bool HasVortoValue(this IPublishedContent content, string property #region GetValue - private static T DoInnerGetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, - bool recursive = false, T defaultValue = default(T)) - { - var prop = content.GetProperty(propertyAlias); - var vortoModel = prop.Value as VortoValue; - if (vortoModel?.Values != null) - { - // Get the serialized value - var bestMatchCultureName = vortoModel.FindBestMatchCulture(cultureName); - if (!bestMatchCultureName.IsNullOrWhiteSpace() - && vortoModel.Values.ContainsKey(bestMatchCultureName) - && vortoModel.Values[bestMatchCultureName] != null - && !vortoModel.Values[bestMatchCultureName].ToString().IsNullOrWhiteSpace()) - { - var value = vortoModel.Values[bestMatchCultureName]; - - // Get target datatype - var targetDataType = VortoHelper.GetTargetDataTypeDefinition(vortoModel.DtdGuid); - - // Umbraco has the concept of a IPropertyEditorValueConverter which it - // also queries for property resolvers. However I'm not sure what these - // are for, nor can I find any implementations in core, so am currently - // just ignoring these when looking up converters. - // NB: IPropertyEditorValueConverter not to be confused with - // IPropertyValueConverter which are the ones most people are creating - var properyType = CreateDummyPropertyType( - targetDataType.Id, - targetDataType.PropertyEditorAlias, - content.ContentType); - - var inPreviewMode = UmbracoContext.Current != null && UmbracoContext.Current.InPreviewMode; - - // Try convert data to source - // We try this first as the value is stored as JSON not - // as XML as would occur in the XML cache as in the act - // of converting to XML this would ordinarily get called - // but with JSON it doesn't, so we try this first - var converted1 = properyType.ConvertDataToSource(value, inPreviewMode); - if (converted1 is T) return (T)converted1; - - var convertAttempt = converted1.TryConvertTo(); - if (convertAttempt.Success) return convertAttempt.Result; - - // Try convert source to object - // If the source value isn't right, try converting to object - var converted2 = properyType.ConvertSourceToObject(converted1, inPreviewMode); - if (converted2 is T) return (T)converted2; - - convertAttempt = converted2.TryConvertTo(); - if (convertAttempt.Success) return convertAttempt.Result; - - // Try just converting - convertAttempt = value.TryConvertTo(); - if (convertAttempt.Success) return convertAttempt.Result; - - // Still not right type so return default value - return defaultValue; - } - } - - return recursive && content.Parent != null - ? content.Parent.DoInnerGetVortoValue(propertyAlias, cultureName, recursive, defaultValue) - : defaultValue; - } - private static T DoGetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, bool recursive = false, T defaultValue = default(T)) { - if (cultureName == null) - cultureName = Thread.CurrentThread.CurrentUICulture.Name; - - return content.DoInnerGetVortoValue(propertyAlias, cultureName, recursive, defaultValue); + var prop = content.GetProperty(propertyAlias); + if (prop == null) + { + // PR #100 - Prevent generation of NullReferenceException: allow return of defaultValue or traversal up the tree if prop is null + return defaultValue; + } + + var vortoModel = prop.Value as VortoValue; + if (vortoModel != null) + { + return vortoModel.GetValue(cultureName, defaultValue); + } + + return recursive && content.Parent != null + ? content.Parent.DoGetVortoValue(propertyAlias, cultureName, recursive, defaultValue) + : defaultValue; } - /// - /// Gets the Vorto value for the given content property as the given type. - /// - /// The type of value to return - /// The cached content - /// The property alias - /// The culture name in the format languagecode2-country/regioncode2 - /// Whether to recursively travel up the content tree looking for the value - /// The default value to return if none is found - /// The culture name in the format languagecode2-country/regioncode2. Optional - /// The value - public static T GetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, + /// + /// Gets the Vorto value for the given content property as the given type. + /// + /// The type of value to return + /// The cached content + /// The property alias + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// Whether to recursively travel up the content tree looking for the value. Optional + /// The default value to return if none is found. Optional + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The value + public static T GetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, bool recursive = false, T defaultValue = default(T), string fallbackCultureName = null) - { - var result = content.DoGetVortoValue(propertyAlias, cultureName, recursive, default(T)); - if (result == null && !string.IsNullOrEmpty(fallbackCultureName) && !fallbackCultureName.Equals(cultureName)) - result = content.DoGetVortoValue(propertyAlias, fallbackCultureName, recursive, defaultValue); + { + if (cultureName.IsNullOrWhiteSpace()) + cultureName = Thread.CurrentThread.CurrentUICulture.Name; + + if (fallbackCultureName.IsNullOrWhiteSpace()) + fallbackCultureName = Vorto.DefaultFallbackCultureName; + + var result = content.DoGetVortoValue(propertyAlias, cultureName, recursive); + if (EqualityComparer.Default.Equals(result, default(T)) && !string.IsNullOrEmpty(fallbackCultureName) && !fallbackCultureName.Equals(cultureName)) + result = content.DoGetVortoValue(propertyAlias, fallbackCultureName, recursive); + if (EqualityComparer.Default.Equals(result, default(T))) + result = defaultValue; return result; } - /// - /// Gets the Vorto value for the given content property. - /// - /// The cached content - /// The property alias - /// The culture name in the format languagecode2-country/regioncode2 - /// Whether to recursively travel up the content tree looking for the value - /// The default value to return if none is found - /// The culture name in the format languagecode2-country/regioncode2. Optional - /// The value - public static object GetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, + /// + /// Gets the Vorto value for the given content property. + /// + /// The cached content + /// The property alias + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// Whether to recursively travel up the content tree looking for the value. Optional + /// The default value to return if none is found. Optional + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The value + public static object GetVortoValue(this IPublishedContent content, string propertyAlias, string cultureName = null, bool recursive = false, object defaultValue = null, string fallbackCultureName = null) { @@ -219,14 +172,5 @@ public static bool IsVortoProperty(this IPublishedContent content, string proper } #endregion - - private static PublishedPropertyType CreateDummyPropertyType(int dataTypeId, string propertyEditorAlias, PublishedContentType contentType) - { - return new PublishedPropertyType(contentType, - new PropertyType(new DataTypeDefinition(-1, propertyEditorAlias) - { - Id = dataTypeId - })); - } } } diff --git a/src/Our.Umbraco.Vorto/Extensions/StringExtensions.cs b/src/Our.Umbraco.Vorto/Extensions/StringExtensions.cs new file mode 100644 index 0000000..8c9de71 --- /dev/null +++ b/src/Our.Umbraco.Vorto/Extensions/StringExtensions.cs @@ -0,0 +1,12 @@ +namespace Our.Umbraco.Vorto.Extensions +{ + internal static class StringExtensions + { + public static bool DetectIsJson(this string input) + { + input = input.Trim(); + return (input.StartsWith("{") && input.EndsWith("}")) + || (input.StartsWith("[") && input.EndsWith("]")); + } + } +} diff --git a/src/Our.Umbraco.Vorto/Extensions/VortoValueExtensions.cs b/src/Our.Umbraco.Vorto/Extensions/VortoValueExtensions.cs index 8b816f0..e3a9bb4 100644 --- a/src/Our.Umbraco.Vorto/Extensions/VortoValueExtensions.cs +++ b/src/Our.Umbraco.Vorto/Extensions/VortoValueExtensions.cs @@ -1,11 +1,12 @@ using System.Linq; +using Umbraco.Core; using Our.Umbraco.Vorto.Models; namespace Our.Umbraco.Vorto.Extensions { - internal static class VortoValueExtensions + internal static class VortoValueExtensions { - public static string FindBestMatchCulture(this VortoValue value, string cultureName) + internal static string FindBestMatchCulture(this VortoValue value, string cultureName) { // Check for actual values if (value.Values == null) @@ -17,8 +18,8 @@ public static string FindBestMatchCulture(this VortoValue value, string cultureN // Close match return cultureName.Length == 2 - ? value.Values.Keys.FirstOrDefault(x => x.StartsWith(cultureName + "-")) + ? value.Values.Keys.FirstOrDefault(x => x.InvariantStartsWith(cultureName + "-")) : string.Empty; } - } + } } diff --git a/src/Our.Umbraco.Vorto/Helpers/VortoHelper.cs b/src/Our.Umbraco.Vorto/Helpers/VortoHelper.cs index a374b28..1c95b0b 100644 --- a/src/Our.Umbraco.Vorto/Helpers/VortoHelper.cs +++ b/src/Our.Umbraco.Vorto/Helpers/VortoHelper.cs @@ -3,6 +3,7 @@ using Our.Umbraco.Vorto.Models; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; namespace Our.Umbraco.Vorto.Helpers { @@ -17,12 +18,44 @@ internal static IDataTypeDefinition GetTargetDataTypeDefinition(Guid myId) // Get instance of our own datatype so we can lookup the actual datatype from prevalue var services = ApplicationContext.Current.Services; var dtd = services.DataTypeService.GetDataTypeDefinitionById(myId); - var preValues = services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtd.Id).PreValuesAsDictionary; + if (dtd == null) return null; + + var preValues = services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtd.Id)?.PreValuesAsDictionary; + if (preValues == null || !preValues.ContainsKey("dataType")) return null; + var dataType = JsonConvert.DeserializeObject(preValues["dataType"].Value); // Grab an instance of the target datatype return services.DataTypeService.GetDataTypeDefinitionById(dataType.Guid); }); } + + internal static PublishedPropertyType GetInnerPublishedPropertyType(PublishedPropertyType propertyType) + { + return (PublishedPropertyType)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( + Constants.CacheKey_GetInnerPublishedPropertyType + propertyType.DataTypeId + "_"+ propertyType.ContentType.Id, + () => + { + var services = ApplicationContext.Current.Services; + + var preValues = services.DataTypeService.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId)?.PreValuesAsDictionary; + if (preValues == null || !preValues.ContainsKey("dataType")) + return null; + + var dataType = JsonConvert.DeserializeObject(preValues["dataType"].Value); + var dtd = services.DataTypeService.GetDataTypeDefinitionById(dataType.Guid); + + return new PublishedPropertyType(propertyType.ContentType, new PropertyType(dtd) { Alias = propertyType.PropertyTypeAlias }); + }); + } + + internal static PublishedPropertyType CreateDummyPropertyType(int dataTypeId, string propertyEditorAlias, PublishedContentType contentType) + { + return new PublishedPropertyType(contentType, + new PropertyType(new DataTypeDefinition(-1, propertyEditorAlias) + { + Id = dataTypeId + })); + } } } diff --git a/src/Our.Umbraco.Vorto/Models/VortoValue.cs b/src/Our.Umbraco.Vorto/Models/VortoValue.cs index 5663457..2abcf92 100644 --- a/src/Our.Umbraco.Vorto/Models/VortoValue.cs +++ b/src/Our.Umbraco.Vorto/Models/VortoValue.cs @@ -1,24 +1,8 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Our.Umbraco.Vorto.Models +namespace Our.Umbraco.Vorto.Models { - /// - /// Represents a multilingual property value - /// - public class VortoValue - { - /// - /// Gets or sets the collection of language independent values - /// - [JsonProperty("values")] - public IDictionary Values { get; set; } - - /// - /// Gets or sets the data type definition id - /// - [JsonProperty("dtdGuid")] - public Guid DtdGuid { get; set; } - } + /// + /// Represents a multilingual property value + /// + public class VortoValue : VortoValue + { } } diff --git a/src/Our.Umbraco.Vorto/Models/VortoValueOfT.cs b/src/Our.Umbraco.Vorto/Models/VortoValueOfT.cs new file mode 100644 index 0000000..d336cc4 --- /dev/null +++ b/src/Our.Umbraco.Vorto/Models/VortoValueOfT.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace Our.Umbraco.Vorto.Models +{ + /// + /// Represents a multilingual property value + /// + public partial class VortoValue + { + /// + /// Gets or sets the collection of language independent values + /// + [JsonProperty("values")] + public IDictionary Values { get; set; } + + /// + /// Gets or sets the data type definition id + /// + [JsonProperty("dtdGuid")] + public Guid DtdGuid { get; set; } + + public VortoValue() + { + Values = new Dictionary(); + } + } +} diff --git a/src/Our.Umbraco.Vorto/Models/VortoValueOfTApi.cs b/src/Our.Umbraco.Vorto/Models/VortoValueOfTApi.cs new file mode 100644 index 0000000..603bcdb --- /dev/null +++ b/src/Our.Umbraco.Vorto/Models/VortoValueOfTApi.cs @@ -0,0 +1,98 @@ +using Newtonsoft.Json; +using Our.Umbraco.Vorto.Extensions; +using System; +using System.Collections.Generic; +using System.Threading; +using Umbraco.Core; + +namespace Our.Umbraco.Vorto.Models +{ + public partial class VortoValue + { + private bool DoHasValue(string cultureName) + { + if (Values == null || Values.Count == 0) + return false; + + var bestMatchCultureName = this.FindBestMatchCulture(cultureName); + if (!bestMatchCultureName.IsNullOrWhiteSpace() + && Values.ContainsKey(bestMatchCultureName) + && EqualityComparer.Default.Equals(Values[bestMatchCultureName], default(T))) + return true; + + return false; + } + + private T DoGetValue(string cultureName, T defaultValue = default(T)) + { + // Get the serialized value + var bestMatchCultureName = this.FindBestMatchCulture(cultureName); + if (!bestMatchCultureName.IsNullOrWhiteSpace() + && Values.ContainsKey(bestMatchCultureName) + && Values[bestMatchCultureName] != null + && !Values[bestMatchCultureName].ToString().IsNullOrWhiteSpace()) + { + var value = Values[bestMatchCultureName]; + var attempt = value.TryConvertTo(); + return attempt.Success ? attempt.Result : defaultValue; + } + + return defaultValue; + } + + /// + /// Returns a value indicating whether the given Vorto model has a value for the given culture + /// + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The + public bool HasValue(string cultureName = null, string fallbackCultureName = null) + { + if (cultureName.IsNullOrWhiteSpace()) + cultureName = Thread.CurrentThread.CurrentUICulture.Name; + + if (fallbackCultureName.IsNullOrWhiteSpace()) + fallbackCultureName = Vorto.DefaultFallbackCultureName; + + var hasValue = DoHasValue(cultureName); + if (!hasValue && !string.IsNullOrEmpty(fallbackCultureName) && !fallbackCultureName.Equals(cultureName)) + hasValue = DoHasValue(fallbackCultureName); + return hasValue; + } + + /// + /// Gets the Vorto value of the given type for the given culture. + /// + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The default value to return if none is found. Optional + /// The culture name in the format languagecode2-country/regioncode2. Optional + /// The value + public T GetValue(string cultureName = null, T defaultValue = default(T), string fallbackCultureName = null) + { + if (cultureName.IsNullOrWhiteSpace()) + cultureName = Thread.CurrentThread.CurrentUICulture.Name; + + if (fallbackCultureName.IsNullOrWhiteSpace()) + fallbackCultureName = Vorto.DefaultFallbackCultureName; + + var result = DoGetValue(cultureName); + if (EqualityComparer.Default.Equals(result, default(T)) && !string.IsNullOrEmpty(fallbackCultureName) && !fallbackCultureName.Equals(cultureName)) + result = DoGetValue(fallbackCultureName); + if (EqualityComparer.Default.Equals(result, default(T))) + result = defaultValue; + return result; + } + + /// + /// Returns value for the current culture with global fallback + /// + [JsonIgnore] + public T Current => GetValue(); + + /// + /// Gets a language value by key + /// + [JsonIgnore] + public T this[string key] => DoGetValue(key); + } +} diff --git a/src/Our.Umbraco.Vorto/Our.Umbraco.Vorto.csproj b/src/Our.Umbraco.Vorto/Our.Umbraco.Vorto.csproj index e2a5ced..d75c510 100644 --- a/src/Our.Umbraco.Vorto/Our.Umbraco.Vorto.csproj +++ b/src/Our.Umbraco.Vorto/Our.Umbraco.Vorto.csproj @@ -36,143 +36,147 @@ False ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + False False ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + False - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\businesslogic.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\businesslogic.dll False ..\packages\ClientDependency.1.7.1.2\lib\ClientDependency.Core.dll + False False ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll + False - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\cms.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\cms.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\controls.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\controls.dll False ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll + False - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\Examine.dll + + ..\packages\Examine.0.1.57.2941\lib\Examine.dll False ..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll + False False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + False - - False - ..\packages\ImageProcessor.1.9.0.0\lib\ImageProcessor.dll + + ..\packages\ImageProcessor.1.9.5.0\lib\ImageProcessor.dll - - False - ..\packages\ImageProcessor.Web.3.2.3.0\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.3.3.0.0\lib\net45\ImageProcessor.Web.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\interfaces.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\interfaces.dll - False - ..\packages\UmbracoCms.Core.7.1.4\lib\log4net.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\log4net.dll False ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + False - False - ..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.ApplicationBlocks.Data.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\Microsoft.ApplicationBlocks.Data.dll - False - ..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.Web.Helpers.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\Microsoft.Web.Helpers.dll - True + False ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False ..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.1\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + False False ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll + False False ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll + False - False - ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + True - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\SQLCE4Umbraco.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\SQLCE4Umbraco.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\System.Data.SqlServerCe.dll True - ..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\System.Data.SqlServerCe.Entity.dll True - ..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.Entity.dll ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll + False - True + False ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll ..\packages\Microsoft.AspNet.WebApi.Core.4.0.30506.0\lib\net40\System.Web.Http.dll + False ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.30506.0\lib\net40\System.Web.Http.WebHost.dll + False - True + False ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll - True + False ..\packages\Microsoft.AspNet.Razor.2.0.30506.0\lib\net40\System.Web.Razor.dll - True + False ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll - True + False ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll - True + False ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll @@ -181,52 +185,42 @@ - False - ..\packages\UmbracoCms.Core.7.1.4\lib\TidyNet.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\TidyNet.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Core.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\Umbraco.Core.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.DataLayer.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.DataLayer.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.editorControls.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.editorControls.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.MacroEngines.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.MacroEngines.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.providers.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.providers.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Web.UI.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\Umbraco.Web.UI.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.XmlSerializers.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\umbraco.XmlSerializers.dll - - False - ..\packages\UmbracoCms.Core.7.1.4\lib\UmbracoExamine.dll + + ..\packages\UmbracoCms.Core.7.1.5\lib\UmbracoExamine.dll - False - ..\packages\UmbracoCms.Core.7.1.4\lib\UrlRewritingNet.UrlRewriter.dll + ..\packages\UmbracoCms.Core.7.1.5\lib\UrlRewritingNet.UrlRewriter.dll + @@ -234,7 +228,10 @@ + + + @@ -265,20 +262,20 @@ REM # Copy files REM ################################################# -IF %25ComputerName%25 == MBP13-PC-BC ( +IF %25ComputerName%25 == DESKTOP-ESFKU58 ( IF NOT "$(SolutionDir)" == "*Undefined*" ( -xcopy /s /y "$(TargetPath)" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.4.0\bin" -xcopy /s /y "$(TargetDir)Our.Umbraco.Vorto.pdb" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.4.0\bin" -xcopy /s /y "$(ProjectDir)Web\UI\*" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.4.0" +xcopy /s /y "$(TargetPath)" "C:\Users\Matt\Storage\Sandbox\Umbraco\UmbracoCms.7.10.4\bin" +xcopy /s /y "$(TargetDir)Our.Umbraco.Vorto.pdb" "C:\Users\Matt\Storage\Sandbox\Umbraco\UmbracoCms.7.10.4\bin" +xcopy /s /y "$(ProjectDir)Web\UI\*" "C:\Users\Matt\Storage\Sandbox\Umbraco\UmbracoCms.7.10.4" ) ) - + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - +