Recently we were requested by a client to create a configurable internal navigation for any Umbraco page that used blocks. That is, a menu with links to anchors where each anchor would be the visual output of a block in the page.
The client wanted to be able to pick which blocks would be included in this menu, as well as their order.
Defining the anchors would be easy - each block has a key, and when rendering a view for a block we always put the string representation of that key as the outer element's id.
So first part was halfway done. All I had to do would be to create a picker to choose from the page's blocks and...
But how do you create a picker that picks blocks from the current page? There's no Umbraco datatype that does that!
So my first thought was that I had to create my own property editor for this. We all know how nasty that can be. But then, I remembered Contentment by Lee Kelleher, an amazing and extremely popular Umbraco plugin that adds useful property editors to the Umbraco back office. Among those there is one called Data List.
The Data List can be configured to use one of the many built-in data sources available, or you can create your own. In our case, I needed to create a custom data source.
Following the instructions that Lee provides here I was able to create my own Data Source for Contentment Data Lists, which - guess what - could get all the blocks from the current page.
Please note that in my implementation I assume the following things that may be different for you:
- I'm only handling documents composed with AbstractPageSections since this is our standard composition when a page has blocks that must be rendered.
- I'm only handling the "Sections" property since this is our standard block editor name.
- I'm composing all blocks with AbstractSectionBlock and assume there is a SectionTitle property (from the composition) there which I use as the title - if this is empty, I'm getting the content type name using the Content Type service.
These are all very easy to adapt to your scenario so don't be scared :)
After you build, you'll see your new data source in the list of available data sources. I used default for the group, so it's going in the "Web" group:
Selecting this as a data source will not give you any preview since there's no page id at that time, but it'll work on the page you need to put it on.
That's it! Just select an Item Picker as your list editor as in the screenshot above and you're ready to go.
So, eventually, when you use it on an actual page, the picker will look something like this:
So now you can select your sections, arrange them in any order and construct that internal menu. Since the value of each option is the key of the block, you can easily relate that with the id of each block in the HTML output as I demonstrated in the start of this post.
Some questions that may come up:
What are those images displayed under the blocks in the background?
This is an evolution of the code you can find in this post on how to create a richer block list view in the Umbraco back office.
How did you get the current page ID?
Lee's feedback to a similar question was very helpful, so I actually found the answer here. It was as simple as checking the query string for the "id" parameter's value.
Isn't it overkill to create a picker that chooses block? Could you not just add a checkbox to each block such as "show in internal menu" or similar and use that to construct the internal menu?
Yes, I could. And in some cases that would be the ideal solution. But the client specifically asked that they should be able to control the order in which menu items are displayed. Also, in a future version, they might want to replace section titles with their own, meaning that the picker would actually be contained into another element type with more properties to cover that. Finally, this is a just an example of how Contentment can help you solve these kinds of problems, and might not be the best use case at hand, but imagine that you can now have a picker allowing you to pick sections from pages - it doesn't even have to be the same page! The scenarios are endless.
I want to use all this in a class library. Can I do that?
Yes. On Nuget there is also the Core version of Contentment which you can use to setup contentment in a class library project.