Umbraco 7 Partial View Macro Rendering

8.8k views Asked by At

In Umbraco 7.0.3 I:

  1. Created a Data Type called Macro Container with Property editor of Macro container
  2. Created Document Type called Contact Form with Property called Body with Type Macro Container
  3. Created Partial View called _contactForm.cshtml (in Views\MacroPartials)
  4. Created Macro called Contact Form with MVC Partial view _contactFrom.cshtml
  5. Added Content of type Contact Form called Contact Us
  6. Added Contact Form macro to the Macro Container property called Body in my Contact Us page

I then have a Surface Controller that I call with some AJAX to display the page (more specifically the Body property of the page):

public class JsController : SurfaceController
{
    public ActionResult GetPage(int id)
    {
        var page = new Node(id);

        if (page == null || page.GetProperty("body") == null)
            return Content(@"Hmm, something went wrong. Unable to find what you're looking for.");

        return Content(page.GetProperty("body").Value);
    }
}

This setup almost works but the problem is that instead of the rendered form, what is returned is:

<!--?UMBRACO_MACRO macroAlias="ContactForm" /-->

So now I need to render this macro\form\partial view...I think that I probably need to do it in the Controller, but if I can do it on the other side (via Javascript) that would work as well. Is there an Umbraco function I can call in the controller to render a macro based on the page id and macro alias?

3

There are 3 answers

1
Serj Sagan On BEST ANSWER

So after spending several hours fuming at how painfully stupid the Umbraco team made this process, reading threads like this and this, I finally figured out a fairly ugly, but working way...things would have been so much more simple if the PublishedContentRequest class constructor was not internal!

Anyways, here's what I had to do: 1) Extend EnsurePublishedContentRequestAttribute

public class CreatePublishedContentRequestAttribute
    : EnsurePublishedContentRequestAttribute
{
    public CreatePublishedContentRequestAttribute() : base(0) { }

    protected override void ConfigurePublishedContentRequest(
        PublishedContentRequest publishedContentRequest,
        ActionExecutedContext filterContext)
    {
        var contentId = filterContext.RouteData.Values["id"];
        int id = 0;

        if (contentId != null && int.TryParse(contentId.ToString(), out id))
        {
            var content = UmbracoContext.ContentCache.GetById(id);
            publishedContentRequest.PublishedContent = content;

            var defaultLanguage = Language.GetAllAsList().FirstOrDefault();
            publishedContentRequest.Culture = (defaultLanguage == null)
                ? CultureInfo.CurrentUICulture
                : new CultureInfo(defaultLanguage.CultureAlias);

            publishedContentRequest.ConfigureRequest();

            HttpContext.Current.Session["PublishedContentRequest"]
                = publishedContentRequest;
        }
    }
}

2) Redirect to an action decorated with this attribute that redirects back to my GetPage action and retrieve the PCR from the Session. Now we can render our macro:

public ActionResult GetPage(int id)
{
    var publishedContent = UmbracoContext.ContentCache.GetById(id);
    if (publishedContent == null || publishedContent.GetProperty("body") == null)
    { return Content(@"Unable to find what you're looking for."); }

    if (UmbracoContext.PublishedContentRequest == null
        && Session["PublishedContentRequest"] == null)
    { return RedirectToAction("CreatePublishedContentRequest", new { id }); }

    UmbracoContext.PublishedContentRequest =
        (PublishedContentRequest) Session["PublishedContentRequest"];
    Session["PublishedContentRequest"] = null;

    UmbracoContext.HttpContext.Items["pageID"] = id;

    return Content(GetHtmlContent(publishedContent));
}

[CreatePublishedContentRequest]
public ActionResult CreatePublishedContentRequest(int id)
{
    return RedirectToAction("GetPage", new { id });
}

private string GetHtmlContent(IPublishedContent publishedContent)
{
    string content = publishedContent.GetProperty("body").Value.ToString();
    if (string.IsNullOrEmpty(content) || !content.Contains("UMBRACO_MACRO"))
    { return content;}

    int startIndex = content.IndexOf("macroAlias=") + 12;
    int length = content.LastIndexOf('"') - startIndex;
    var macroAlias = content.Substring(startIndex, length);

    return (Umbraco.RenderMacro(macroAlias) ?? new HtmlString("")).ToString();
}

This works, but this is some pretty hacky stuff. If the Umbraco team made the PublishedContentRequest constructor public, this could have been much, much cleaner. Of course, there's probably a better way to do this, if so, I'm all ears.

1
Vlax On

wasn't it possible to use umbraco.library.RenderMacroContent ?

1
Daniel Bardi On

Your controller name needs to contain 'Surface' in its name.

JsSurfaceController

Also, add [HttpPost] attribute to the ActionResult method.

http://our.umbraco.org/documentation/Reference/Mvc/surface-controllers http://our.umbraco.org/documentation/Reference/Mvc/forms