1
votes

I'm building a site in Orchard that will have around 130 pages. We've setup a Navigation menu called "Main Menu" with all of our pages setup in a hierarchy (the example I'm showing is from my sandbox site and not our actual development site).

enter image description here

In my "AsideFirst" zone on our default widget layer, I have added a menu widget that references the above "Main Menu." The start level is set to 0, and the levels to display is set to 0. On the front end, using the shape tracer, I have created an alternate called "Parts.MenuWidget-AsideFirst.cshtml."

The menu renders just fine, however, I'm not sure where to start on modifying the core "Navigation" driver to allow me to display only the pages relative to the page that I'm on. For instance, if I'm on the page "Testing II" I'd like navigation that renders the following HTML:

<ul>
    <li><a href="/testing-i/">Testing I</a></li>
    <li>
        <a href="/testing-ii/" class="active">Testing II</a>
        <ul>
            <li><a href="/bark-bark/"></a></li>
        </ul>
    </li>
    <li><a href="/testing-iii/">Testing III</a>
</ul>

The alternate that I'm using has the following code. This works if I'm within any of the top level sections, but not the deeper levels. The following code is not the Orchard way to do things, so I'd like to know how I should achieve this properly through drivers and handlers - especially since this isn't accomplishing what I need it to anyway.

@{
var navTag = Tag(Model, "nav");
navTag.AddCssClass("nav-sidebar");
@navTag.StartElement;

    var ulTag = Tag(Model, "ul");
    @ulTag.StartElement
        // Model is Model.Menu from the layout (Layout.Menu)
        var items = getSectionItems((IList<dynamic>)Enumerable.Cast<dynamic>(Model.Menu.Items));

        // Add First and Last Classes
        if (items.Any())
        {
            items[0].Classes.Add("first");
            items[items.Count - 1].Classes.Add("last");
        }

        // List Items
        foreach (var listModel in items)
        {
            // Current URL
            string requestUrl = getRequestUrl();
            string modelUrl = getModelUrl(listModel);
            bool showMenu = false;

            if (isActivePage(requestUrl, modelUrl))
            {
                listModel.Classes.Add("active");
                showMenu = true;
            }

            if (showMenu)
            {
                // Sub Items
                var listItems = Enumerable.Cast<dynamic>((System.Collections.IEnumerable)listModel);
                if (listItems.Any())
                {
                    listModel.Classes.Add("dropdown");
                }

                // List Tag
                var liTag = Tag(listModel, "li");

                @liTag.StartElement;

                    listModel.Metadata.Alternates.Clear();
                    listModel.Metadata.Type = "MenuItemLink";

                    // Top Level Nav Items
                    string className = System.Text.RegularExpressions.Regex.Replace(listModel.Href, "[^A-Za-z0-9-]", "");
                    className = className.Length > 0 ? className : "home";

                    <a href="@listModel.Href" class="@className">@listModel.Text</a>

                    if (listItems.Any())
                    {
                        <ul>
                            @DisplayChildren(listModel)
                        </ul>
                    }

                @liTag.EndElement;
            }
        }

    @ulTag.EndElement;

@navTag.EndElement;
}

@functions{
private IList<dynamic> getSectionItems(IList<dynamic> sectionItems)
{
    return sectionItems;
}

private string getRequestUrl()
{
     return Request.Path.Replace(Request.ApplicationPath, string.Empty).TrimEnd('/').ToUpperInvariant();
}

private string getModelUrl(dynamic listModel)
 {
     return listModel.Href.Replace(Request.ApplicationPath, string.Empty).TrimEnd('/').ToUpperInvariant();
 }

private bool isActivePage(string requestUrl, string modelUrl)
{
    if (requestUrl == modelUrl || (!string.IsNullOrEmpty(modelUrl) && requestUrl.StartsWith(modelUrl + "/")))
    {
        return true;
    }

    return false;
}
}
1
This looks more like a breadcrumb than a menu. Look for the "display as breadcrumb" setting of the menu widgetBertrand Le Roy
I've tried the breadcrumb setting, but unfortunately it didn't render any nav items. I am using very similar code to display breadcrumbs on a different zone with the "Main Menu."Javier

1 Answers

0
votes

Updated

I've solved this using the following code. I was close, but I wish I still had a better solution. It solves this for up to 4 levels deep.

@{
    var navTag = Tag(Model, "nav");
    navTag.AddCssClass("nav-sidebar");
    @navTag.StartElement;

        var ulTag = Tag(Model, "ul");
        @ulTag.StartElement
            // Model is Model.Menu from the layout (Layout.Menu)
            string requestUrl = getRequestUrl();
            var menus = (IList<dynamic>)Enumerable.Cast<dynamic>(Model.Menu.Items);
            var items = getSectionItems(requestUrl, menus);

            // List Items
            foreach (var listModel in items)
            {
                // Sub Items
                var listItems = Enumerable.Cast<dynamic>((System.Collections.IEnumerable)listModel);
                if (listItems.Any())
                {
                    listModel.Classes.Add("nav-section");
                }

                // List Tag
                var liTag = Tag(listModel, "li");

                @liTag.StartElement;

                    listModel.Metadata.Alternates.Clear();
                    listModel.Metadata.Type = "MenuItemLink";

                    // Top Level Nav Items
                    string className = System.Text.RegularExpressions.Regex.Replace(listModel.Href, "[^A-Za-z0-9-]", "");
                    className = className.Length > 0 ? className : "home";

                    <a href="@listModel.Href" class="@className">@listModel.Text</a>

                    if (listItems.Any())
                    {
                        <ul>
                            @DisplayChildren(listModel)
                        </ul>
                    }

                @liTag.EndElement;
            }

        @ulTag.EndElement;

    @navTag.EndElement;
}

@functions{
    private IList<dynamic> getSectionItems(string requestUrl, IList<dynamic> sectionItems1)
    {
        foreach (var sectionItem1 in sectionItems1)
        {
            if (getModelUrl(sectionItem1) == requestUrl)
                return sectionItem1;

            foreach (var sectionItem2 in sectionItem1)
            {
                if (getModelUrl(sectionItem2) == requestUrl)
                    return sectionItem2;

                foreach (var sectionItem3 in sectionItem2)
                {
                    if (getModelUrl(sectionItem3) == requestUrl)
                        return sectionItem3;

                    foreach (var sectionItem4 in sectionItem3)
                    {
                        if (getModelUrl(sectionItem4) == requestUrl)
                            return sectionItem4;
                    }
                }
            }
        }

        return sectionItems1;
    }

    private string getRequestUrl()
    {
         return Request.Path.Replace(Request.ApplicationPath, string.Empty).TrimEnd('/').ToUpperInvariant();
    }

    private string getModelUrl(dynamic listModel)
     {
         return listModel.Href.Replace(Request.ApplicationPath, string.Empty).TrimEnd('/').ToUpperInvariant();
     }

    private bool isActivePage(string requestUrl, string modelUrl)
    {
        if (requestUrl == modelUrl || (!string.IsNullOrEmpty(modelUrl) && requestUrl.StartsWith(modelUrl + "/")))
        {
            return true;
        }

        return false;
    }
}