Alternative template for MenuItemLink

Topics: Customizing Orchard, Writing themes
Jul 15, 2013 at 3:57 PM
I'm creating a sub navigation widget called 'Callouts' that will show an image summary of each page it links to.

I want to change MenuItemLink.cshtml to include these images, but the only template I've found that I'm able to change based on the widget name is Widget-Name-Callouts.cshtml

There are several layers in between this and the template I want to change:
Widget-Name-Callouts.cshtml
@Display(Model.Content)

    Parts.MenuWidget.cshtml
    @Display(Model.Menu)

        Menu.cshtml
        ...
        @DisplayChildren(Model)
        ...

             MenuItemLink.cshtml
Is there a way to make a specific MenuItemLink template based on the widget it is contained in?

Thanks.
Jul 17, 2013 at 2:02 PM
It looks like MenuItem link does not get an alternative based on it's widget or zone. I tried enabling WidgetAlternatives and stepped through the code to see what happened when it got a MenuItemLink. It ignores them because it can't get a WidgetPart from them.

So I'm trying to create an Alternates Factory to add a zone or widget name based alternative into MenuItemLink. This is as far as I've got:
public class MenuItemLinkAlternatesFactory : ShapeDisplayEvents {
    public override void Displaying(ShapeDisplayingContext context) {
        context.ShapeMetadata.OnDisplaying(displayedContext => {
            if(displayedContext.ShapeMetadata.Type == "MenuItemLink") {
                var zoneOrWidgetName = "Todo: How do I get the zone or widget name";
                displayedContext.ShapeMetadata.Alternates.Add(shapeName + "__" + zoneOrWidgetName);
            }
        }
    }
}
I can't figure out how to get the containing zone or the containing widget from the MenuItemLink. Is it possible?
Jul 18, 2013 at 3:28 PM
Edited Jul 18, 2013 at 4:46 PM
I couldn't find a way to get the zone directly from a MenuItemLink, but as MenuItems are in Parts_MenuWidget I was able to grab those parts too and modify their shapes.

I'm going to package this as a module, but here is the code for people to check out and comment on if I'm doing something foolish:
String zone;
switch (displayedContext.ShapeMetadata.Type) {                      
    case "Parts_MenuWidget":
        zone = displayedContext.Shape.ContentItem.WidgetPart.Zone;
        displayedContext.Shape.Menu.Zone = zone;
        break;
    case "MenuItemLink":
        zone = displayedContext.Shape.Parent.Zone;
        displayedContext.ShapeMetadata.Alternates.Add("MenuItemLink__" + zone);
        break;
}
Jul 18, 2013 at 4:45 PM
Now available as a module in the Orchard Gallery:

MenuItemLink Alternates Module
Marked as answer by RichardGarside on 2/12/2014 at 3:40 AM
Jul 25, 2013 at 7:12 PM
I used your module to push a different shape for the Breadcrumbs widget into another zone. Works perfect! Awesome job, thanks!
Sep 30, 2013 at 11:36 PM
I've installed this module and enabled it but I can't figure out how to use it.

Is there any documentation or can you please explain me how should anyone use the module?

Thanks!
Sep 30, 2013 at 11:43 PM
Turn on Shape Tracing. After you add the widget to the zone you want, Shape Tracing will allow you to create a zone specific shape for it.
Oct 1, 2013 at 12:35 AM
Edited Oct 1, 2013 at 12:56 AM
psenechal wrote:
Turn on Shape Tracing. After you add the widget to the zone you want, Shape Tracing will allow you to create a zone specific shape for it.
That's completely and absolutely amazing! Thanks!

I think your module solves this problem: https://orchard.codeplex.com/workitem/19930 so you should notify Bertrand or any other developer so that they can be aware of your development (This is to Richard Garside)

Just one more question:

By zone aware do you mean that the alternate can be positioned in the placement.info or do you mean anything else?

Thanks again! :)
Oct 1, 2013 at 12:52 AM
Good question...I'm actually not sure how to position an alternate in Placement...I've only ever used it for Content Types and Paths. I'll have to defer to the Placement experts. The Shape Alternate you create using Richard's module will only display for a MenuItemLink shape in the zone it's created for though.

For example, I use this module to generate a breadcrumb in the BeforeMain zone because it uses the same MenuItemLink shape as the main menu and I want it to behave differently for the breadcrumb.
Oct 1, 2013 at 12:58 AM
psenechal wrote:
Good question...I'm actually not sure how to position an alternate in Placement...I've only ever used it for Content Types and Paths. I'll have to defer to the Placement experts. The Shape Alternate you create using Richard's module will only display for a MenuItemLink shape in the zone it's created for though.

For example, I use this module to generate a breadcrumb in the BeforeMain zone because it uses the same MenuItemLink shape as the main menu and I want it to behave differently for the breadcrumb.
I need to do exactly what you say in your last 2 lines. Can you please show screen shots or some code to see clearly how you achieved that?

Sry if I'm asking too much, if you can't that's ok! ^^'

Thanks!
Oct 1, 2013 at 1:33 AM
No worries...let me grab a laptop. Too difficult to do it on my phone =)
Oct 1, 2013 at 2:00 AM
Okay...much better. I'll just run through the steps I used
  1. Install the MenuItemLink Alternates module
  2. Create a new Widget layer named NotTheHomepage so we don't display the breadcrumb on the homepage - Layer Rule: not url '~/'
  3. Add a new Menu Widget to the BeforeContent zone on the NotTheHomepage layer
  4. Give the widget a title, but uncheck the box to display it.
  5. Check the box to Display as Breadcrumb
  6. Check both the "Add the home page as the first element" and "Add the current content item as the last element" boxes
  7. Click the Save button
  8. Add a file to your theme's Views folder named "MenuItemLink-BeforeMain.cshtml". Below is the code from my file:
@if (Model.Text.ToString() == "Home") {
    <a href="@Model.Href"><i class="icon-home"></i></a>
}
else {
    <i class="icon-angle-right"></i>
    <a href="@Model.Href">@Model.Text</a>
}
What my alternate does is display an icon for the homepage instead of the name, and it adds a right angle icon (font-awesome) between each link.

I also add some jQuery code in a script that runs on every page that removes the <a> tag around the last link. It looks like this:
$('.breadcrumbs ul li.last a').contents().unwrap();
that jQuery is specific to using Bootstrap though. I believe with the default Orchard setup, you'd want to use:
$('ul.breadcrumb li.last a').contents().unwrap();
also...keep in mind that you can change the breadcrumb separator using CSS as long as you don't need anything fancy. Here's some CSS I use (Bootstrap again) that can be changed a bit to suit Orchard's default setup:
.breadcrumbs { margin: 24px 0; }
.breadcrumbs ul li { display: inline-block; line-height: 24px; font-size: 13px; color: #9e9e9e; margin: 0 0 0 5px; }
.breadcrumbs ul li a { color: #898989; font-weight: 600; }
.breadcrumbs ul li:first-child { margin-left: 2px !important; }
.breadcrumbs ul li:nth-child(2):before, .breadcrumbs ul li:first-child:before { content: " "; margin: 0; }
.breadcrumbs ul li:before { color: #9e9e9e; content: "\f105"; font-family: FontAwesome; font-size: 12px; margin: 0 9px 0 0; }
Hope that helps. Let me know if you have any other questions about it.