using System; using System.Linq; using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Dynamics; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core { internal class PublishedContentHelper { /// /// This callback is used only so we can set it dynamically because in the "Core" project currently we don't have /// access to the business logic layer. /// TODO: Once 6.0 is released we need to change this to use the new business logic layer that we can access from /// this proejct. Until then this will return a Guid.Empty but the callback will need to be set in the WebBootManager /// to work in the website. if people use this in a non-web aspect without the WebBootManager, the the IPropertyEditorValueConverters /// will not be executed. /// internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => Guid.Empty; internal static Guid GetDataType(string docTypeAlias, string propertyAlias) { return GetDataTypeCallback(docTypeAlias, propertyAlias); } /// /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards. /// /// /// /// /// /// internal static Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias) { if (currentValue == null) return Attempt.False; //First lets check all registered converters for this data type. var converters = PropertyEditorValueConvertersResolver.Current.Converters .Where(x => x.IsConverterFor(dataType, docTypeAlias, propertyTypeAlias)) .ToArray(); //try to convert the value with any of the converters: foreach (var converted in converters .Select(p => p.ConvertPropertyValue(currentValue)) .Where(converted => converted.Success)) { return new Attempt(true, converted.Result); } //if none of the converters worked, then we'll process this from what we know var sResult = Convert.ToString(currentValue).Trim(); //this will eat csv strings, so only do it if the decimal also includes a decimal seperator (according to the current culture) if (sResult.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) { decimal dResult; if (decimal.TryParse(sResult, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.CurrentCulture, out dResult)) { return new Attempt(true, dResult); } } //process string booleans as booleans if (sResult.InvariantEquals("true")) { return new Attempt(true, true); } if (sResult.InvariantEquals("false")) { return new Attempt(true, false); } //a really rough check to see if this may be valid xml //TODO: This is legacy code, I'm sure there's a better and nicer way if (sResult.StartsWith("<") && sResult.EndsWith(">") && sResult.Contains("/")) { try { var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); //check that the document element is not one of the disallowed elements //allows RTE to still return as html if it's valid xhtml var documentElement = e.Name.LocalName; //TODO: See note against this setting, pretty sure we don't need this if (!UmbracoSettings.NotDynamicXmlDocumentElements.Any( tag => string.Equals(tag, documentElement, StringComparison.CurrentCultureIgnoreCase))) { return new Attempt(true, new DynamicXml(e)); } return Attempt.False; } catch (Exception) { return Attempt.False; } } return Attempt.False; } } }