Better Menus Module Idea

Topics: General, Writing modules
Jun 1, 2011 at 5:19 PM
Edited Jun 1, 2011 at 5:22 PM

Hi there,

I've just recently began looking into Orchard and decided to port an old ASP.NET MVC web site to Orchard for learning purposes. One of the first things I am unsatisfied with is the navigation editor. I have a requirement for:

1) Hierarchical Menu structure, e.g:

Home
Services
   Aesthetic
   Periodontics
   ...
Contact Us
   Book an Appointment
   Location

2) Since my site is multi-cultural I need that menu to be in multiple languages, possibly with different links for some of the menu items.

So I am thinking that I want to implement a better menu module for Orchard to replace the default one and have created a mockup for discussion:

A menu consists of menu items that can have zero-or-many children menu items and has a:

1) Name

2) Localization/Culture associated with it. This shoud I think be a LocalizationPart somehow so that we can pick the menu based on which is the current culture. Note that localized menus will have the same name but a different culture. I suspect the localization dropdown should be hidden if the Localization Module is disabled.

Admin Interaction will be completely AJAX driven:

1) When you click the Add Menu/Add Sub Menu button a new menu item will be inserted automatically using Ajax underneath the current one

2) Menu Items can be dragged and dropped up and down in the current menu level (e.g. using JQuery UI Sortable or knockout.js) - notice the drag handle before the menu item.

To be used the menu will be exposed as a widget. The widget which will be configured with a menu name and will pick up the current culture and display the correct menu.

I would love to hear your feedback!

I also have a question. To implement the menu item AJAX collection editing and validation I will need to use a custom controller and actions rather than a plain ContentPart driver from what I've seen so far. Where can I look at a good example of how to do that?

P.S: I've already looked the Taxonomies module, but while it allows to organize content in hierarchical categories I couldn't find a way to use it as a hierarchical menu rather then a hierarchical content list drill down. I've also looked at the Advanced Menu module, which doesn't support localization, appears to be very flaky as well and doesn't offer very nice and quick UI to quickly create menus - still very useful as a source of information on how to replace the default navigation provider ,etc. 

Jun 1, 2011 at 6:12 PM
Edited Jun 1, 2011 at 6:17 PM

This is the kind of structure (and eventually UI) I'm aiming for in Science Project (specifically the Cartography module which provides a hierarchical menu). With the next iteration of the project I wanted to start adding AJAX and drag'n'drop behaviour - which would allow sorting by dragging, but also adding to the menu from any page just by dropping any content item or link onto it. At the moment it's just a hierarchical menu which is defined by creating relationships between items. I was already planning to have named menus; and adding localization to it should be pretty straightforward - there are enough hooks in the system that this could be a separate module/feature which users can enable if they want (and if they have the Localization module of course). I could certainly use some help - there are a lot of other aspects to the project so my time is very divided!

Jun 1, 2011 at 6:16 PM

BTW; regarding your question on AJAX. Yes you'd need a custom controller to serve partial templates. A ContentPart driver just supplies shapes to render a content item. But I've basically worked out a way to add AJAX wrappers to shapes, and provide some generic controllers that can be used for most types of AJAX operation. I started implementing it experimentally and the concept is good, but I'm leaving it to one side until I've got a bunch of other bits finished.

Jun 1, 2011 at 6:26 PM
randompete wrote:

This is the kind of structure (and eventually UI) I'm aiming for in Science Project (specifically the Cartography module which provides a hierarchical menu). With the next iteration of the project I wanted to start adding AJAX and drag'n'drop behaviour - which would allow sorting by dragging, but also adding to the menu from any page just by dropping any content item or link onto it. At the moment it's just a hierarchical menu which is defined by creating relationships between items. I was already planning to have named menus; and adding localization to it should be pretty straightforward - there are enough hooks in the system that this could be a separate module/feature which users can enable if they want (and if they have the Localization module of course). I could certainly use some help - there are a lot of other aspects to the project so my time is very divided!

Sorry, but I am really confused what this Cartography module actually does and if that's related to a simple hierarchy menu module.

Jun 1, 2011 at 6:28 PM
randompete wrote:

BTW; regarding your question on AJAX. Yes you'd need a custom controller to serve partial templates. A ContentPart driver just supplies shapes to render a content item. But I've basically worked out a way to add AJAX wrappers to shapes, and provide some generic controllers that can be used for most types of AJAX operation. I started implementing it experimentally and the concept is good, but I'm leaving it to one side until I've got a bunch of other bits finished.

I don't want an AJAX-loaded shape. I want to know if I can render a shape that internally uses a custom controller for custom business logic and which can also take in the Model when I hit the Save button. I want to look at some module that uses similar technique to understand how it fits in the context of orchard.

Jun 1, 2011 at 7:34 PM
Edited Jun 1, 2011 at 7:37 PM

I think I got most of my questions answered from looking at the .Navigation Code in .Core code . I am just still a bit puzzled in regards to custom AdminController actions and views and the ContentPartDrivers. The driver has a TryUpdateModel, but then I know that there is a transaction/session per HttpRequest, so any changes to the Part properties (which wrap the Record properties) in the controller action will be applied at the end of the request unless the transaction is cancelled. So when does the ContentPartDriver kick in? It would be great if someone can explain how does MVC CRUD compare with Orchard MVC Crud and the driver. Is the driver only used to display default editor/display for e.g. some of the "Edit" links that are present in the Admin interface for a content item, so if I am doing a custom view with a custom view model (of a ContentPart) I should not worry about the Driver at all and just update the ContentPart properties from my custom view ones?

Jun 2, 2011 at 1:49 AM

Ok - in order:

- Cartography is a hierarchical menu module. I built it as an example of how you can use Mechanics to create many-to-many relationships for multiple purposes. At the moment the UI is drop-down menus when you're actually creating a page; so you can make a page be a top-level menu item, or make it a child menu of another page, etc. It's called Cartography because it's defining a map of the site and using that as a menu. But the system is very flexible so localization and named menus can easily be added in.

- What you're asking for with shapes sounds a lot like my Origami module which is also part of Science Project (http://scienceproject.codeplex.com). I use it to build all the custom UIs for Mechanics where you have a variety of different models and editors (some nested). I've implemented a custom ModelDriver system which is very similar to ContentPartDrivers but can be applied to any model object you like, not just content parts. So it has separate logic which you can run on display/edit/update, as well as some other hooks. Unfortunately I haven't documented it yet, it's just used heavily in Mechanics. What I'm saying about AJAX is that eventually I'll also be supporting that in these scenarios, by wrapping shapes (which are a core Orchard concept, basically everything on the page is built out of shapes which are just little fragments of template in .cshtml files or in code).

Your third question is a bit more complicated. But basically drivers are only used when Content Items are displayed, or editors displayed, or when the POST is received and so the models are updated in the editor and logic is handled there. On some other pages (i.e. not content items, for instance the registration page) you just have ordinary MVC views. Orchard takes care of applying a theme to your view ... it's just MVC. So drivers are just a specialised way of contributing to content items. If you're making a custom view using a controller, you don't need drivers or parts.

Jun 2, 2011 at 12:30 PM

- Cartography is a hierarchical menu module. I built it as an example of how you can use Mechanics to create many-to-many relationships for multiple purposes. At the moment the UI is drop-down menus when you're actually creating a page; so you can make a page be a top-level menu item, or make it a child menu of another page, etc. It's called Cartography because it's defining a map of the site and using that as a menu. But the system is very flexible so localization and named menus can easily be added in.

Thanks for the info. I looked it up and I must admit that the terminology/naming (Cartography, Mechanics, Sockets, Plumbing, Paperclips, etc, etc) is utterly confusing in a technological/dev context. I tried to install the Cartography  module in my sandbox and now my Modules/Gallery is broken ("Error loading extensions from gallery source 'Orchard Gallery'. The operation has timed out.").

- What you're asking for with shapes sounds a lot like my Origami module which is also part of Science Project (http://scienceproject.codeplex.com). I use it to build all the custom UIs for Mechanics where you have a variety of different models and editors (some nested). I've implemented a custom ModelDriver system which is very similar to ContentPartDrivers but can be applied to any model object you like, not just content parts. So it has separate logic which you can run on display/edit/update, as well as some other hooks. Unfortunately I haven't documented it yet, it's just used heavily in Mechanics. What I'm saying about AJAX is that eventually I'll also be supporting that in these scenarios, by wrapping shapes (which are a core Orchard concept, basically everything on the page is built out of shapes which are just little fragments of template in .cshtml files or in code).

Your third question is a bit more complicated. But basically drivers are only used when Content Items are displayed, or editors displayed, or when the POST is received and so the models are updated in the editor and logic is handled there. On some other pages (i.e. not content items, for instance the registration page) you just have ordinary MVC views. Orchard takes care of applying a theme to your view ... it's just MVC. So drivers are just a specialised way of contributing to content items. If you're making a custom view using a controller, you don't need drivers or parts.

After posting last night I spend some time looking through the Orchard source code and I think I understand a bit better now how the editing part works.

- When I am adding a new custom admin panel menu item on the left (such as "New Hierarchical Menu") it's driven by a standard ASP.NET MVC controller that returns normal views and handles POSTs, etc, but has the [Themed] attribute applied. This works fine for either models that derive from ContentItem or ContentPart as well as such that don't (custom non-orchard-content models).

- ContentParts/ContentItems do not use standard MVC controllers - instead they use a Driver/Handler to handle the update/display, but still use Razor Views. However the pattern seems pretty similar, because I see that Display/Editor return a DriverResult which probably subclasses ViewResult (haven't checked). Also does sortof makes me think that in the Razor View for the editor of the content part I can still use something similar to Url.Action() and a custom controller to add some extra dynamic behavior when editing a content part.

It's quite interesting that one can combine both. E.g I can have a MenuItemPart which is attached to a content item and also have a MenuItem non-content model & record which simply have a string Url property. Then I can query the contentmanger for contentitems that have a  MenuItemPart and combine that with non-MenuItem parts. Can't wait to play around with that! :)

Jun 2, 2011 at 3:16 PM

I'll have to investigate why that might be broken - firstly I'm guessing you didn't install Origami and Mechanics first, which are both dependencies. But when I tested that scenario, it didn't break the gallery - there was just an error installing the module and I could then carry on installing the dependencies.

I'm in two minds about the naming scheme. On one hand I realise it might look utterly ridiculous at first glance. On the other hand, I was trying to keep concepts distinct and memorable; once you get used to them it's way easier. e.g. Paperclips clip shapes to zones. Sockets are for having Connectors plugged in. Plumbing because I wanted a name distinct from Routing so as not to confuse terminologies with existing Routing mechanisms. There's already a module called Advanced Menu / Hierarchical Menu, so I wasn't going to call it "Yet another menu" :) The thing is that all the projects are named after sciences of a kind; all the features are components of that science. So Sockets, Plumbing, Paperclips are all mechanical devices and that's why they're in the Mechanics project...

Regarding your description of part editing I should just correct a couple of things;

- You said "models that derive from ContentItem or ContentPart". That's not the case. The controllers work for normal models, or shapes generated from a shape factory. You can't have a model derived from ContentItem or ContentPart. A ContentItem is just a collection of ContentParts. Then ContentManager has some Build methods which build a shape from a ContentItem. This shape is composed of a number of Razor views, which each have their own model that might be a content part, a viewmodel generated in a driver, or a dynamic object.

- ContentItems do use an MVC controller - it's called ItemController and it calls ContentManager methods to build the display shapes and return them in a ShapeResult.

 

Jun 15, 2011 at 5:33 PM

Yes, I'm investigating Orchard for our web business, and the current Navigation management was certainly a turn off.  Are you going to re-order a site numerically by hand like that if you have a site with 1,000 pages?  Even 50 pages would be a chore.  That's our sales dept's complaint with Wordpress right now, but Orchard looks even more clunky.

Drag-and-drop navigation management seems like a "must have" for adoption.

Jun 15, 2011 at 6:30 PM

Well ... Orchard's core features are designed more around the "standard case" of a simple blog or business card-style site with perhaps 5-10 pages. For that scenario a full-blown drag-and-drop system would be largely unnecessary.

Due to its modular design it can be extended to handle much more complex situations. But I think if you have 1,000 pages then you don't really want them all in the top-bar navigation - it would seriously slow down page load times. You'd want a focussed selection there, and then maybe an expandable LHS category menu. Which would require a supplementary module in any instance.

My Cartography module takes a different approach - each time you publish a page you can select its parent menu pages and / or child menu pages, and / or whether you want it to appear as a top-level menu. Menus will be sorted alphabetically by default, or there's a Sequence field to override ordering if you ever need to. Typically I wouldn't imagine a sub-menu having more than 10-20 items, more than that would be equally as hard for users to navigate as it was for you to manage; and entering sequence values for 10-20 items isn't really hard if you absolutely have to specify the position of every single menu item. Most of the time you surely just want them alphabetised anyway?

AJAX menu building will come later; I'm aiming for that with v1.1 of Science Project - right now I'm just about to hit 1.0RC.

Coordinator
Jun 15, 2011 at 6:48 PM

Also, with this kind of scale, Taxonomies seem to be a must. http://orchardproject.net/gallery/List/Modules/Orchard.Module.Contrib.Taxonomies

Jun 15, 2011 at 7:04 PM

In most of our sites, we do a standard top bar navigation and contextual left-side navigation.  That is, the top navigation bar shows only specially selected "main navigation" parent nodes.  (You might have a top level page that doesn't show in the main navigation, of course, such as a sitemap page.)

The left side navigation tree changes based on your selection.  Whatever top-level nav is selected determines which part of the sub-tree is rendered.  Similarly, selecting a second-level nav item renders the third-level items underneath on post-back.

This is a pretty standard way of laying out navigation, so I think it's certainly something to build in.  Designing a CMS for only 5-10 pages is silly.  If I have only 5 pages, I don't need a CMS at all, do I?

Jun 15, 2011 at 8:19 PM

A CMS is a very convenient way for a web developer to manage LOTS of small client websites that only have 5-10 pages each. So no, it's not silly at all :)  Plus you get blogs as well - you'll post lots of pages on that, but you only want them to surface through the blog navigation and tags UI, not through any menus.

In any case, Orchard's navigation system is built exactly for the "main navigation" scenario you describe - i.e. the cherry-picked pages that you want in the main menu bar.

For the "contextual left-side navigation", that's something I want to achieve with my Mechanics module. Actually most of the requirements to do it are in place already, just needs a bit of tweaking and wiring up - I'll be packaging it as one of my "Theory" projects that provide implementations of common website mechanisms using the other modules.