Best way to push an existing Part to a Zone?

Topics: Customizing Orchard, Writing themes
Mar 30, 2011 at 10:52 AM

I've just started building a new site and theme. I started building the theme from scratch (instead of copying Layout.cshtml from TheThemeMachine) - partly so I could get a better understanding of everything, partly so I could have very specific control over the markup and layout.

Here's my very simple layout right now:

 

<div class="layout">

    <div class="header">
        @Zone(Model.Header)
    </div>

    <div class="content">
        @Zone(Model.Content)

        <aside class="left-column">
            @Zone(Model.LeftColumn)
        </aside>
        
    </div>

    <div class="navigation">
        @Zone(Model.Navigation)
    </div>

    <div class="footer">
        @Zone(Model.Footer)
    </div>
</div>

 

Next, I want to move the Parts_RoutableTitle shape from Content into the Header zone.

I thought this would be possible using Placement.info but I was mistaken in thinking that Placement was referring to zones. I did:

 

  <Place Parts_RoutableTitle="Header:1"></Place>

...But this just moved it to position #1, still in the Content zone.

What's the "best practice" way of achieving this? If it's not possible in Placement, might this be something nice to enable in the future?

Coordinator
Mar 30, 2011 at 11:14 PM

Placement only moves content parts around the local zones within the global Content zone. See this post to go beyond this limitation: http://weblogs.asp.net/bleroy/archive/2011/03/26/dispatching-orchard-shapes-to-arbitrary-zones.aspx

Mar 31, 2011 at 5:39 AM

With that method you do it from the part's Driver - but here I'm trying to move an existing shape, Parts_RoutableTitle, where I don't have any control over its driver and the shape originates from Orchard Core. How would I go about accessing that shape and push it to new places? Do I just have to implement my own RoutePartDriver - will that even work?

Coordinator
Mar 31, 2011 at 5:40 AM

No, just do it from a handler.

Mar 31, 2011 at 6:38 AM

I've written the following handler in my theme:

 

    public class ThemeHandler : ContentHandler
    {
        IWorkContextAccessor _workContextAccessor;

        public ThemeHandler(IWorkContextAccessor workContextAccessor)
        {
            _workContextAccessor = workContextAccessor;

            this.OnGetDisplayShape<RoutePart>((context, part) =>
            {
                var workContext = _workContextAccessor.GetContext();
                workContext.Layout.Zones["Header"]
                    .Add(context.Shape, 1);
            });
        }
    }

The OnGetDisplayShape delegate does get called, but still nothing shows up in my Header zone? Is there a different event I should be using?

 

 

 

Coordinator
Apr 1, 2011 at 12:16 AM

That might be too late, yes. You should try other events, or to put the code in a shape table provider.

May 5, 2011 at 3:02 PM

Did you solve this randompete? I'm trying to do something similar.

May 5, 2011 at 4:12 PM

I got it to work by writing a new driver for RoutePart and building a new shape which I could then add to the zone. It didn't seem too satisfactory however so I've been planning a different approach. Hopefully I'll get around to that today or tomorrow and let you know.

Jun 16, 2011 at 10:52 AM

I had a similar problem, being unable to access the model directly from views added to the header. This may be a bit of a hack but I got around it by adding the title to the ViewBag which I could then access from Branding.cshtml. Added the following to Layout.cshtml

/* Add the page title to the viewbag for rendering in Branding.cshtml */
    ViewBag.PageTitle = Model.Title;
    /**********************************/
    WorkContext.Layout.Header.Add(New.Branding(), "5"); // Site name and link to the home page
Jun 16, 2011 at 11:55 AM

You can pass properties directly into the model when you make shapes like Branding; e.g.:

 WorkContext.Layout.Header.Add(New.Branding(Title:Model.Title), "5");

Then in Branding.cshtml:

<h1>@Model.Title</h1>
This thread was discussing a slightly different problem, namely how to move a chunk of a content item into a different zone. Never really solved that particular task completely - worked around it in different ways, and I've now got some other really useful ways to push stuff into zones.

Jun 16, 2011 at 7:55 PM

Hi randompete,

Sorry, I am quite new to this. Thank you for the post, I have been looking for a way to simply pass properties to the view and have until now been unable to find an example on how to do this. Really liking Orchard so far though.

Jun 16, 2011 at 8:58 PM

There are lots of different ways to do things in Orchard; it takes time to discover them all and work out which ones are best to use in what scenarios!

Jun 23, 2011 at 5:48 PM

This seems like an issue that would come up quite regularly.  I know I've run into this several times, however I feel like the routable part for a page is going to regularly need to be in different zone in many different designs.  

Jun 28, 2011 at 2:29 AM

Does anyone have a specific example of how to do this?  

Here is my use case:  

Blog post has field of HTML type called AsideFirstContent which I want to show in the AsideFirst zone.  

Can I get it there or do I have to make a new field type called ZonedHTML which follows Bertrand's Amazon example?

Coordinator
Jun 28, 2011 at 2:40 AM

You don't need to create a new field. From a handler or a shape table provider, you should be able to intercept the displaying of a specific shape and add it to a different zone, which will have the effect of moving it.

Jan 24, 2012 at 3:29 PM
Edited Jan 24, 2012 at 3:32 PM
randompete wrote:

Next, I want to move the Parts_RoutableTitle shape from Content into the Header zone.

I thought this would be possible using Placement.info but I was mistaken in thinking that Placement was referring to zones. I did:

  <Place Parts_RoutableTitle="Header:1"></Place>

Solved - it only took me 10 months to figure out but I finally have an answer ;)

I found a way to implement a Clay behaviour that lets you do this in Placement.info:

  <Place Parts_RoutableTitle="Layout@Header:1"></Place>

Tutorial and code for this behaviour is here: http://downplay.co.uk/tutorials/clay/part-2-through-the-looking-glass

Developer
Jan 25, 2012 at 12:09 PM

Wonderful, I'm truly amazed. I demand this to be part of the core :-).

Jan 25, 2012 at 1:10 PM

If it was moved into core I could fix ContentShapeResult to allow a syntax of "Layout:Header:1" which would be slightly neater. Currently adding any extra colons will make placement fail completely.

Developer
Jan 25, 2012 at 1:56 PM
Edited Jan 25, 2012 at 4:33 PM

I think the syntax is fine as it's better to use a different separator between layout zone-local zone (both string identifiers) than local zone-weight (string identifier and integer weight). Although the order of the elements follows the same logic here as with namespaces (meaning it's practically big-endian), considering the meaning of the @ sign (at) and its usage in e-mail addresses a reversed order would have a clearer meaning (e.g. Header:1@Layout, read out as "Header local zone with weight 1 at layout zone Layout"). Or just use the syntax of namespaces with dots: Layout.Header:1 (AFAIK this wouldn't interfere with anything either).

Jan 25, 2012 at 2:06 PM

Unfortunately having the @ right at the end wouldn't work, because the position is passed into the zone; I had to provide the correct zone, so I needed to catch things earlier, when the zone name is accessed. Basically I'm cheating the system into thinking that it's looking for a zone called "Layout@Header", but the clay behaviour kicks in and engages the proxy, so it looks for Header in a different place.

I did consider using dots, but I was worried that dots have a lot of meanings already and the meaning might not be as clear. The @ sign makes it obvious that something special is happening. So I could just swap around the two bits, so it's Header@Layout:1 instead, but it looks kind of strange and out-of-order, a bit like when I see an mm/dd/yyyy date ;) 

Developer
Jan 25, 2012 at 4:41 PM

I see. Of course if this gets into the core there will be no such constraints :-).

Header@Layout:1 looks silly for me too although its logic is defensible: weight can be at the end, as it marks the whole expression, since Header@Layout is what unambiguously identifies the zone ("Header local zone at Layout layout zone") and the weight defines the shape's rendering in this specific zone.

Maybe a core developer could share his thoughts?

Coordinator
Jan 26, 2012 at 5:37 AM

Pete's syntax looks just fine to me. Not that he had a choice in the matter.

Mar 23, 2012 at 2:10 AM

Hi randompete, this is exactly what I am currently wanting to do. Got a couple of questions about implementing your solution:

What directory do the files in the zoneproxy.zip go?

Do I need to install your Downplay.Origami module first?

Does it still work with Orchard 1.4?

Many thanks,