How to write my own menu widget?

Topics: Core, Writing modules
Mar 20, 2013 at 6:04 PM
Hi, I would like to create an additional widget for Orchard 1.6 Core Menu. Can you please provide me some information or even tell me if there are any modules available that extend Orchard Menu they way I want to do?

The approach that I have follow is by creating a new module, with a driver 'DynMenuWidgetPartDriver', migration class as shown below. I actually copy/paste the core functionality.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Routing;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.Core.Navigation.Models;
using Orchard.Core.Navigation.Services;
using Orchard.Core.Navigation.ViewModels;
using Orchard.Core.Title.Models;
using Orchard.Localization;
using Orchard.UI.Navigation;
using Orchard.Utility.Extensions;
using Orchard.DynamicMenu.Models;

namespace Orchard.DynamicMenu.Drivers
{
    public class DynMenuWidgetPartDriver : ContentPartDriver<MenuWidgetPart>
    {
        private readonly IContentManager _contentManager;
        private readonly INavigationManager _navigationManager;
        private readonly IWorkContextAccessor _workContextAccessor;
        private readonly IMenuService _menuService;

        public DynMenuWidgetPartDriver(
            IContentManager contentManager,
            INavigationManager navigationManager,
            IWorkContextAccessor workContextAccessor,
            IMenuService menuService) {
            _contentManager = contentManager;
            _navigationManager = navigationManager;
            _workContextAccessor = workContextAccessor;
            _menuService = menuService;
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        protected DriverResult Display(MenuWidgetPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_MenuWidget", () =>
            {
                if (part.Menu == null)
                {
                    return null;
                }

                var menu = _menuService.GetMenu(part.Menu.Id);

                if (menu == null)
                {
                    return null;
                }

                var menuName = menu.As<TitlePart>().Title.HtmlClassify();
                var currentCulture = _workContextAccessor.GetContext().CurrentCulture;

                IEnumerable<MenuItem> menuItems = _navigationManager.BuildMenu(menu);

                var localized = new List<MenuItem>();
                foreach (var menuItem in menuItems)
                {
                    // if there is no associated content, it as culture neutral
                    if (menuItem.Content == null)
                    {
                        localized.Add(menuItem);
                    }

                    // if the menu item is culture neutral or of the current culture
                    else if (String.IsNullOrEmpty(menuItem.Culture) || String.Equals(menuItem.Culture, currentCulture, StringComparison.OrdinalIgnoreCase))
                    {
                        localized.Add(menuItem);
                    }
                }

                menuItems = localized;

                var routeData = _workContextAccessor.GetContext().HttpContext.Request.RequestContext.RouteData;

                var selectedPath = NavigationHelper.SetSelectedPath(menuItems, routeData);

                dynamic menuShape = shapeHelper.Menu();

                if (part.Breadcrumb && selectedPath != null)
                {
                    menuItems = selectedPath;
                    foreach (var menuItem in menuItems)
                    {
                        menuItem.Items = Enumerable.Empty<MenuItem>();
                    }

                    // apply level limits to breadcrumb
                    menuItems = menuItems.Skip(part.StartLevel - 1);
                    if (part.Levels > 0)
                    {
                        menuItems = menuItems.Take(part.Levels);
                    }

                    var result = new List<MenuItem>(menuItems);

                    // inject the home page
                    if (part.AddHomePage)
                    {
                        result.Insert(0, new MenuItem
                        {
                            Href = _navigationManager.GetUrl("~/", null),
                            Text = T("Home")
                        });
                    }

                    // inject the current page
                    if (!part.AddCurrentPage)
                    {
                        result.RemoveAt(result.Count - 1);
                    }

                    // prevent the home page to be added as the home page and the current page
                    if (result.Count == 2 && String.Equals(result[0].Href, result[1].Href, StringComparison.OrdinalIgnoreCase))
                    {
                        result.RemoveAt(1);
                    }

                    menuItems = result;

                    menuShape = shapeHelper.Breadcrumb();
                }
                else
                {
                    IEnumerable<MenuItem> topLevelItems = menuItems.ToList();

                    if (part.StartLevel > 1 && selectedPath != null)
                    {
                        // the selected path will return the whole selected hierarchy
                        // intersecting will return the root selected menu item
                        topLevelItems = topLevelItems.Intersect(selectedPath.Where(x => x.Selected)).ToList();
                    }

                    if (topLevelItems.Any())
                    {
                        // apply start level by pushing childrens as top level items
                        int i = 0;
                        for (; i < part.StartLevel - 1; i++)
                        {
                            var temp = new List<MenuItem>();
                            foreach (var menuItem in topLevelItems)
                            {
                                temp.AddRange(menuItem.Items);
                            }
                            topLevelItems = temp;
                        }

                        // apply display level ?
                        if (part.Levels > 0)
                        {
                            var current = topLevelItems.ToList();
                            for (int j = 1; j < part.Levels; j++)
                            {
                                var temp = new List<MenuItem>();
                                foreach (var menuItem in current)
                                {
                                    temp.AddRange(menuItem.Items);
                                }
                                current = temp;
                            }

                            topLevelItems = current;

                            // cut the sub-levels of any selected menu item
                            foreach (var menuItem in topLevelItems)
                            {
                                menuItem.Items = Enumerable.Empty<MenuItem>();
                            }

                        }

                        menuItems = topLevelItems;
                    }
                }


                menuShape.MenuName(menuName);
                NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems);

                return shapeHelper.Parts_MenuWidget(Menu: menuShape);
            });
        }

        protected DriverResult Editor(MenuWidgetPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_MenuWidget_Edit", () =>
            {
                var model = new MenuWidgetViewModel
                {
                    CurrentMenuId = part.Menu == null ? -1 : part.Menu.Id,
                    StartLevel = part.StartLevel,
                    StopLevel = part.Levels,
                    Breadcrumb = part.Breadcrumb,
                    AddCurrentPage = part.AddCurrentPage,
                    AddHomePage = part.AddHomePage,
                    Menus = _menuService.GetMenus(),
                };

                return shapeHelper.EditorTemplate(TemplateName: "Parts.MenuWidget.Edit", Model: model, Prefix: Prefix);
            });
        }

        protected DriverResult Editor(MenuWidgetPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            var model = new MenuWidgetViewModel();

            if (updater.TryUpdateModel(model, Prefix, null, null))
            {
                part.StartLevel = model.StartLevel;
                part.Levels = model.StopLevel;
                part.Breadcrumb = model.Breadcrumb;
                part.AddHomePage = model.AddHomePage;
                part.AddCurrentPage = model.AddCurrentPage;
                part.Menu = _contentManager.Get(model.CurrentMenuId).Record;
            }

            return Editor(part, shapeHelper);
        }
    }
}
public class Migrations : DataMigrationImpl
    {
        public int Create()
        {
            ContentDefinitionManager.AlterTypeDefinition("DynMenuWidget", cfg => cfg
                .WithPart("CommonPart")
                .WithPart("IdentityPart")
                .WithPart("WidgetPart")
                .WithPart("MenuWidgetPart")
                .WithSetting("Stereotype", "Widget")
                );
            return 1;
        }
    }
Mar 21, 2013 at 11:17 AM
I have managed to create a new module, my Driver Editor methods are called with success and a record is created in my new table. The issue is when I try to see how my widget looks on the homepage, instead of my Driver to be called and then my Display method, it calls the Display method from class MenuWidgetPartDriver.
I already check to see what is the issue but I could not find anything. Any ideas what might be wrong?
public class DynMenuWidgetPartDriver : ContentPartDriver<DynMenuWidgetPart>{
        private readonly IContentManager _contentManager;
        private readonly INavigationManager _navigationManager;
        private readonly IWorkContextAccessor _workContextAccessor;
        private readonly IMenuService _menuService;

        public DynMenuWidgetPartDriver(
            IContentManager contentManager,
            INavigationManager navigationManager,
            IWorkContextAccessor workContextAccessor,
            IMenuService menuService) {
            _contentManager = contentManager;
            _navigationManager = navigationManager;
            _workContextAccessor = workContextAccessor;
            _menuService = menuService;
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }

        protected override string Prefix

        protected override DriverResult Display(DynMenuWidgetPart part, string displayType, dynamic shapeHelper)
      
    protected override DriverResult Editor(DynMenuWidgetPart part, dynamic shapeHelper)

        protected override DriverResult Editor(DynMenuWidgetPart part, IUpdateModel updater, dynamic shapeHelper)

    }
Mar 22, 2013 at 9:14 AM
Any ideas why Display method in Driver is not called but Editor it is.
Developer
Mar 28, 2013 at 12:07 AM
Yes, you're not overriding the Display method and you're accepting the wrong type.