In short, I've tapped into the GatheringNodeData event on a Lucene indexer to massage the contents of the index. I have that all working but I can't seem to successfully render macros inside my content strings. Here's my helper method:
public static string GetContent(string content, int pageId = 0)
{
var regexMacro = new Regex(@"<\?UMBRACO_MACRO[^>]+>");
return regexMacro.Replace(content, delegate(Match m)
{
var macroContent = umbraco.library.RenderMacroContent(m.Value, pageId);
if (regexMacro.IsMatch(macroContent))
{
macroContent = GetContent(macroContent, pageId);
}
return macroContent;
});
}
I can't get any macro to render I get a null exception. As a test, I replaced all contents of my cshtml macro script with just "test", but I still receive the following:
"<!-- Error generating macroContent: 'System.NullReferenceException: Object reference not set to an instance of an object.\r\n at umbraco.page..ctor(XmlNode node)\r\n at umbraco.library.RenderMacroContent(String Text, Int32 PageId)' -->"
The comment on the RenderMacroContent method states, "This only works properly with xslt macros. Python and .ascx based macros will not render properly, as viewstate is not included.". Anyone have an idea I can attempt to get these pages to render with all its content so I can index them to my liking?
public static string GetContent(string content, int pageId = 0)
{
var regexMacro =newRegex(@"<\?UMBRACO_MACRO[^>]+>");
return regexMacro.Replace(content, delegate(Match m)
{
var aliasMatch = Regex.Match(m.Value, "alias=.([^\"']+).", RegexOptions.IgnoreCase);
if (!aliasMatch.Success) return string.Empty; // macro must be screwed up to not have an alias
var macro = umbraco.macro.GetMacro(aliasMatch.Groups[1].Value);
var attribs = new Hashtable();
macro.Model.Properties.ForEach(p =>
{
var pMatch = Regex.Match(m.Value, string.Format("{0}=\"([^\"]+)\"", p.Key),
RegexOptions.IgnoreCase);
attribs.Add(p.Key, (pMatch.Success) ? pMatch.Groups[1].Value : string.Empty);
});
macro.UpdateMacroModel(attribs);
var macroContentControl = macro.renderMacro(attribs, pageId);
var page = new Page();
page.Controls.Add(macroContentControl);
var sw = new StringWriter();
using (var htw = new HtmlTextWriter(sw))
{
page.RenderControl(htw);
}
var macroContent = sw.ToString();
// Check to see if the macro content has another macro to render
if (regexMacro.IsMatch(macroContent))
{
macroContent = GetContent(macroContent, pageId);
}
return macroContent;
});
}
I received an error about not having an HttpContext so I created a dummy one in the GatheringNodeData event like so:
// mock up a context if necessary
if (HttpContext.Current == null)
{
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://mydomain.com", ""),
new HttpResponse(new StringWriter()))
{
User = new GenericPrincipal(
new GenericIdentity("admin"),
new string[0]
)
};
}
This didn't work either, so I gave up and manually gather the content based on the macro parameters. I only have two macros I use in the editor, so this is what I ended up with:
public static string GetContent(string content, int pageId = 0)
{
var regexMacro =newRegex(@"<\?UMBRACO_MACRO[^>]+>");
return regexMacro.Replace(content, delegate(Match m)
{
var aliasMatch = Regex.Match(m.Value, "alias=.([^\"']+).", RegexOptions.IgnoreCase);
if (!aliasMatch.Success) return string.Empty; // macro must be screwed up to not have an alias
var macro = umbraco.macro.GetMacro(aliasMatch.Groups[1].Value);
var cs = SearchService.Instance.ContentService;
var sb = new StringBuilder();
switch (macro.Alias)
{
case "GuideForms":
macro.Model.Properties.ForEach(p =>
{
var val = Regex.Match(m.Value,
string.Format("{0}=.([^\"']+).", p.Key),
RegexOptions.IgnoreCase);
if (val.Success && !string.IsNullOrWhiteSpace(val.Groups[1].Value))
{
if (p.Key.StartsWith("formPick", StringComparison.InvariantCultureIgnoreCase))
{
var form = new DynamicNode(val.Groups[1].Value);
var formName = (form.HasValue("formName"))
? form.GetPropertyValue("formName")
: form.Name;
sb.AppendFormat("{0} {1} ", form.GetPropertyValue("formID"),
formName);
}
}
if (p.Key.StartsWith("formInfo"))
{
var info = Regex.Match(m.Value,
string.Format("{0}=.([^\"']+).", p.Key),
RegexOptions.IgnoreCase);
if (info.Success && string.IsNullOrWhiteSpace(info.Groups[1].Value))
{
sb.AppendFormat("{0} ", info.Groups[1].Value);
}
}
});
break;
case "GuideNote":
var nid = Regex.Match(m.Value, "nodeId=.([^\"']+).", RegexOptions.IgnoreCase);
if (nid.Success && !string.IsNullOrWhiteSpace(nid.Groups[1].Value))
{
var note = new DynamicNode(nid.Groups[1].Value);
sb.AppendFormat("{0} ", note.GetPropertyValue("content"));
}
break;
}
var macroContent = sb.ToString();
// Check to see if the macro content has another macro to render
if (regexMacro.IsMatch(macroContent))
{
macroContent = GetContent(macroContent, pageId);
}
return macroContent;
});
}
If someone has a solution to either of the first two that would be great! This gets the job done at least. Thought I'd post it for someone else travelling down this path or correct me if there's a simpler path.
RenderMacro for a Lucene Index
In short, I've tapped into the GatheringNodeData event on a Lucene indexer to massage the contents of the index. I have that all working but I can't seem to successfully render macros inside my content strings. Here's my helper method:
I can't get any macro to render I get a null exception. As a test, I replaced all contents of my cshtml macro script with just "test", but I still receive the following:
"<!-- Error generating macroContent: 'System.NullReferenceException: Object reference not set to an instance of an object.\r\n at umbraco.page..ctor(XmlNode node)\r\n at umbraco.library.RenderMacroContent(String Text, Int32 PageId)' -->"
The comment on the RenderMacroContent method states, "This only works properly with xslt macros. Python and .ascx based macros will not render properly, as viewstate is not included.". Anyone have an idea I can attempt to get these pages to render with all its content so I can index them to my liking?
Here's my 2nd attempt:
I received an error about not having an HttpContext so I created a dummy one in the GatheringNodeData event like so:
This didn't work either, so I gave up and manually gather the content based on the macro parameters. I only have two macros I use in the editor, so this is what I ended up with:
If someone has a solution to either of the first two that would be great! This gets the job done at least. Thought I'd post it for someone else travelling down this path or correct me if there's a simpler path.
is working on a reply...