display part in different zone on demand

Topics: Writing modules
Jul 25, 2011 at 12:11 AM
Edited Jul 25, 2011 at 12:37 AM

Hi,

The general requirement is that there is an "ImageField"(from Contrib.ImageField module) in "Page" content type as a banner of the page.

I then need to display the banner in "BeforeMain" zone when displaying the detail of the page. I am currently using a ContentHandler to do this, it seems a bit odd, just wondering is it the right way?

 

 

public class BannerHandler : ContentHandler
    {
        private readonly IWorkContextAccessor _workContextAccessor;

        public BannerHandler(IWorkContextAccessor workContextAccessor)
        {
            _workContextAccessor = workContextAccessor;
        }

        protected override void BuildDisplayShape(BuildDisplayContext context)
        {
            if (context.ContentItem.ContentType == "Page")
            {
                var part = context.ContentItem.Parts.FirstOrDefault(p => p.PartDefinition.Name == "Page" && p.PartDefinition.Fields.Any(f => f.Name == "Banner" && f.FieldDefinition.Name == "ImageField"));
                if (part != null)
                {
                    // look for non-empty image field and put into BeforeMain zone to display
                    var field = (ImageField)part.Fields.Single(f => f.Name == "Banner" && f.FieldDefinition.Name == "ImageField");
                    if (!string.IsNullOrEmpty(field.FileName))
                    {
                        var workContext = _workContextAccessor.GetContext();
                        workContext.Layout.BeforeMain.Add(context.New.Banner(ContentField: field));
                    }
                }
            }
        }
    }
Coordinator
Jul 25, 2011 at 7:33 PM

You should probably read about placement.info: http://orchardproject.net/docs/Understanding-placement-info.ashx

Coordinator
Jul 25, 2011 at 8:02 PM

Oh, sorry, placement is not going to do that. If you want to send it to top-level zones then yes, what you are doing is pretty much the only way, until someone comes up with a module that makes it more approachable.

Jul 26, 2011 at 12:21 AM

I finally do it using OnGetDisplayShape in handler, it looks like:

    public class BannerHandler : ContentHandler
    {
        public BannerHandler(IWorkContextAccessor workContextAccessor)
        {
            OnGetDisplayShape<BannerPart>((context, part) =>
                                              {
                                                  if (context.DisplayType == "Detail")
                                                  {
                                                      var field = part.Fields.SingleOrDefault(f => f is ImageField) as ImageField;
                                                      if (field != null && !string.IsNullOrEmpty(field.FileName))
                                                      {
                                                          workContextAccessor.GetContext().Layout.BeforeMain.Add(context.New.Banner(ContentField: field));
                                                      }    
                                                  }
                                              });
        }
    }

But it requires me put an empty driver for BannerPart in order to trigger the execution of OnGetDisplayShape in handler, can you explain why?

    public class BannerPartDriver : ContentPartDriver<BannerPart>
    {
        // empty, use to trigger OnGetDisplayShap in handler
    }

Cheers,

Coordinator
Jul 26, 2011 at 12:24 AM

Because if you don't have a driver, there will be no shape, I think.

Jan 24, 2012 at 2:19 PM
bertrandleroy wrote:

Oh, sorry, placement is not going to do that. If you want to send it to top-level zones then yes, what you are doing is pretty much the only way, until someone comes up with a module that makes it more approachable.

Solved finally with Clay behaviours:

http://downplay.co.uk/tutorials/clay/part-2-through-the-looking-glass