diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index 013f54c5fc..85861a1496 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -112,10 +112,6 @@ namespace Umbraco.Web.Macros } } - // this is legacy and I'm not sure what exactly it is supposed to do - if (macroContent.Control != null) - macroContent.Control.ID = macroContent.ControlId; - return macroContent; } @@ -137,10 +133,6 @@ namespace Umbraco.Web.Macros if (key == null) return; } - // this is legacy and I'm not sure what exactly it is supposed to do - if (macroContent.Control != null) - macroContent.ControlId = macroContent.Control.ID; - // remember when we cache the content macroContent.Date = DateTime.Now; @@ -155,23 +147,12 @@ namespace Umbraco.Web.Macros } // gets the macro source file name - // null if the macro is not file-based + // null if the macro is not file-based, or not supported internal static string GetMacroFileName(MacroModel model) { - string filename; + string filename = model.MacroSource; // partial views are saved with their full virtual path - switch (model.MacroType) - { - case MacroTypes.PartialView: - filename = model.MacroSource; // partial views are saved with their full virtual path - break; - default: - // not file-based, or not supported - filename = null; - break; - } - - return filename; + return string.IsNullOrEmpty(filename) ? null : filename; } // gets the macro source file @@ -210,24 +191,22 @@ namespace Umbraco.Web.Macros if (m == null) throw new InvalidOperationException("No macro found by alias " + macroAlias); - var page = new PublishedContentHashtableConverter(content, _userService); - var macro = new MacroModel(m); UpdateMacroModelProperties(macro, macroParams); - return Render(macro, content, page.Elements); + return Render(macro, content); } - private MacroContent Render(MacroModel macro, IPublishedContent content, IDictionary pageElements) + private MacroContent Render(MacroModel macro, IPublishedContent content) { if (content == null) throw new ArgumentNullException(nameof(content)); - var macroInfo = $"Render Macro: {macro.Name}, type: {macro.MacroType}, cache: {macro.CacheDuration}"; + var macroInfo = $"Render Macro: {macro.Name}, cache: {macro.CacheDuration}"; using (_plogger.DebugDuration(macroInfo, "Rendered Macro.")) { // parse macro parameters ie replace the special [#key], [$key], etc. syntaxes foreach (var prop in macro.Properties) - prop.Value = ParseAttribute(pageElements, prop.Value); + prop.Value = ParseAttribute(prop.Value); macro.CacheIdentifier = GetContentCacheIdentifier(macro, content.Id); @@ -334,22 +313,11 @@ namespace Umbraco.Web.Macros var textService = _textService; - switch (model.MacroType) - { - case MacroTypes.PartialView: - return ExecuteMacroWithErrorWrapper(model, - $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", - "Executed PartialView.", - () => ExecutePartialView(model, content), - () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); - - default: - return ExecuteMacroWithErrorWrapper(model, - $"Execute macro with unsupported type \"{model.MacroType}\".", - "Executed.", - () => { throw new Exception("Unsupported macro type."); }, - () => textService.Localize("errors/macroErrorUnsupportedType")); - } + return ExecuteMacroWithErrorWrapper(model, + $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", + "Executed PartialView.", + () => ExecutePartialView(model, content), + () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); } @@ -371,12 +339,10 @@ namespace Umbraco.Web.Macros #region Execution helpers - // parses attribute value looking for [@requestKey], [%sessionKey], [#pageElement], [$recursiveValue] + // parses attribute value looking for [@requestKey], [%sessionKey] // supports fallbacks eg "[@requestKey],[%sessionKey],1234" - private string ParseAttribute(IDictionary pageElements, string attributeValue) + private string ParseAttribute(string attributeValue) { - if (pageElements == null) throw new ArgumentNullException(nameof(pageElements)); - // check for potential querystring/cookie variables attributeValue = attributeValue.Trim(); if (attributeValue.StartsWith("[") == false) @@ -388,7 +354,7 @@ namespace Umbraco.Web.Macros // like [1,2,3] which we don't want to parse - however the last one can be a literal, so // don't check on the last one which can be just anything - check all previous tokens - char[] validTypes = { '@', '%', '#', '$' }; + char[] validTypes = { '@', '%' }; if (tokens.Take(tokens.Length - 1).Any(x => x.Length < 4 // ie "[?x]".Length - too short || x[0] != '[' // starts with [ @@ -424,14 +390,6 @@ namespace Umbraco.Web.Macros if (string.IsNullOrEmpty(attributeValue)) attributeValue = _cookieManager.GetCookieValue(name); break; - case '#': - attributeValue = pageElements[name]?.ToString(); - break; - case '$': - attributeValue = pageElements[name]?.ToString(); - if (string.IsNullOrEmpty(attributeValue)) - attributeValue = ParseAttributeOnParents(pageElements, name); - break; } attributeValue = attributeValue?.Trim(); @@ -442,26 +400,6 @@ namespace Umbraco.Web.Macros return attributeValue; } - private string ParseAttributeOnParents(IDictionary pageElements, string name) - { - if (pageElements == null) throw new ArgumentNullException(nameof(pageElements)); - // this was, and still is, an ugly piece of nonsense - - var value = string.Empty; - var cache = _umbracoContextAccessor.UmbracoContext.Content; - - var splitpath = (string[])pageElements["splitpath"]; - for (var i = splitpath.Length - 1; i > 0; i--) // at 0 we have root (-1) - { - var content = cache.GetById(int.Parse(splitpath[i])); - if (content == null) continue; - value = content.Value(name)?.ToString(); - if (string.IsNullOrEmpty(value) == false) break; - } - - return value; - } - #endregion } diff --git a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs b/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs deleted file mode 100644 index 29c03f7cfa..0000000000 --- a/src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Web.Editors; -using Umbraco.Web.Routing; - -namespace Umbraco.Web.Macros -{ - /// - /// Legacy class used by macros which converts a published content item into a hashset of values - /// - internal class PublishedContentHashtableConverter - { - #region Constructors - - /// - /// Initializes a new instance of the class for a published document request. - /// - /// The pointing to the document. - /// The . - /// - /// The difference between creating the page with PublishedRequest vs an IPublishedContent item is - /// that the PublishedRequest takes into account how a template is assigned during the routing process whereas - /// with an IPublishedContent item, the template id is assigned purely based on the default. - /// - internal PublishedContentHashtableConverter(IPublishedRequest frequest, IUserService userService) - { - if (!frequest.HasPublishedContent) - throw new ArgumentException("Document request has no node.", nameof(frequest)); - - PopulatePageData(frequest.PublishedContent.Id, - frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias, - frequest.PublishedContent.GetWriterName(userService), frequest.PublishedContent.GetCreatorName(userService), frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate, - frequest.PublishedContent.Path, frequest.PublishedContent.Parent?.Id ?? -1); - - if (frequest.HasTemplate) - { - Elements["template"] = frequest.TemplateModel.Id.ToString(); - } - - PopulateElementData(frequest.PublishedContent); - - } - - /// - /// Initializes a new instance of the page for a published document - /// - /// - internal PublishedContentHashtableConverter(IPublishedContent doc, IUserService userService) - { - if (doc == null) throw new ArgumentNullException(nameof(doc)); - - PopulatePageData(doc.Id, - doc.Name, doc.ContentType.Id, doc.ContentType.Alias, - doc.GetWriterName(userService), doc.GetCreatorName(userService), doc.CreateDate, doc.UpdateDate, - doc.Path, doc.Parent?.Id ?? -1); - - if (doc.TemplateId.HasValue) - { - //set the template to whatever is assigned to the doc - Elements["template"] = doc.TemplateId.Value.ToString(); - } - - PopulateElementData(doc); - } - - /// - /// Initializes a new instance of the page for a content. - /// - /// The content. - /// - /// This is for usage only. - internal PublishedContentHashtableConverter(IContent content, IVariationContextAccessor variationContextAccessor, IUserService userService, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IPublishedContentTypeFactory publishedContentTypeFactory, UrlSegmentProviderCollection urlSegmentProviders) - : this(new PagePublishedContent(content, variationContextAccessor, userService, shortStringHelper, contentTypeBaseServiceProvider, publishedContentTypeFactory, urlSegmentProviders), userService) - { } - - #endregion - - #region Initialize - - private void PopulatePageData(int pageId, - string pageName, int nodeType, string nodeTypeAlias, - string writerName, string creatorName, DateTime createDate, DateTime updateDate, - string path, int parentId) - { - // Update the elements hashtable - Elements.Add("pageID", pageId); - Elements.Add("parentID", parentId); - Elements.Add("pageName", pageName); - Elements.Add("nodeType", nodeType); - Elements.Add("nodeTypeAlias", nodeTypeAlias); - Elements.Add("writerName", writerName); - Elements.Add("creatorName", creatorName); - Elements.Add("createDate", createDate); - Elements.Add("updateDate", updateDate); - Elements.Add("path", path); - Elements.Add("splitpath", path.Split(',')); - } - - /// - /// Puts the properties of the node into the elements table - /// - /// - private void PopulateElementData(IPublishedElement node) - { - foreach (var p in node.Properties) - { - if (Elements.ContainsKey(p.Alias) == false) - { - // note: legacy used the raw value (see populating from an Xml node below) - // so we're doing the same here, using DataValue. If we use Value then every - // value will be converted NOW - including RTEs that may contain macros that - // require that the 'page' is already initialized = catch-22. - - // to properly fix this, we'd need to turn the elements collection into some - // sort of collection of lazy values. - - Elements[p.Alias] = p.GetSourceValue(); - } - } - } - #endregion - - /// - /// Returns a Hashtable of data for a published content item - /// - public Hashtable Elements { get; } = new Hashtable(); - - - #region PublishedContent - - private class PagePublishedProperty : PublishedPropertyBase - { - private readonly object _sourceValue; - private readonly IPublishedContent _content; - - public PagePublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content) - : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored - { - _sourceValue = null; - _content = content; - } - - public PagePublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, IProperty property) - : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored - { - _sourceValue = property.GetValue(); - _content = content; - } - - public override bool HasValue(string culture = null, string segment = null) - { - return _sourceValue != null && ((_sourceValue is string) == false || string.IsNullOrWhiteSpace((string)_sourceValue) == false); - } - - public override object GetSourceValue(string culture = null, string segment = null) - { - return _sourceValue; - } - - public override object GetValue(string culture = null, string segment = null) - { - // isPreviewing is true here since we want to preview anyway... - const bool isPreviewing = true; - var source = PropertyType.ConvertSourceToInter(_content, _sourceValue, isPreviewing); - return PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Unknown, source, isPreviewing); - } - - public override object GetXPathValue(string culture = null, string segment = null) - { - throw new NotImplementedException(); - } - } - - private class PagePublishedContent : IPublishedContent - { - private readonly IContent _inner; - private readonly IPublishedProperty[] _properties; - private IReadOnlyDictionary _cultureInfos; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IShortStringHelper _shortStringHelper; - private readonly UrlSegmentProviderCollection _urlSegmentProviders; - - private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary(); - - private PagePublishedContent(int id) - { - Id = id; - } - - public PagePublishedContent(IContent inner, IVariationContextAccessor variationContextAccessor, IUserService userService, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IPublishedContentTypeFactory publishedContentTypeFactory, UrlSegmentProviderCollection urlSegmentProviders) - { - _inner = inner ?? throw new ArgumentNullException(nameof(inner)); - _variationContextAccessor = variationContextAccessor; - _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); - _urlSegmentProviders = urlSegmentProviders ?? throw new ArgumentNullException(nameof(urlSegmentProviders)); - - Id = _inner.Id; - Key = _inner.Key; - - CreatorName = _inner.GetCreatorProfile(userService)?.Name; - WriterName = _inner.GetWriterProfile(userService)?.Name; - - var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(_inner); - ContentType = publishedContentTypeFactory.CreateContentType(contentType); - - _properties = ContentType.PropertyTypes - .Select(x => - { - var p = _inner.Properties.SingleOrDefault(xx => xx.Alias == x.Alias); - return p == null ? new PagePublishedProperty(x, this) : new PagePublishedProperty(x, this, p); - }) - .Cast() - .ToArray(); - - Parent = new PagePublishedContent(_inner.ParentId); - } - - public IPublishedContentType ContentType { get; } - - public int Id { get; } - - public Guid Key { get; } - - public int? TemplateId => _inner.TemplateId; - - public int SortOrder => _inner.SortOrder; - - public string Name => _inner.Name; - - public IReadOnlyDictionary Cultures - { - get - { - if (!_inner.ContentType.VariesByCulture()) - return NoCultureInfos; - - if (_cultureInfos != null) - return _cultureInfos; - - return _cultureInfos = _inner.PublishCultureInfos.Values - .ToDictionary(x => x.Culture, x => new PublishedCultureInfo(x.Culture, x.Name, _inner.GetUrlSegment(_shortStringHelper, _urlSegmentProviders, x.Culture), x.Date)); - } - } - - public string UrlSegment => throw new NotImplementedException(); - - public string WriterName { get; } - - public string CreatorName { get; } - - public int WriterId => _inner.WriterId; - - public int CreatorId => _inner.CreatorId; - - public string Path => _inner.Path; - - public DateTime CreateDate => _inner.CreateDate; - - public DateTime UpdateDate => _inner.UpdateDate; - - public int Level => _inner.Level; - - public string Url => throw new NotImplementedException(); - - public PublishedItemType ItemType => PublishedItemType.Content; - - public bool IsDraft(string culture = null) - { - throw new NotImplementedException(); - } - - public bool IsPublished(string culture = null) - { - throw new NotImplementedException(); - } - - public IPublishedContent Parent { get; } - - public IEnumerable Children => throw new NotImplementedException(); - - public IEnumerable ChildrenForAllCultures => throw new NotImplementedException(); - - public IEnumerable Properties => _properties; - - public IPublishedProperty GetProperty(string alias) - { - throw new NotImplementedException(); - } - } - - #endregion - } -}