Use different (zone-dependent) shapes for same widget type

Topics: Writing themes
Oct 27, 2012 at 1:31 PM

I'm writing a theme, where I want to use different shape/view for a widget type depending on what zone the widget are displayed in.

An example is, when the Menu widget is displayed in the zone "Navigation", I want it to use the view named "Navigation.Menu.cshtml". When the Menu widget is displayed in the left aside in my theme (zone: LeftAside), I want it to use the view named "LeftAside.Menu.cshtml".

First, I thought it could be done by shape names as mentioned above, but I've tried it and it didn't work. I've also tried shape-tracing, but without any hints on how this can be achieved. I've read http://docs.orchardproject.net/Documentation/Alternates and there is some hints about settings alternates through the placement.info, but I'm not quite sure how to do it zone-based, or if this is even possible.

My guess would be something like:

  <Match Zone="Navigation">
    <Place Parts_MenuItems="Header:after;Alternate=MenuItem" />
  </Match>
  <Match Zone="LeftAside">
    <Place Parts_MenuItems="Header:after;Wrapper=LeftAsideMenu;Alternate=LeftAsideMenuItem" />
  </Match>

But this doesn't seem to work.

I thought about checking from within the view, what zone it is being displayed in, and then change the alternates on the model directly, but I'm unsure about how to check the current zone from within the view.

Does anyone have any hints about how I could do this? :-)

Oct 28, 2012 at 10:18 PM

I tried implementing the code from this issue: http://orchard.codeplex.com/workitem/17784 (based on discussion: http://orchard.codeplex.com/discussions/249947)

I can now override the Parts.MenuWidget.cshtml with Parts.MenuWidget-Navigation.cshtml, when it's rendered in the navigation zone - so far, so good!

I tried changing the code in the new Parts.MenuWidget-Navigation view like this:

@*Display(Model.Menu)*@
@Display.Menu_Navigation(Model.Menu)

and it renders my view "Menu_Navigation.cshtml" (I didn't change the code, just duplicated and renamed file) - however, "Model.Items" are now empty within this view. It seems like the menu items/the Model.Menu are not passed on. The Menu_Navigation renders this to html: System.Collections.Generic.List`1[System.Object]

Someone got a clue about why this is?

Oct 28, 2012 at 11:26 PM

I solved this (a bit hackish though) by changing the filter implemented from the workitem (mentioned above) to this:

        public override void Displaying(ShapeDisplayingContext context) {            
            context.ShapeMetadata.OnDisplaying(displayedContext => {                
                // We don't want the widget itself, but the content item that consists of the Widget part (e.g. Parts.Blogs.RecentBlogPosts)
                if (displayedContext.ShapeMetadata.Type != "Widget") {
                    ContentItem contentItem = displayedContext.Shape.ContentItem;
                    if (contentItem != null) {
                        // Is the contentItem a widget? (we could probably test for the stereotype setting, don't know if that is more efficient than selecting the WidgetPart)
                        var widgetPart = contentItem.As<WidgetPart>();
                        if (widgetPart != null) {
                            var zoneName = widgetPart.Zone;
                            var shapeName = displayedContext.ShapeMetadata.Type;

                            // Add 2 alternates for flexible shape naming, e.g.
                            // [ShapeName]-[ZoneName].cshtml: "Parts.Blogs.RecentBlogPosts-myZoneName.cshtml"
                            // [ContentTypeName]-[ZoneName].cshtml: "RecentBlogPosts-myZoneName.cshtml"
                            displayedContext.ShapeMetadata.Alternates.Add(contentItem.ContentType + "__" + zoneName);
                            displayedContext.ShapeMetadata.Alternates.Add(shapeName + "__" + zoneName);

                            if (zoneName.Equals("Navigation") && context.Shape.Menu != null && context.Shape.Menu.Metadata.Type.Equals("Menu")) {
                                context.Shape.Menu.Metadata.Type = context.Shape.Menu.Metadata.Type + "_" + zoneName;
                            }
                        }
                    }
                }
            });
        }

And in the newly added view, Menu_Navigation.cshtml, I added this:

    foreach (var item in items) {
        item.Metadata.Type = "MenuItem_Navigation";
    }

And then I customized the shape rendering in a view i named "MenuItem_Navigation.cshtml".

It's really ugly, but it works.

Coordinator
Oct 29, 2012 at 7:42 PM

Did you try enabling widget alternates?

Oct 29, 2012 at 11:23 PM

No. D'oh, only tracing and url alternates - missed that one. But brilliant - that's the thing, thanks for pointing out! :-) ...

 

If I'm not wrong, the shapes Menu and MenuItem still aren't affected?

Anyway, it's easy to change the shapes/models Metadata.Type from the MenuWidget view like this:

@if(Model.Menu != null) {
    Model.Menu.Metadata.Type = "Menu__Navigation";
}