From 59fa733156263af7acc758c30c703134cac435d2 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Sat, 8 Sep 2012 07:13:03 +0700 Subject: [PATCH] Created MacroTagParser and updated ItemRenderer to use it. Have DynamicDocument rendering RTE content with macro content without having to use any special methods, we just use a new custom Property Editor value converter. Now just need to port this over to DynamicNode too. Obsoleted a method on the 'helper' class to something much more meaningful... hopefully we can do that with the rest of that class too. --- .../Dynamics/IRazorDataTypeModel.cs | 7 - .../Dynamics/RazorDataTypeModel.cs | 24 - src/Umbraco.Core/Dynamics/RazorLibraryCore.cs | 583 ------------------ src/Umbraco.Core/Macros/MacroTagParser.cs | 88 +++ .../Macros/PersistableMacroProperty.cs | 2 +- .../TinyMcePropertyEditorValueConverter.cs | 5 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- src/Umbraco.Core/XmlHelper.cs | 19 + ...roRenderingPropertyEditorValueConverter.cs | 45 ++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoHelper.cs | 22 +- src/Umbraco.Web/WebBootManager.cs | 7 + .../umbraco.presentation/helper.cs | 20 +- .../umbraco/templateControls/ItemRenderer.cs | 524 ++++++++-------- src/umbraco.cms/helpers/xhtml.cs | 21 +- .../tinyMCE3/TinyMCE.cs | 22 +- 16 files changed, 459 insertions(+), 935 deletions(-) delete mode 100644 src/Umbraco.Core/Dynamics/IRazorDataTypeModel.cs delete mode 100644 src/Umbraco.Core/Dynamics/RazorDataTypeModel.cs delete mode 100644 src/Umbraco.Core/Dynamics/RazorLibraryCore.cs create mode 100644 src/Umbraco.Core/Macros/MacroTagParser.cs create mode 100644 src/Umbraco.Web/PropertyEditors/RteMacroRenderingPropertyEditorValueConverter.cs diff --git a/src/Umbraco.Core/Dynamics/IRazorDataTypeModel.cs b/src/Umbraco.Core/Dynamics/IRazorDataTypeModel.cs deleted file mode 100644 index ca17e4c81f..0000000000 --- a/src/Umbraco.Core/Dynamics/IRazorDataTypeModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Umbraco.Core.Dynamics -{ - //public interface IRazorDataTypeModel - //{ - // bool Init(int currentNodeId, string propertyData, out object instance); - //} -} diff --git a/src/Umbraco.Core/Dynamics/RazorDataTypeModel.cs b/src/Umbraco.Core/Dynamics/RazorDataTypeModel.cs deleted file mode 100644 index 681dec9efe..0000000000 --- a/src/Umbraco.Core/Dynamics/RazorDataTypeModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Umbraco.Core.Dynamics -{ - //[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - //public sealed class RazorDataTypeModel : Attribute - //{ - // public readonly Guid DataTypeEditorId; - // public readonly int Priority; - - // public RazorDataTypeModel(string DataTypeEditorId) - // { - // this.DataTypeEditorId = new Guid(DataTypeEditorId); - // Priority = 100; - // } - - // public RazorDataTypeModel(string DataTypeEditorId, int Priority) - // { - // this.DataTypeEditorId = new Guid(DataTypeEditorId); - // this.Priority = Priority; - // } - //} - -} diff --git a/src/Umbraco.Core/Dynamics/RazorLibraryCore.cs b/src/Umbraco.Core/Dynamics/RazorLibraryCore.cs deleted file mode 100644 index 957d90f50b..0000000000 --- a/src/Umbraco.Core/Dynamics/RazorLibraryCore.cs +++ /dev/null @@ -1,583 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Umbraco.Core.Models; -using umbraco.interfaces; -using System.Xml.Linq; -using System.Xml.XPath; -using System.Web; -using System.IO; - -namespace Umbraco.Core.Dynamics -{ - //internal class RazorLibraryCore - //{ - // private readonly IDocument _node; - // public IDocument Node - // { - // get { return _node; } - // } - // public RazorLibraryCore(IDocument node) - // { - // this._node = node; - // } - - // public dynamic NodeById(int id) - // { - // var node = new DynamicNode(id); - // if (node != null && node.Id == 0) return new DynamicNull(); - // return node; - // } - // public dynamic NodeById(string id) - // { - // var node = new DynamicNode(id); - // if (node != null && node.Id == 0) return new DynamicNull(); - // return node; - // } - // public dynamic NodeById(DynamicNull Id) - // { - // return new DynamicNull(); - // } - // public dynamic NodeById(object Id) - // { - // if (Id.GetType() == typeof(DynamicNull)) - // { - // return new DynamicNull(); - // } - // var node = new DynamicNode(Id); - // if (node != null && node.Id == 0) return new DynamicNull(); - // return node; - // } - // public dynamic NodesById(List Ids) - // { - // List nodes = new List(); - // foreach (object eachId in Ids) - // nodes.Add(new DynamicNode(eachId)); - // return new DynamicNodeList(nodes); - // } - // public dynamic NodesById(List Ids) - // { - // List nodes = new List(); - // foreach (int eachId in Ids) - // nodes.Add(new DynamicNode(eachId)); - // return new DynamicNodeList(nodes); - // } - // public dynamic NodesById(List Ids, DynamicBackingItemType ItemType) - // { - // List nodes = new List(); - // foreach (int eachId in Ids) - // nodes.Add(new DynamicNode(eachId, ItemType)); - // return new DynamicNodeList(nodes); - // } - // public dynamic NodesById(params object[] Ids) - // { - // return NodesById(Ids.ToList()); - // } - // public dynamic MediaById(DynamicNull Id) - // { - // return new DynamicNull(); - // } - // public dynamic MediaById(int Id) - // { - // var ebm = ExamineBackedMedia.GetUmbracoMedia(Id); - // if (ebm != null && ebm.Id == 0) - // { - // return new DynamicNull(); - // } - // return new DynamicNode(new DynamicBackingItem(ebm)); - // } - // public dynamic MediaById(string Id) - // { - // int mediaId = 0; - // if (int.TryParse(Id, out mediaId)) - // { - // return MediaById(mediaId); - // } - // return new DynamicNull(); - // } - // public dynamic MediaById(object Id) - // { - // if (Id.GetType() == typeof(DynamicNull)) - // { - // return new DynamicNull(); - // } - // int mediaId = 0; - // if (int.TryParse(string.Format("{0}", Id), out mediaId)) - // { - // return MediaById(mediaId); - // } - // return null; - // } - // public dynamic MediaById(List Ids) - // { - // List nodes = new List(); - // foreach (object eachId in Ids) - // nodes.Add(MediaById(eachId)); - // return new DynamicNodeList(nodes); - // } - // public dynamic MediaById(List Ids) - // { - // List nodes = new List(); - // foreach (int eachId in Ids) - // nodes.Add(MediaById(eachId)); - // return new DynamicNodeList(nodes); - // } - // public dynamic MediaById(params object[] Ids) - // { - // return MediaById(Ids.ToList()); - // } - - - // //public dynamic Search(string term, bool useWildCards = true, string searchProvider = null) - // //{ - // // var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; - // // if (!string.IsNullOrEmpty(searchProvider)) - // // searcher = Examine.ExamineManager.Instance.SearchProviderCollection[searchProvider]; - - // // var results = searcher.Search(term, useWildCards); - // // return ExamineSearchUtill.convertSearchResultToDynamicNode(results); - // //} - - // //public dynamic Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) - // //{ - // // var s = Examine.ExamineManager.Instance.DefaultSearchProvider; - // // if (searchProvider != null) - // // s = searchProvider; - - // // var results = s.Search(criteria); - // // return ExamineSearchUtill.convertSearchResultToDynamicNode(results); - // //} - - - // public T As() where T : class - // { - // return (this as T); - // } - - // public dynamic ToDynamicXml(string xml) - // { - // if (string.IsNullOrWhiteSpace(xml)) return null; - // var xElement = XElement.Parse(xml); - // return new DynamicXml(xElement); - // } - // public dynamic ToDynamicXml(XElement xElement) - // { - // return new DynamicXml(xElement); - // } - // public dynamic ToDynamicXml(XPathNodeIterator xpni) - // { - // return new DynamicXml(xpni); - // } - // public string Coalesce(params object[] args) - // { - // foreach (var arg in args) - // { - // if (arg != null && arg.GetType() != typeof(DynamicNull)) - // { - // var sArg = string.Format("{0}", arg); - // if (!string.IsNullOrWhiteSpace(sArg)) - // { - // return sArg; - // } - // } - // } - // return string.Empty; - // } - - // public string Concatenate(params object[] args) - // { - // StringBuilder result = new StringBuilder(); - // foreach (var arg in args) - // { - // if (arg != null && arg.GetType() != typeof(DynamicNull)) - // { - // var sArg = string.Format("{0}", arg); - // if (!string.IsNullOrWhiteSpace(sArg)) - // { - // result.Append(sArg); - // } - // } - // } - // return result.ToString(); - // } - // public string Join(string seperator, params object[] args) - // { - // List results = new List(); - // foreach (var arg in args) - // { - // if (arg != null && arg.GetType() != typeof(DynamicNull)) - // { - // var sArg = string.Format("{0}", arg); - // if (!string.IsNullOrWhiteSpace(sArg)) - // { - // results.Add(sArg); - // } - // } - // } - // return string.Join(seperator, results); - // } - - // public HtmlString If(bool test, string valueIfTrue, string valueIfFalse) - // { - // return test ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - // } - // public HtmlString If(bool test, string valueIfTrue) - // { - // return test ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - // } - - // public HtmlTagWrapper Wrap(string tag, string innerText, params IHtmlTagWrapper[] Children) - // { - // var item = Wrap(tag, innerText, (object)null); - // foreach (var child in Children) - // { - // item.AddChild(child); - // } - // return item; - // } - // public HtmlTagWrapper Wrap(string tag, string innerText) - // { - // return Wrap(tag, innerText, (object)null); - // } - // public HtmlTagWrapper Wrap(string tag, object inner, object anonymousAttributes) - // { - // string innerText = null; - // if (inner != null && inner.GetType() != typeof(DynamicNull)) - // { - // innerText = string.Format("{0}", inner); - // } - // return Wrap(tag, innerText, anonymousAttributes); - // } - - // public HtmlTagWrapper Wrap(string tag, object inner, object anonymousAttributes, params IHtmlTagWrapper[] Children) - // { - // string innerText = null; - // if (inner != null && inner.GetType() != typeof(DynamicNull)) - // { - // innerText = string.Format("{0}", inner); - // } - // var item = Wrap(tag, innerText, anonymousAttributes); - // foreach (var child in Children) - // { - // item.AddChild(child); - // } - // return item; - // } - // public HtmlTagWrapper Wrap(string tag, object inner) - // { - // string innerText = null; - // if (inner != null && inner.GetType() != typeof(DynamicNull)) - // { - // innerText = string.Format("{0}", inner); - // } - // return Wrap(tag, innerText, null); - // } - // public HtmlTagWrapper Wrap(string tag, string innerText, object anonymousAttributes) - // { - // HtmlTagWrapper wrap = new HtmlTagWrapper(tag); - // if (anonymousAttributes != null) - // { - // wrap.ReflectAttributesFromAnonymousType(anonymousAttributes); - // } - // if (!string.IsNullOrWhiteSpace(innerText)) - // { - // wrap.Children.Add(new HtmlTagWrapperTextNode(innerText)); - // } - // return wrap; - // } - // public HtmlTagWrapper Wrap(string tag, string innerText, object anonymousAttributes, params IHtmlTagWrapper[] Children) - // { - // HtmlTagWrapper wrap = new HtmlTagWrapper(tag); - // if (anonymousAttributes != null) - // { - // wrap.ReflectAttributesFromAnonymousType(anonymousAttributes); - // } - // if (!string.IsNullOrWhiteSpace(innerText)) - // { - // wrap.Children.Add(new HtmlTagWrapperTextNode(innerText)); - // } - // foreach (var child in Children) - // { - // wrap.AddChild(child); - // } - // return wrap; - // } - - // public HtmlTagWrapper Wrap(bool visible, string tag, string innerText, object anonymousAttributes) - // { - // var item = Wrap(tag, innerText, anonymousAttributes); - // item.Visible = visible; - // return item; - // } - // public HtmlTagWrapper Wrap(bool visible, string tag, string innerText, object anonymousAttributes, params IHtmlTagWrapper[] Children) - // { - // var item = Wrap(tag, innerText, anonymousAttributes, Children); - // item.Visible = visible; - // foreach (var child in Children) - // { - // item.AddChild(child); - // } - // return item; - // } - // public IHtmlString Truncate(IHtmlString html, int length) - // { - // return Truncate(html.ToHtmlString(), length, true, false); - // } - // public IHtmlString Truncate(IHtmlString html, int length, bool addElipsis) - // { - // return Truncate(html.ToHtmlString(), length, addElipsis, false); - // } - // public IHtmlString Truncate(IHtmlString html, int length, bool addElipsis, bool treatTagsAsContent) - // { - // return Truncate(html.ToHtmlString(), length, addElipsis, treatTagsAsContent); - // } - // public IHtmlString Truncate(DynamicNull html, int length) - // { - // return new HtmlString(string.Empty); - // } - // public IHtmlString Truncate(DynamicNull html, int length, bool addElipsis) - // { - // return new HtmlString(string.Empty); - // } - // public IHtmlString Truncate(DynamicNull html, int length, bool addElipsis, bool treatTagsAsContent) - // { - // return new HtmlString(string.Empty); - // } - // public IHtmlString Truncate(string html, int length) - // { - // return Truncate(html, length, true, false); - // } - // public IHtmlString Truncate(string html, int length, bool addElipsis) - // { - // return Truncate(html, length, addElipsis, false); - // } - // public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) - // { - // using (MemoryStream outputms = new MemoryStream()) - // { - // using (TextWriter outputtw = new StreamWriter(outputms)) - // { - // using (MemoryStream ms = new MemoryStream()) - // { - // using (TextWriter tw = new StreamWriter(ms)) - // { - // tw.Write(html); - // tw.Flush(); - // ms.Position = 0; - // Stack tagStack = new Stack(); - // using (TextReader tr = new StreamReader(ms)) - // { - // bool IsInsideElement = false; - // bool lengthReached = false; - // int ic = 0; - // int currentLength = 0, currentTextLength = 0; - // string currentTag = string.Empty; - // string tagContents = string.Empty; - // bool insideTagSpaceEncountered = false; - // bool isTagClose = false; - // while ((ic = tr.Read()) != -1) - // { - // bool write = true; - - // if (ic == (int)'<') - // { - // if (!lengthReached) - // { - // IsInsideElement = true; - // } - // insideTagSpaceEncountered = false; - // currentTag = string.Empty; - // tagContents = string.Empty; - // isTagClose = false; - // if (tr.Peek() == (int)'/') - // { - // isTagClose = true; - // } - // } - // else if (ic == (int)'>') - // { - // //if (IsInsideElement) - // //{ - // IsInsideElement = false; - // //if (write) - // //{ - // // outputtw.Write('>'); - // //} - // currentTextLength++; - // if (isTagClose && tagStack.Count > 0) - // { - // string thisTag = tagStack.Pop(); - // outputtw.Write(""); - // } - // if (!isTagClose && currentTag.Length > 0) - // { - // if (!lengthReached) - // { - // tagStack.Push(currentTag); - // outputtw.Write("<" + currentTag); - // if (tr.Peek() != (int)' ') - // { - // if (!string.IsNullOrEmpty(tagContents)) - // { - // if (tagContents.EndsWith("/")) - // { - // //short close - // tagStack.Pop(); - // } - // outputtw.Write(tagContents); - // } - // outputtw.Write(">"); - // } - // } - // } - // //} - // continue; - // } - // else - // { - // if (IsInsideElement) - // { - // if (ic == (int)' ') - // { - // if (!insideTagSpaceEncountered) - // { - // insideTagSpaceEncountered = true; - // //if (!isTagClose) - // //{ - // // tagStack.Push(currentTag); - // //} - // } - // } - // if (!insideTagSpaceEncountered) - // { - // currentTag += (char)ic; - // } - // } - // } - // if (IsInsideElement || insideTagSpaceEncountered) - // { - // write = false; - // if (insideTagSpaceEncountered) - // { - // tagContents += (char)ic; - // } - // } - // if (!IsInsideElement || treatTagsAsContent) - // { - // currentTextLength++; - // } - // currentLength++; - // if (currentTextLength <= length || (lengthReached && IsInsideElement)) - // { - // if (write) - // { - // outputtw.Write((char)ic); - // } - // } - // if (!lengthReached && currentTextLength >= length) - // { - // //reached truncate point - // if (addElipsis) - // { - // outputtw.Write("…"); - // } - // lengthReached = true; - // } - - // } - - // } - // } - // } - // outputtw.Flush(); - // outputms.Position = 0; - // using (TextReader outputtr = new StreamReader(outputms)) - // { - // return new HtmlString(outputtr.ReadToEnd().Replace(" ", " ").Trim()); - // } - // } - // } - // } - - - // public HtmlString StripHtml(IHtmlString html) - // { - // return StripHtml(html.ToHtmlString(), (List)null); - // } - // public HtmlString StripHtml(DynamicNull html) - // { - // return new HtmlString(string.Empty); - // } - // public HtmlString StripHtml(string html) - // { - // return StripHtmlTags(html, (List)null); - // } - - // public HtmlString StripHtml(IHtmlString html, List tags) - // { - // return StripHtml(html.ToHtmlString(), tags); - // } - // public HtmlString StripHtml(DynamicNull html, List tags) - // { - // return new HtmlString(string.Empty); - // } - // public HtmlString StripHtml(string html, List tags) - // { - // return StripHtmlTags(html, tags); - // } - - // public HtmlString StripHtml(IHtmlString html, params string[] tags) - // { - // return StripHtml(html.ToHtmlString(), tags.ToList()); - // } - // public HtmlString StripHtml(DynamicNull html, params string[] tags) - // { - // return new HtmlString(string.Empty); - // } - // public HtmlString StripHtml(string html, params string[] tags) - // { - // return StripHtmlTags(html, tags.ToList()); - // } - - // private HtmlString StripHtmlTags(string html, List tags) - // { - // HtmlDocument doc = new HtmlDocument(); - // doc.LoadHtml("

" + html + "

"); - // using (MemoryStream ms = new MemoryStream()) - // { - // List targets = new List(); - - // var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); - // if (nodes != null) - // { - // foreach (var node in nodes) - // { - // //is element - // if (node.NodeType == HtmlNodeType.Element) - // { - // bool filterAllTags = (tags == null || tags.Count == 0); - // if (filterAllTags || tags.Any(tag => string.Equals(tag, node.Name, StringComparison.CurrentCultureIgnoreCase))) - // { - // targets.Add(node); - // } - // } - // } - // foreach (var target in targets) - // { - // HtmlNode content = doc.CreateTextNode(target.InnerText); - // target.ParentNode.ReplaceChild(content, target); - // } - - // } - // else - // { - // return new HtmlString(html); - // } - // return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); - // } - // } - - - //} -} diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs new file mode 100644 index 0000000000..62f81ee91d --- /dev/null +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Umbraco.Core.Macros +{ + /// + /// Parses the macro syntax in a string and renders out it's contents + /// + internal class MacroTagParser + { + /// + /// This will accept a text block and seach/parse it for macro markup. + /// When either a text block or a a macro is found, it will call the callback method. + /// + /// + /// + /// + /// + /// + /// This method simply parses the macro contents, it does not create a string or result, + /// this is up to the developer calling this method to implement this with the callbacks. + /// + internal static void ParseMacros( + string text, + Action textFoundCallback, + Action> macroFoundCallback ) + { + if (textFoundCallback == null) throw new ArgumentNullException("textFoundCallback"); + if (macroFoundCallback == null) throw new ArgumentNullException("macroFoundCallback"); + + string elementText = text; + + var fieldResult = new StringBuilder(elementText); + + //NOTE: This is legacy code, this is definitely not the correct way to do a while loop! :) + var stop = false; + while (!stop) + { + var tagIndex = fieldResult.ToString().ToLower().IndexOf(" -1) + { + var tempElementContent = ""; + + //text block found, call the call back method + textFoundCallback(fieldResult.ToString().Substring(0, tagIndex)); + + fieldResult.Remove(0, tagIndex); + + var tag = fieldResult.ToString().Substring(0, fieldResult.ToString().IndexOf(">") + 1); + var attributes = XmlHelper.GetAttributesFromElement(tag); + + // Check whether it's a single tag () or a tag with children (...) + if (tag.Substring(tag.Length - 2, 1) != "/" && tag.IndexOf(" ") > -1) + { + String closingTag = ""; + // Tag with children are only used when a macro is inserted by the umbraco-editor, in the + // following format: "", so we + // need to delete extra information inserted which is the image-tag and the closing + // umbraco_macro tag + if (fieldResult.ToString().IndexOf(closingTag) > -1) + { + fieldResult.Remove(0, fieldResult.ToString().IndexOf(closingTag)); + } + } + + + var macroAlias = attributes["macroalias"] ?? attributes["alias"]; + + //call the callback now that we have the macro parsed + macroFoundCallback(macroAlias, attributes); + + fieldResult.Remove(0, fieldResult.ToString().IndexOf(">") + 1); + fieldResult.Insert(0, tempElementContent); + } + else + { + //text block found, call the call back method + textFoundCallback(fieldResult.ToString()); + + stop = true; //break; + } + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Macros/PersistableMacroProperty.cs b/src/Umbraco.Core/Macros/PersistableMacroProperty.cs index d8b4471ca2..db3f824e4b 100644 --- a/src/Umbraco.Core/Macros/PersistableMacroProperty.cs +++ b/src/Umbraco.Core/Macros/PersistableMacroProperty.cs @@ -1,8 +1,8 @@ using System; +using System.Collections; namespace Umbraco.Core.Macros { - /// /// NOTE: This is legacy code, might require a cleanup /// diff --git a/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs index 327d535ce3..5861f41c4d 100644 --- a/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs @@ -3,6 +3,9 @@ using System.Web; namespace Umbraco.Core.PropertyEditors { + /// + /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used. + /// internal class TinyMcePropertyEditorValueConverter : IPropertyEditorValueConverter { public bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias) @@ -15,7 +18,7 @@ namespace Umbraco.Core.PropertyEditors /// /// /// - public Attempt ConvertPropertyValue(object value) + public virtual Attempt ConvertPropertyValue(object value) { return new Attempt(true, new HtmlString(value.ToString())); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 65f78ea052..b1c8606d1e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -88,17 +88,15 @@ + - - - diff --git a/src/Umbraco.Core/XmlHelper.cs b/src/Umbraco.Core/XmlHelper.cs index b30e502672..10b2d8a09d 100644 --- a/src/Umbraco.Core/XmlHelper.cs +++ b/src/Umbraco.Core/XmlHelper.cs @@ -1,4 +1,8 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; using System.Xml; using Umbraco.Core.IO; @@ -164,5 +168,20 @@ namespace Umbraco.Core // return the XML node. return xml; } + + /// + /// Return a dictionary of attributes found for a string based tag + /// + /// + /// + public static Dictionary GetAttributesFromElement(string tag) + { + var m = + Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", + RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + // fix for issue 14862: return lowercase attributes for case insensitive matching + var d = m.Cast().ToDictionary(attributeSet => attributeSet.Groups["attributeName"].Value.ToString().ToLower(), attributeSet => attributeSet.Groups["attributeValue"].Value.ToString()); + return d; + } } } diff --git a/src/Umbraco.Web/PropertyEditors/RteMacroRenderingPropertyEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/RteMacroRenderingPropertyEditorValueConverter.cs new file mode 100644 index 0000000000..42a8b11bb6 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/RteMacroRenderingPropertyEditorValueConverter.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Web; +using Umbraco.Core; +using Umbraco.Core.Macros; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// A value converter for TinyMCE that will ensure any macro content is rendered properly even when + /// used dynamically. + /// + internal class RteMacroRenderingPropertyEditorValueConverter : TinyMcePropertyEditorValueConverter + { + + /// + /// Return IHtmlString so devs doesn't need to decode html + /// + /// + /// + public override Attempt ConvertPropertyValue(object value) + { + //TODO: we need to make a few new classes, like a new MacroParser, etc... and move a bunch of the logic + // out of 'Item' that does all of this but it will be tricky because of the way that it creates a 'Macro' class + // we may have to do some server Execute trickyness. + // Then, we need to add this to the resolver and remove the base class and make it abstract. + + var sb = new StringBuilder(); + var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + MacroTagParser.ParseMacros( + value.ToString(), + //callback for when text block is found + textBlock => sb.Append(textBlock), + //callback for when macro syntax is found + (macroAlias, macroAttributes) => sb.Append(umbracoHelper.RenderMacro( + macroAlias, + //needs to be explicitly casted to Dictionary + macroAttributes.ConvertTo(x => (string)x, x => (object)x)).ToString())); + + return new Attempt(true, new HtmlString(sb.ToString())); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 6bf708e859..b8cddbfc82 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -257,6 +257,7 @@ + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 4c1f94d05b..ec632f1d45 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -22,6 +22,7 @@ namespace Umbraco.Web internal UmbracoHelper(UmbracoContext umbracoContext) { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); _umbracoContext = umbracoContext; _currentPage = _umbracoContext.DocumentRequest.Document; } @@ -46,6 +47,17 @@ namespace Umbraco.Web /// The parameters. /// public IHtmlString RenderMacro(string alias, object parameters) + { + return RenderMacro(alias, parameters.ToDictionary()); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + public IHtmlString RenderMacro(string alias, IDictionary parameters) { if (alias == null) throw new ArgumentNullException("alias"); var containerPage = new FormlessPage(); @@ -59,20 +71,20 @@ namespace Umbraco.Web throw new InvalidOperationException("Cannot render a macro when there is no current DocumentRequest."); } var macroProps = new Hashtable(); - foreach(var i in parameters.ToDictionary()) + foreach (var i in parameters) { //TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method of macro.cs // looks for a lower case match. WTF. the whole macro concept needs to be rewritten. macroProps.Add(i.Key.ToLower(), i.Value); - } - var macroControl = m.renderMacro(macroProps, - UmbracoContext.Current.DocumentRequest.UmbracoPage.Elements, + } + var macroControl = m.renderMacro(macroProps, + UmbracoContext.Current.DocumentRequest.UmbracoPage.Elements, _umbracoContext.PageId.Value); containerPage.Controls.Add(macroControl); using (var output = new StringWriter()) { _umbracoContext.HttpContext.Server.Execute(containerPage, output, false); - return new HtmlString(output.ToString()); + return new HtmlString(output.ToString()); } } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 322ab952b4..a398b189c3 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -5,9 +5,11 @@ using System.Web.Routing; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; +using Umbraco.Core.PropertyEditors; using Umbraco.Web.Dictionary; using Umbraco.Web.Media.ThumbnailProviders; using Umbraco.Web.Mvc; +using Umbraco.Web.PropertyEditors; using Umbraco.Web.Routing; using umbraco.businesslogic; @@ -126,6 +128,11 @@ namespace Umbraco.Web { base.InitializeResolvers(); + //the base creates the PropertyEditorValueConvertersResolver but we want to modify it in the web app and replace + //the TinyMcePropertyEditorValueConverter with the RteMacroRenderingPropertyEditorValueConverter + PropertyEditorValueConvertersResolver.Current.RemoveType(); + PropertyEditorValueConvertersResolver.Current.AddType(); + ContentStoreResolver.Current = new ContentStoreResolver(new XmlPublishedContentStore()); FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver( diff --git a/src/Umbraco.Web/umbraco.presentation/helper.cs b/src/Umbraco.Web/umbraco.presentation/helper.cs index 80b636328c..869169ccd2 100644 --- a/src/Umbraco.Web/umbraco.presentation/helper.cs +++ b/src/Umbraco.Web/umbraco.presentation/helper.cs @@ -36,18 +36,16 @@ namespace umbraco return String.Empty; } + [Obsolete("Has been superceded by Umbraco.Core.XmlHelper.GetAttributesFromElement")] public static Hashtable ReturnAttributes(String tag) - { - Hashtable ht = new Hashtable(); - MatchCollection m = - Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - // fix for issue 14862: return lowercase attributes for case insensitive matching - foreach (Match attributeSet in m) - ht.Add(attributeSet.Groups["attributeName"].Value.ToString().ToLower(), attributeSet.Groups["attributeValue"].Value.ToString()); - - return ht; - } + { + var h = new Hashtable(); + foreach(var i in Umbraco.Core.XmlHelper.GetAttributesFromElement(tag)) + { + h.Add(i.Key, i.Value); + } + return h; + } public static String FindAttribute(IDictionary attributes, String key) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index 9ae03d6a8e..ae006a537f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -2,10 +2,12 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Web; using System.Web.UI; using System.Xml; +using Umbraco.Core.Macros; using Umbraco.Web.umbraco.templateControls; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.property; @@ -14,309 +16,277 @@ using umbraco.IO; namespace umbraco.presentation.templateControls { - public class ItemRenderer - { - public readonly static ItemRenderer Instance = new ItemRenderer(); - /// - /// Initializes a new instance of the class. - /// - protected ItemRenderer() - { } + public class ItemRenderer + { + public readonly static ItemRenderer Instance = new ItemRenderer(); + /// + /// Initializes a new instance of the class. + /// + protected ItemRenderer() + { } - /// - /// Renders the specified item. - /// - /// The item. - /// The writer. - public virtual void Render(Item item, HtmlTextWriter writer) - { - if (item.DebugMode) - { - writer.AddAttribute(HtmlTextWriterAttribute.Title, string.Format("Field Tag: '{0}'", item.Field)); - writer.AddAttribute("style", "border: 1px solid #fc6;"); - writer.RenderBeginTag(HtmlTextWriterTag.Div); - } + /// + /// Renders the specified item. + /// + /// The item. + /// The writer. + public virtual void Render(Item item, HtmlTextWriter writer) + { + if (item.DebugMode) + { + writer.AddAttribute(HtmlTextWriterAttribute.Title, string.Format("Field Tag: '{0}'", item.Field)); + writer.AddAttribute("style", "border: 1px solid #fc6;"); + writer.RenderBeginTag(HtmlTextWriterTag.Div); + } - try - { - StringWriter renderOutputWriter = new StringWriter(); - HtmlTextWriter htmlWriter = new HtmlTextWriter(renderOutputWriter); - foreach (Control control in item.Controls) - { - try - { - control.RenderControl(htmlWriter); - } - catch (Exception renderException) - { - // TODO: Validate that the current control is within the scope of a form control - // Even controls that are inside this scope, can produce this error in async postback. - HttpContext.Current.Trace.Warn("ItemRenderer", - String.Format("Error rendering control {0} of {1}.", control.ClientID, item), renderException); - } - } + try + { + StringWriter renderOutputWriter = new StringWriter(); + HtmlTextWriter htmlWriter = new HtmlTextWriter(renderOutputWriter); + foreach (Control control in item.Controls) + { + try + { + control.RenderControl(htmlWriter); + } + catch (Exception renderException) + { + // TODO: Validate that the current control is within the scope of a form control + // Even controls that are inside this scope, can produce this error in async postback. + HttpContext.Current.Trace.Warn("ItemRenderer", + String.Format("Error rendering control {0} of {1}.", control.ClientID, item), renderException); + } + } - // parse macros and execute the XSLT transformation on the result if not empty - string renderOutput = renderOutputWriter.ToString(); - string xsltTransformedOutput = renderOutput.Trim().Length == 0 - ? String.Empty - : XsltTransform(item.Xslt, renderOutput, item.XsltDisableEscaping); - // handle text before/after - xsltTransformedOutput = AddBeforeAfterText(xsltTransformedOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); - string finalResult = xsltTransformedOutput.Trim().Length > 0 ? xsltTransformedOutput : GetEmptyText(item); - writer.Write(IOHelper.ResolveUrlsFromTextString(finalResult)); - } - catch (Exception renderException) - { - HttpContext.Current.Trace.Warn("ItemRenderer", String.Format("Error rendering {0}.", item), renderException); - } - finally - { - if (item.DebugMode) - { - writer.RenderEndTag(); - } - } - } + // parse macros and execute the XSLT transformation on the result if not empty + string renderOutput = renderOutputWriter.ToString(); + string xsltTransformedOutput = renderOutput.Trim().Length == 0 + ? String.Empty + : XsltTransform(item.Xslt, renderOutput, item.XsltDisableEscaping); + // handle text before/after + xsltTransformedOutput = AddBeforeAfterText(xsltTransformedOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); + string finalResult = xsltTransformedOutput.Trim().Length > 0 ? xsltTransformedOutput : GetEmptyText(item); + writer.Write(IOHelper.ResolveUrlsFromTextString(finalResult)); + } + catch (Exception renderException) + { + HttpContext.Current.Trace.Warn("ItemRenderer", String.Format("Error rendering {0}.", item), renderException); + } + finally + { + if (item.DebugMode) + { + writer.RenderEndTag(); + } + } + } - /// - /// Renders the field contents. - /// Checks via the NodeId attribute whether to fetch data from another page than the current one. - /// - /// A string of field contents (macros not parsed) - protected virtual string GetFieldContents(Item item) - { - string tempElementContent = String.Empty; + /// + /// Renders the field contents. + /// Checks via the NodeId attribute whether to fetch data from another page than the current one. + /// + /// A string of field contents (macros not parsed) + protected virtual string GetFieldContents(Item item) + { + string tempElementContent = String.Empty; - // if a nodeId is specified we should get the data from another page than the current one - if (!String.IsNullOrEmpty(item.NodeId)) - { - int? tempNodeId = item.GetParsedNodeId(); - if (tempNodeId != null && tempNodeId.Value != 0) - { - string currentField = helper.FindAttribute(item.LegacyAttributes, "field"); - // check for a cached instance of the content - object contents = GetContentFromCache(tempNodeId.Value, currentField); - if (contents != null) - tempElementContent = (string)contents; - else - { - // as the field can be used for both documents, media and even members we'll use the - // content class to lookup field items - try - { - tempElementContent = GetContentFromDatabase(item.Attributes, tempNodeId.Value, currentField); - } - catch - { - // content was not found in property fields, - // so the last place to look for is page fields - page itemPage = new page(content.Instance.XmlContent.GetElementById(tempNodeId.ToString())); - tempElementContent = new item(itemPage.Elements, item.LegacyAttributes).FieldContent; - } - } - } + // if a nodeId is specified we should get the data from another page than the current one + if (!String.IsNullOrEmpty(item.NodeId)) + { + int? tempNodeId = item.GetParsedNodeId(); + if (tempNodeId != null && tempNodeId.Value != 0) + { + string currentField = helper.FindAttribute(item.LegacyAttributes, "field"); + // check for a cached instance of the content + object contents = GetContentFromCache(tempNodeId.Value, currentField); + if (contents != null) + tempElementContent = (string)contents; + else + { + // as the field can be used for both documents, media and even members we'll use the + // content class to lookup field items + try + { + tempElementContent = GetContentFromDatabase(item.Attributes, tempNodeId.Value, currentField); + } + catch + { + // content was not found in property fields, + // so the last place to look for is page fields + page itemPage = new page(content.Instance.XmlContent.GetElementById(tempNodeId.ToString())); + tempElementContent = new item(itemPage.Elements, item.LegacyAttributes).FieldContent; + } + } + } - } - else - { - // gets the field content from the current page (via the PageElements collection) - tempElementContent = new item(item.PageElements, item.LegacyAttributes).FieldContent; - } + } + else + { + // gets the field content from the current page (via the PageElements collection) + tempElementContent = new item(item.PageElements, item.LegacyAttributes).FieldContent; + } - return tempElementContent; - } + return tempElementContent; + } - /// - /// Inits the specified item. To be called from the OnInit method of Item. - /// - /// The item. - public virtual void Init(Item item) - { - ParseMacros(item); - } + /// + /// Inits the specified item. To be called from the OnInit method of Item. + /// + /// The item. + public virtual void Init(Item item) + { + ParseMacros(item); + } - /// - /// Loads the specified item. To be called from the OnLoad method of Item. - /// - /// The item. - public virtual void Load(Item item) - { } + /// + /// Loads the specified item. To be called from the OnLoad method of Item. + /// + /// The item. + public virtual void Load(Item item) + { } - /// - /// Parses the macros inside the text, by creating child elements for each item. - /// - /// The item. - protected virtual void ParseMacros(Item item) - { - // do nothing if the macros have already been rendered - if (item.Controls.Count > 0) - return; + /// + /// Parses the macros inside the text, by creating child elements for each item. + /// + /// The item. + protected virtual void ParseMacros(Item item) + { + // do nothing if the macros have already been rendered + if (item.Controls.Count > 0) + return; - string elementText = GetFieldContents(item); + string elementText = GetFieldContents(item); - StringBuilder output = new StringBuilder(); - StringBuilder fieldResult = new StringBuilder(elementText); + MacroTagParser.ParseMacros( + elementText, - bool stop = false; - while (!stop) - { - int tagIndex = fieldResult.ToString().ToLower().IndexOf(" -1) - { - String tempElementContent = ""; - item.Controls.Add(new LiteralControl(fieldResult.ToString().Substring(0, tagIndex))); + //callback for when a text block is parsed + textBlock => item.Controls.Add(new LiteralControl(textBlock)), - fieldResult.Remove(0, tagIndex); + //callback for when a macro is parsed: + (macroAlias, attributes) => + { + var macroControl = new Macro + { + Alias = macroAlias + }; + foreach (var i in attributes.Where(i => macroControl.Attributes[i.Key] == null)) + { + macroControl.Attributes.Add(i.Key, i.Value); + } + item.Controls.Add(macroControl); + }); + } - String tag = fieldResult.ToString().Substring(0, fieldResult.ToString().IndexOf(">") + 1); - Hashtable attributes = helper.ReturnAttributes(tag); + /// + /// Transforms the content using the XSLT attribute, if provided. + /// + /// The xpath expression. + /// The item's rendered content. + /// if set to true, escaping is disabled. + /// The transformed content if the XSLT attribute is present, otherwise the original content. + protected virtual string XsltTransform(string xpath, string itemData, bool disableEscaping) + { + if (!String.IsNullOrEmpty(xpath)) + { + // XML-encode the expression and add the itemData parameter to it + string xpathEscaped = xpath.Replace("<", "<").Replace(">", ">").Replace("\"", """); + string xpathExpression = string.Format(xpathEscaped, "$itemData"); - // Check whether it's a single tag () or a tag with children (...) - if (tag.Substring(tag.Length - 2, 1) != "/" && tag.IndexOf(" ") > -1) - { - String closingTag = ""; - // Tag with children are only used when a macro is inserted by the umbraco-editor, in the - // following format: "", so we - // need to delete extra information inserted which is the image-tag and the closing - // umbraco_macro tag - if (fieldResult.ToString().IndexOf(closingTag) > -1) - { - fieldResult.Remove(0, fieldResult.ToString().IndexOf(closingTag)); - } - } + // prepare support for XSLT extensions + StringBuilder namespaceList = new StringBuilder(); + StringBuilder namespaceDeclaractions = new StringBuilder(); + foreach (KeyValuePair extension in macro.GetXsltExtensions()) + { + namespaceList.Append(extension.Key).Append(' '); + namespaceDeclaractions.AppendFormat("xmlns:{0}=\"urn:{0}\" ", extension.Key); + } - Macro macroControl = new Macro(); - macroControl.Alias = helper.FindAttribute(attributes, "macroalias"); - if (macroControl.Alias.Length == 0) - macroControl.Alias = helper.FindAttribute(attributes, "alias"); - IDictionaryEnumerator ide = attributes.GetEnumerator(); - while (ide.MoveNext()) - if (macroControl.Attributes[ide.Key.ToString()] == null) - macroControl.Attributes.Add(ide.Key.ToString(), ide.Value.ToString()); - item.Controls.Add(macroControl); - fieldResult.Remove(0, fieldResult.ToString().IndexOf(">") + 1); - fieldResult.Insert(0, tempElementContent); - } - else - { - item.Controls.Add(new LiteralControl(fieldResult.ToString())); - break; - } - } - } + // add the XSLT expression into the full XSLT document, together with the needed parameters + string xslt = string.Format(Resources.InlineXslt, xpathExpression, disableEscaping ? "yes" : "no", + namespaceList, namespaceDeclaractions); - /// - /// Transforms the content using the XSLT attribute, if provided. - /// - /// The xpath expression. - /// The item's rendered content. - /// if set to true, escaping is disabled. - /// The transformed content if the XSLT attribute is present, otherwise the original content. - protected virtual string XsltTransform(string xpath, string itemData, bool disableEscaping) - { - if (!String.IsNullOrEmpty(xpath)) - { - // XML-encode the expression and add the itemData parameter to it - string xpathEscaped = xpath.Replace("<", "<").Replace(">", ">").Replace("\"", """); - string xpathExpression = string.Format(xpathEscaped, "$itemData"); + // create the parameter + Dictionary parameters = new Dictionary(1); + parameters.Add("itemData", itemData); - // prepare support for XSLT extensions - StringBuilder namespaceList = new StringBuilder(); - StringBuilder namespaceDeclaractions = new StringBuilder(); - foreach (KeyValuePair extension in macro.GetXsltExtensions()) - { - namespaceList.Append(extension.Key).Append(' '); - namespaceDeclaractions.AppendFormat("xmlns:{0}=\"urn:{0}\" ", extension.Key); - } + // apply the XSLT transformation + XmlTextReader xslReader = new XmlTextReader(new StringReader(xslt)); + System.Xml.Xsl.XslCompiledTransform xsl = macro.CreateXsltTransform(xslReader, false); + itemData = macro.GetXsltTransformResult(new XmlDocument(), xsl, parameters); + xslReader.Close(); + } + return itemData; + } - // add the XSLT expression into the full XSLT document, together with the needed parameters - string xslt = string.Format(Resources.InlineXslt, xpathExpression, disableEscaping ? "yes" : "no", - namespaceList, namespaceDeclaractions); + protected string AddBeforeAfterText(string text, string before, string after) + { + if (!String.IsNullOrEmpty(text)) + { + if (!String.IsNullOrEmpty(before)) + text = String.Format("{0}{1}", HttpContext.Current.Server.HtmlDecode(before), text); + if (!String.IsNullOrEmpty(after)) + text = String.Format("{0}{1}", text, HttpContext.Current.Server.HtmlDecode(after)); + } - // create the parameter - Dictionary parameters = new Dictionary(1); - parameters.Add("itemData", itemData); + return text; + } - // apply the XSLT transformation - XmlTextReader xslReader = new XmlTextReader(new StringReader(xslt)); - System.Xml.Xsl.XslCompiledTransform xsl = macro.CreateXsltTransform(xslReader, false); - itemData = macro.GetXsltTransformResult(new XmlDocument(), xsl, parameters); - xslReader.Close(); - } - return itemData; - } + /// + /// Gets the text to display if the field contents are empty. + /// + /// The item. + /// The text to display. + protected virtual string GetEmptyText(Item item) + { + return item.TextIfEmpty; + } - protected string AddBeforeAfterText(string text, string before, string after) - { - if (!String.IsNullOrEmpty(text)) - { - if (!String.IsNullOrEmpty(before)) - text = String.Format("{0}{1}", HttpContext.Current.Server.HtmlDecode(before), text); - if (!String.IsNullOrEmpty(after)) - text = String.Format("{0}{1}", text, HttpContext.Current.Server.HtmlDecode(after)); - } + /// + /// Gets the field content from database instead of the published XML via the APIs. + /// + /// The node id. + /// The field that should be fetched. + /// The contents of the from the content object + protected virtual string GetContentFromDatabase(AttributeCollection itemAttributes, int nodeIdInt, string currentField) + { + Content c = new Content(nodeIdInt); - return text; - } + Property property = c.getProperty(currentField); + if (property == null) + throw new ArgumentException(String.Format("Could not find property {0} of node {1}.", currentField, nodeIdInt)); - /// - /// Gets the text to display if the field contents are empty. - /// - /// The item. - /// The text to display. - protected virtual string GetEmptyText(Item item) - { - return item.TextIfEmpty; - } + item umbItem = new item(property.Value.ToString(), new AttributeCollectionAdapter(itemAttributes)); + string tempElementContent = umbItem.FieldContent; - /// - /// Gets the field content from database instead of the published XML via the APIs. - /// - /// The node id. - /// The field that should be fetched. - /// The contents of the from the content object - protected virtual string GetContentFromDatabase(AttributeCollection itemAttributes, int nodeIdInt, string currentField) - { - Content c = new Content(nodeIdInt); + // If the current content object is a document object, we'll only output it if it's published + if (c.nodeObjectType == cms.businesslogic.web.Document._objectType) + { + try + { + Document d = (Document)c; + if (!d.Published) + tempElementContent = ""; + } + catch { } + } - Property property = c.getProperty(currentField); - if (property == null) - throw new ArgumentException(String.Format("Could not find property {0} of node {1}.", currentField, nodeIdInt)); + // Add the content to the cache + if (!String.IsNullOrEmpty(tempElementContent)) + HttpContext.Current.Cache.Insert(String.Format("contentItem{0}_{1}", nodeIdInt.ToString(), currentField), tempElementContent); + return tempElementContent; + } - item umbItem = new item(property.Value.ToString(), new AttributeCollectionAdapter(itemAttributes)); - string tempElementContent = umbItem.FieldContent; - - // If the current content object is a document object, we'll only output it if it's published - if (c.nodeObjectType == cms.businesslogic.web.Document._objectType) - { - try - { - Document d = (Document)c; - if (!d.Published) - tempElementContent = ""; - } - catch { } - } - - // Add the content to the cache - if (!String.IsNullOrEmpty(tempElementContent)) - HttpContext.Current.Cache.Insert(String.Format("contentItem{0}_{1}", nodeIdInt.ToString(), currentField), tempElementContent); - return tempElementContent; - } - - /// - /// Gets the content from cache. - /// - /// The node id. - /// The field. - /// The cached contents of the from the content object - protected virtual object GetContentFromCache(int nodeIdInt, string field) - { - object content = HttpContext.Current.Cache[String.Format("contentItem{0}_{1}", nodeIdInt.ToString(), field.ToString())]; - return content; - } - } + /// + /// Gets the content from cache. + /// + /// The node id. + /// The field. + /// The cached contents of the from the content object + protected virtual object GetContentFromCache(int nodeIdInt, string field) + { + object content = HttpContext.Current.Cache[String.Format("contentItem{0}_{1}", nodeIdInt.ToString(), field.ToString())]; + return content; + } + } } diff --git a/src/umbraco.cms/helpers/xhtml.cs b/src/umbraco.cms/helpers/xhtml.cs index 51eb0fde31..3700b7f130 100644 --- a/src/umbraco.cms/helpers/xhtml.cs +++ b/src/umbraco.cms/helpers/xhtml.cs @@ -127,17 +127,16 @@ namespace umbraco.cms.helpers return closeBracket+tag; } - public static Hashtable ReturnAttributes(String tag) { - Hashtable ht = new Hashtable(); - MatchCollection m = - Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - // fix for issue 14862: return lowercase attributes for case insensitive matching - foreach (Match attributeSet in m) - ht.Add(attributeSet.Groups["attributeName"].Value.ToString().ToLower(), attributeSet.Groups["attributeValue"].Value.ToString()); - - return ht; - } + [Obsolete("Has been superceded by Umbraco.Core.XmlHelper.GetAttributesFromElement")] + public static Hashtable ReturnAttributes(String tag) + { + var h = new Hashtable(); + foreach (var i in Umbraco.Core.XmlHelper.GetAttributesFromElement(tag)) + { + h.Add(i.Key, i.Value); + } + return h; + } private static string returnLowerCaseAttributes(String tag) { diff --git a/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs b/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs index f1145acbcc..232caab2ed 100644 --- a/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs +++ b/src/umbraco.editorControls/tinyMCE3/TinyMCE.cs @@ -534,18 +534,16 @@ namespace umbraco.editorControls.tinyMCE3 return macroTag; } - public static Hashtable ReturnAttributes(String tag) - { - var ht = new Hashtable(); - MatchCollection m = - Regex.Matches(tag, "(?\\S*)=\"(?[^\"]*)\"", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match attributeSet in m) - ht.Add(attributeSet.Groups["attributeName"].Value, - attributeSet.Groups["attributeValue"].Value); - - return ht; - } + [Obsolete("Has been superceded by Umbraco.Core.XmlHelper.GetAttributesFromElement")] + public static Hashtable ReturnAttributes(String tag) + { + var h = new Hashtable(); + foreach (var i in Umbraco.Core.XmlHelper.GetAttributesFromElement(tag)) + { + h.Add(i.Key, i.Value); + } + return h; + } private int findStartTag(string text) {