From e0fb09c6bcff714b855c8735a2fa4f1c59bab1d7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Sep 2013 11:02:59 +1000 Subject: [PATCH] Added better backwards compatibility for when people are using the legacy business logic APIs but they are using new property editors when there is no legacy property editor predecessor (IDataType implementation). --- src/Umbraco.Core/Models/DataTypeDefinition.cs | 8 ++- src/Umbraco.Core/Models/PropertyType.cs | 8 ++- .../BackwardsCompatibleData.cs | 43 +++++++++++++ .../BackwardsCompatibleDataType.cs | 62 +++++++++++++++++++ .../LegacyPropertyEditorIdToAliasConverter.cs | 28 +++++++-- src/Umbraco.Core/PublishedContentHelper.cs | 6 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 + ...cyPropertyEditorIdToAliasConverterTests.cs | 23 ++++++- .../umbraco/Trees/BaseMediaTree.cs | 2 +- .../datatype/DataTypeDefinition.cs | 22 ++++++- 10 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs create mode 100644 src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs index 943bb134d9..6f7fac6db7 100644 --- a/src/Umbraco.Core/Models/DataTypeDefinition.cs +++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs @@ -191,10 +191,14 @@ namespace Umbraco.Core.Models /// Id of the DataType control /// [DataMember] - [Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead")] + [Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead. This method will return a generated GUID for any property editor alias not explicitly mapped to a legacy ID")] public Guid ControlId { - get { return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(_propertyEditorAlias, true).Value; } + get + { + return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias( + _propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId).Value; + } set { var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value, true); diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index c5a703df67..c1526a9ffb 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -141,10 +141,14 @@ namespace Umbraco.Core.Models /// Gets of Sets the Id of the DataType control /// /// This is the Id of the actual DataType control - [Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead")] + [Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the PropertyEditorAlias property instead. This method will return a generated GUID for any property editor alias not explicitly mapped to a legacy ID")] public Guid DataTypeId { - get { return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(_propertyEditorAlias, true).Value; } + get + { + return LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias( + _propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId).Value; + } set { var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value, true); diff --git a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs new file mode 100644 index 0000000000..9283256e54 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs @@ -0,0 +1,43 @@ +using System; +using System.Xml; +using umbraco.interfaces; + +namespace Umbraco.Core.PropertyEditors +{ + + /// + /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a + /// legacy property editor predecessor when developers are using the legacy APIs + /// + internal class BackwardsCompatibleData : IData + { + public int PropertyId { set; get; } + + public object Value { get; set; } + + + public XmlNode ToXMl(XmlDocument data) + { + throw new NotSupportedException( + typeof(IData) + + " is a legacy object and is not supported by runtime generated " + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + + public void MakeNew(int PropertyId) + { + throw new NotSupportedException( + typeof(IData) + + " is a legacy object and is not supported by runtime generated " + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + + public void Delete() + { + throw new NotSupportedException( + typeof(IData) + + " is a legacy object and is not supported by runtime generated " + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs new file mode 100644 index 0000000000..0bee6322b3 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleDataType.cs @@ -0,0 +1,62 @@ +using System; +using umbraco.interfaces; + +namespace Umbraco.Core.PropertyEditors +{ + + /// + /// This is used purelty to attempt to maintain some backwards compatibility with new property editors that don't have a + /// legacy property editor predecessor when developers are using the legacy APIs + /// + internal class BackwardsCompatibleDataType : IDataType + { + public Guid Id { get; private set; } + public string DataTypeName { get; private set; } + public IData Data { get; private set; } + public int DataTypeDefinitionId { get; set; } + + /// + /// Creates a runtime instance + /// + /// + /// + /// + /// + internal static BackwardsCompatibleDataType Create(string propEdAlias, Guid legacyId, int dataTypeDefId) + { + var dt = new BackwardsCompatibleDataType + { + Id = legacyId, + DataTypeName = propEdAlias, + DataTypeDefinitionId = dataTypeDefId, + Data = new BackwardsCompatibleData() + }; + + return dt; + } + + public IDataEditor DataEditor + { + get + { + throw new NotSupportedException( + typeof(IDataEditor) + + " is a legacy object and is not supported by runtime generated " + + typeof(IDataType) + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } + public IDataPrevalue PrevalueEditor + { + get + { + throw new NotSupportedException( + typeof(IDataPrevalue) + + " is a legacy object and is not supported by runtime generated " + + typeof(IDataType) + + " instances to maintain backwards compatibility with the legacy APIs. Consider upgrading your code to use the new Services APIs."); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs index e36b86b94c..53a4365244 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; +using Umbraco.Core.Logging; namespace Umbraco.Core.PropertyEditors { @@ -14,6 +15,14 @@ namespace Umbraco.Core.PropertyEditors /// public static class LegacyPropertyEditorIdToAliasConverter { + + public enum NotFoundLegacyIdResponseBehavior + { + ThrowException, + ReturnNull, + GenerateId + } + /// /// The map consists of a key which is always the GUID (lowercase, no hyphens + alias (trimmed)) /// @@ -55,18 +64,27 @@ namespace Umbraco.Core.PropertyEditors /// Gets a legacy Id based on the alias /// /// - /// if set to true will throw an exception if the map isn't found + /// /// Returns the legacy GUID of a property editor if found, otherwise returns null - public static Guid? GetLegacyIdFromAlias(string alias, bool throwIfNotFound = false) + public static Guid? GetLegacyIdFromAlias(string alias, NotFoundLegacyIdResponseBehavior notFoundBehavior) { var found = _map.FirstOrDefault(x => x.Value.Item2 == alias); if (found.Equals(default(KeyValuePair>))) { - if (throwIfNotFound) + switch (notFoundBehavior) { - throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias + ". Consider using the new business logic APIs instead of the old obsoleted ones."); + case NotFoundLegacyIdResponseBehavior.ThrowException: + throw new ObjectNotFoundException("Could not find a map for a property editor with an alias of " + alias + ". Consider using the new business logic APIs instead of the old obsoleted ones."); + case NotFoundLegacyIdResponseBehavior.ReturnNull: + return null; + case NotFoundLegacyIdResponseBehavior.GenerateId: + var generated = alias.EncodeAsGuid(); + CreateMap(generated, alias); + + LogHelper.Warn(typeof(LegacyPropertyEditorIdToAliasConverter), "A legacy GUID id was generated for property editor " + alias + ". This occurs when the legacy APIs are used and done to attempt to maintain backwards compatibility. Consider upgrading all code to use the new Services APIs instead to avoid any potential issues."); + + return generated; } - return null; } return found.Value.Item1; } diff --git a/src/Umbraco.Core/PublishedContentHelper.cs b/src/Umbraco.Core/PublishedContentHelper.cs index a6a3c1cadc..1707f0f745 100644 --- a/src/Umbraco.Core/PublishedContentHelper.cs +++ b/src/Umbraco.Core/PublishedContentHelper.cs @@ -113,16 +113,16 @@ namespace Umbraco.Core //if it is good return it, otherwise we'll continue processing the legacy stuff below. if (result.Success) { - return new Attempt(true, result.Result); + return Attempt.Succeed(result.Result); } } //In order to maintain backwards compatibility here with IPropertyEditorValueConverter we need to attempt to lookup the // legacy GUID for the current property editor. If one doesn't exist then we will abort the conversion. - var legacyId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(propertyDefinition.PropertyEditorAlias); + var legacyId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(propertyDefinition.PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ReturnNull); if (legacyId.HasValue == false) { - return Attempt.False; + return Attempt.Fail(); } //First lets check all registered converters for this data type. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a1d9969d52..2d79ca600e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -417,6 +417,8 @@ + + diff --git a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs index d82ce6a259..f27b201005 100644 --- a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs @@ -28,7 +28,11 @@ namespace Umbraco.Tests.PropertyEditors var id = Guid.NewGuid(); LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - Assert.AreEqual(id, LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("test", true)); + Assert.AreEqual( + id, + LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias( + "test", + LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ThrowException)); } [Test] @@ -37,7 +41,22 @@ namespace Umbraco.Tests.PropertyEditors var id = Guid.NewGuid(); LegacyPropertyEditorIdToAliasConverter.CreateMap(id, "test"); - Assert.AreEqual("test", LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(id, true)); + Assert.AreEqual( + "test", + LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId( + id, + true)); + } + + [Test] + public void Can_Generate_Id_From_Missing_Alias() + { + var gen1 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); + var gen2 = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias("Donotfindthisone", LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); + + Assert.IsNotNull(gen1); + Assert.IsNotNull(gen2); + Assert.AreEqual(gen1, gen2); } [Test] diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs index 3f7f5a5bfc..5a033ace09 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs @@ -171,7 +171,7 @@ function openMedia(id) { foreach (var property in entity.UmbracoProperties) { //required for backwards compatibility with v7 with changing the GUID -> alias - var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(property.PropertyEditorAlias); + var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(property.PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.ReturnNull); if (controlId != null) { if (LinkableMediaDataTypes.Contains(controlId.Value) && diff --git a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs index 162880f143..8ae006f9a6 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs @@ -13,6 +13,9 @@ using Umbraco.Core; namespace umbraco.cms.businesslogic.datatype { + + + /// /// Datatypedefinitions is the basic buildingblocks of umbraco's documents/medias/members generic datastructure /// @@ -59,12 +62,27 @@ namespace umbraco.cms.businesslogic.datatype if (_propertyEditorAlias.IsNullOrWhiteSpace()) return null; - var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(_propertyEditorAlias, true); + //Attempt to resolve a legacy control id from the alias. If one is not found we'll generate one - + // the reason one will not be found is if there's a new v7 property editor created that doesn't have a legacy + // property editor predecessor. + //So, we'll generate an id for it based on the alias which will remain consistent, but then we'll try to resolve a legacy + // IDataType which of course will not exist. In this case we'll have to create a new one on the fly for backwards compatibility but + // this instance will have limited capabilities and will really only work for saving data so the legacy APIs continue to work. + var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(_propertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); var dt = DataTypesResolver.Current.GetById(controlId.Value); - + if (dt != null) + { dt.DataTypeDefinitionId = Id; + } + else + { + //Ok so it was not found, we can only assume that this is because this is a new property editor that does not have a legacy predecessor. + //we'll have to attempt to generate one at runtime. + dt = BackwardsCompatibleDataType.Create(_propertyEditorAlias, controlId.Value, Id); + } + return dt; }