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 { /// /// 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, IDataValueSetter { private readonly string _propertyEditorAlias; private bool _valueLoaded = false; private object _value; public BackwardsCompatibleData(string propertyEditorAlias) { _propertyEditorAlias = propertyEditorAlias; } public int PropertyId { set; get; } /// /// This returns the value /// /// /// 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. /// 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."); } /// /// 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. /// /// /// void IDataValueSetter.SetValue(object val, string strDbType) { _value = val; _valueLoaded = true; } /// /// 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 /// private void LoadValueFromDatabase() { var sql = new Sql(); sql.Select("*") .From() .InnerJoin() .On(x => x.Id, y => y.PropertyTypeId) .InnerJoin() .On(x => x.DataTypeId, y => y.DataTypeId) .Where(x => x.Id == PropertyId); var dto = ApplicationContext.Current.DatabaseContext.Database.Fetch(sql).FirstOrDefault(); if (dto != null) { //get the value for the data type, if null, set it to an empty string _value = dto.GetValue; } } } }