Files
Umbraco-CMS/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs

147 lines
5.9 KiB
C#

using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using umbraco.interfaces;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// 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
/// </summary>
internal class BackwardsCompatibleData : IData, IDataValueSetter
{
private readonly string _propertyEditorAlias;
private bool _valueLoaded = false;
private object _value;
public BackwardsCompatibleData(string propertyEditorAlias)
{
_propertyEditorAlias = propertyEditorAlias;
}
public int PropertyId { set; get; }
/// <summary>
/// This returns the value
/// </summary>
/// <remarks>
/// There's code here to load the data from the db just like the legacy DefaultData does but in theory the value of this
/// IData should always be set using the IDataValueSetter.SetValue which is done externally. Just in case there's some edge
/// case out there that doesn't set this value, we'll go and get it based on the same logic in DefaultData.
/// </remarks>
public virtual object Value
{
get
{
//Lazy load the value when it is required.
if (_valueLoaded == false)
{
LoadValueFromDatabase();
_valueLoaded = true;
}
return _value;
}
set
{
_value = value;
_valueLoaded = true;
}
}
public XmlNode ToXMl(XmlDocument data)
{
//we'll get the property editor by alias, if it exists (which it absolutely should), then we'll have to create a
// fake 'Property' object and pass it to the ConvertDbToXml method so we can get the correct XML fragment that
// it needs to make.
var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias);
if (propertyEditor != null)
{
//create a 'fake' property - we will never know the actual db type here so we'll just make it nvarchar, this shouldn't
// make any difference for the conversion process though.
var property = new Property(new PropertyType(_propertyEditorAlias, DataTypeDatabaseType.Nvarchar))
{
Id = PropertyId,
Value = Value
};
var xd = new XmlDocument();
var xNode = propertyEditor.ValueEditor.ConvertDbToXml(property, property.PropertyType, ApplicationContext.Current.Services.DataTypeService);
//check if this xml fragment can be converted to an XmlNode
var xContainer = xNode as XContainer;
if (xContainer != null)
{
//imports to the document
xContainer.GetXmlNode(xd);
// return the XML node.
return data.ImportNode(xd.DocumentElement, true);
}
return ReturnCDataElement(data);
}
//if for some reason the prop editor wasn't found we'll default to returning the string value in a CDATA block.
return ReturnCDataElement(data);
}
private XmlNode ReturnCDataElement(XmlDocument doc)
{
var sValue = Value != null ? Value.ToString() : string.Empty;
return doc.CreateCDataSection(sValue);
}
public void MakeNew(int propertyId)
{
//DO nothing
}
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.");
}
/// <summary>
/// This is here for performance reasons since in some cases we will have already resolved the value from the db
/// and want to just give this object the value so it doesn't go re-look it up from the database.
/// </summary>
/// <param name="val"></param>
/// <param name="strDbType"></param>
void IDataValueSetter.SetValue(object val, string strDbType)
{
_value = val;
_valueLoaded = true;
}
/// <summary>
/// In the case where the value is not set, this will go get it from the db ourselves - this shouldn't really ever be needed,
/// the value should always be set with IDataValueSetter.SetValue
/// </summary>
private void LoadValueFromDatabase()
{
var sql = new Sql();
sql.Select("*")
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, PropertyDataDto>(x => x.Id, y => y.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<DataTypeDto, PropertyTypeDto>(x => x.DataTypeId, y => y.DataTypeId)
.Where<PropertyDataDto>(x => x.Id == PropertyId);
var dto = ApplicationContext.Current.DatabaseContext.Database.Fetch<PropertyDataDto, PropertyTypeDto, DataTypeDto>(sql).FirstOrDefault();
if (dto != null)
{
//get the value for the data type, if null, set it to an empty string
_value = dto.GetValue;
}
}
}
}