Output of a list of objects within a view with specific routes
Hello everyone.
What I need (Umbraco 13):
I would like to display a list of search results when calling the following URLs.
/{culture}/discipline/{discipline}
/{culture}/disziplin/{discipline}
/{culture}/topic/{topic}
/{culture}/thema/{topic}
The URLs are therefore also language-dependent, in this case English and German, but more could be added in the future.
A distinction is also made between topic and discipline. The resulting list is the same, e.g. List of SearchResult objects. Only the logic within is different.
The view should have the master.cshtml defined as the layout, which currently contains the following line:
I have now tried to define custom routes and have them point to a controller, which has 2 actions, which in turn returns the view with the list of search results. But I always got the error message that the model cannot be bound because it is not IPublishedContent. At least when I used the layout. Without the layout it worked without any problems.
I didn't create any document types or content nodes either, as I don't think they help me very much here.
But none of it really works.
Has anyone already done something like this? I don't need a full implementation of course, just some guidance. A direction that leads me to the solution. Do I need to create custom routes? Do I have to create document types and solve this via route hijacking? Is there an alternative, perhaps without document types?
UPDATE
My current implementation (quick and dirty, since I'm only evaluating Umbraco currently to see if it fits our needs):
To use a specific custom view model, the @inherits directive will need to be updated to reference your custom model using the Umbraco.Cms.Web.Common.Views.UmbracoViewPage
It should be pretty straightforward, I have made a search like it in U13 with a controller and IUmbracoContextAccessor. But I havent gotten so far to output it as a view, since I was using it as a JSON API.
I'm pretty sure I've already tried that. But I'll try it again now, just to be on the safe side. I just have to install .NET 8 (I'm no longer in the office) and will let you know as soon as I know anything.
Theoretically, I do that too. At least in my controller. The content list is of type IPublishedContent. And I think that if I only pass one of them to the view, it will work. But after I take the IPublishedContent and wrap it in a ViewModel (simple class, only with the properties Name and URL), it is no longer IPublishedContent.
ModelBindingException: Cannot bind source type System.Collections.Generic.List`1[[Project.Umbraco.Models.SearchResultByTagModel, Project.Umbraco, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] to model type Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent.
Same when I create a new class, containing a property with the list and use that instead. Same error (just with the other class).
Yes, I'm going to continue to dig through the docs too. Thanks for your help!
Something else that might be relevant: In the docs you sent, it says below the line you copied:
<> contains a model generated for each Document Type to give strongly typed access to the Document Type properties in the template view.
But I am not using a Document Type here. So maybe the complete UmbracoPageView is wrong and it has to be done differently.
In most cases you will need your custom model to build upon the underlying existing PublishedContent model for the page. This can be achieved by making your custom model inherit from a special base class called PublishedContentWrapped
PublishedContentWrapped will take care of populating all the usual underlying Umbraco properties and means the @Model. syntax will continue to work in the layouts used by your template.
"Upon the underlying existing PublishedContent".
But the problem is that my list (or class) has no underlying PublishedContent.
I'm afraid that the problem starts before that. If you set such a custom route (as in my Composer), then there is the method "ForUmbracoPage". And the FindContent method should also return a PublishedContent.
So that already implies that this is actually about content, not random classes. Perhaps the complete creation of the custom routes is a waste of time because I generally don't work with document types here?
Perhaps I need to define the custom routes differently after all?
And for your information: "search" is actually the wrong word here. I have a search that also works, with AJAX and calling an API. This is just about "navigation". I simply want to list all content nodes of a document type that have a certain tag (either in the discipline or in the topic group). But under the routes mentioned above and without having to create document types and content nodes. Otherwise I would have to create at least one new content node for each language and each category, hijack its URL and integrate my controller.
yes, it was in my master.cshtml..
I cant pass ipublished content to the view at the moment, I can only pass on a hardcoded viewmodel..
But the mvc is ok.
Interesting. I have now removed all the UmbracoPageViews (from index and master), but I still get the same exception...
ModelBindingException: Cannot bind source type System.Collections.Generic.List`1[[Project.Umbraco.Models.SearchResultByTagModel, Project.Umbraco, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] to model type Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent.
Hey, yes, I downloaded it and it works.
And I think the reason why it works for you is because your "ClubsPage" is a Document Type/Content Node. Meaning, you are in the context of a "Published Content", because the ClubsPage page itself is published content.
The problem for me is when I now compare ClubsPage with my "Discipline page", for example: My "Page" is not a Document Type and not a Content Node. But ClubsPage is. I try to do all this without creating document types and content nodes by working with custom routes and views.
Because otherwise, in the scenario above, 4 content nodes would already have to be created, namely for /de/disziplin, /en/discipline, /de/thema and /en/topic.
And if several languages are added, I have to create even more. And then hijack the URL for each content node and set up a separate controller.
Umbraco is mainly expecting to be used as a Cms, so you define Document Types that define different types of pages for a website and which types of pages are allowed to be created underneath...
When a page is published, then it's custom properties and core properties are represented by an IPublishedContent object and it is automatically routed by the position of the item in tree, so if you create a Homepage doctype and allow it to be created at root, and allow Standard page doc types underneath, then in the backoffice you can create instances of those page eg about us page and this would be automatically routed via /about-us to the selected template for the standard page doc type...
... I know you don't want to do this but it gives a bit of context...
So on a basic level there is no need to add any custom routing rules or controllers...
The reason I mention this is that for all requests through Umbraco there is the concept of there being a current IPublishedContent tied to the request... And by default this would be the published page in umbraco.
But if you want to mix other external content to a page or create your own view models then you can create a controller that must inherit from Render Controller and follow the naming convention DocTypeAliasController
And then without any Routing rules, any request to a page url based on that document type will be hijacked by the custom controller, you'll have reference to the IPublishedContent for the requested page, but up to you to build a view model and send it to a view...
.. The key thing is you don't need a controller for each Url, just for the underlying Document Type, it will hijack all urs to pages of that type.
... But the flexibility is also there to make your IPublishedContent object come outside of Umbraco...
Which is where you can map your own custom vitual routes, but you have to implement Find Content and either pick a dummy content item published in Umbraco to associate with the request or construct your own IPublishedContent item, which is where PublishedContentWrapped comes in as a starting point.
This is good if you have fixed url patterns that won't change... But if you want something more flexible then you can create an implementation of IContentFinder, and add it to Umbraco incoming request pipeline... Here you can write your own custom rules for mapping a request to umbraco...
... It's hard to explain...
But if you had an external library of products that you wanted to show in umbraco you could map a virtual route to /products/{id}
But you'd be hardcoding that route...
But if you used an IContentFinder you could map a request from /category1/product-id
And the category could be edited in Umbraco...
Not sure I've sold the benefits there
... Additionally when you create a site in Umbraco you can create different languages, and allow your Document Types to vary by culture, then by default umbraco routes the content on a unique url for each language culture...
But you can completely ignore the cms and map mvc routes through, and associate dummy Ipublishedcontent, but it might be worth exploring having a Document Type called something like Section and allow it to vary by culture and create two pages called Discipline and Topic, and see what Umbraco gives you around this setup in terms of automatic routing and hijacking, in future if you have new language you just add it to umbraco, and it all automatically routes, another type of Theme, just add a new Section Content item..
First of all, I would like to apologise for the late reply and thank you for your detailed answer!
I understand that Umbraco works with document types and that you should always have some in the best case.
I also know that these can vary in the culture. But it's true, I haven't given the impression in my previous answers.
Nevertheless, I had hoped to be able to do this without them (without much effort).
But anyway, I have now created document types (for discipline and topic) and content nodes based on these.
It works so far.
I now have a menu with all disciplines and topics. If I click on a discipline, I am shown topics. If I click on a topic, disciplines are shown.
So far, so good, everything is working. I still have to change the document types to the same template and the like, but that should work.
But now I still need another URL, namely:
/{culture}/topic/{topic}/{discipline?}
So I have created a TopicsController (derived from RenderController), with an Index and a Discipline Action. I use the HttpGet attribute to tell the Discipline action to listen to the above URL.
My problem is that this action is never called and I don't understand why.
Is the URL defined here simply ignored by Umbraco? Because when I call "/en-us/topic/tutorials", the action is called. However, if I then call "/en-us/topic/tutorials/architecture", the action is not called.
Also, if I do this with the HttpGet attribute, then I have to hardcode the URLs with the respective languages again, which is not the point. Of course, I could now go and declare all disciplines with a new document type within my topics again. But I have ~10 disciplines and ~8 topics. If I have to define all disciplines for each topic, we're already talking about 80 new content nodes here alone. +10 for the menu.
What do I actually want to achieve with all this?
Simply put, a menu structure based on 2 factors: discipline and topic. If both discipline and topic are specified, I want to display all the articles that are in them. If only one of them is specified, the list of the missing one comes up for selection.
Do you have any suggestion how I could achieve this? Any approach, an idea?
Is this the point where I would need the IContentFinder? It would make no sense to me. Ultimately, I just want to "extend" the existing URL by simply attaching another URL segment.
My TopicsController:
public class TopicsController(
ILogger<RenderController> logger,
ICompositeViewEngine compositeViewEngine,
IUmbracoContextAccessor umbracoContextAccessor) : RenderController(logger, compositeViewEngine,
umbracoContextAccessor)
{
public override IActionResult Index()
{
return base.Index();
}
[HttpGet("/{culture}/thema/{topic}/{discipline?}")]
public IActionResult Topic(string culture, string topic, string? discipline)
{
return CurrentTemplate(CurrentPage);
}
}
Yes, it wasn't so much that you need to create DocTypes, but in your evaluation, if you did, you would get a better grok on how the routing works out of the box and therefore how you can alter it...
There is so much I don't know about what you are ultimately trying to do :-P, even though you have explained! but in answer to your question:
In terms of this observation:
So I have created a TopicsController (derived from RenderController),
with an Index and a Discipline Action. I use the HttpGet attribute to
tell the Discipline action to listen to the above URL.
My problem is that this action is never called and I don't understand
why
This is because when you inherit from RenderController to route hijack the convention is that your Action will match the alias of the template you are rendering this page. In Umbraco a Document Type can have multiple templates and you can create an Action to handle each one.
Is this the point where I would need the IContentFinder? It would make
no sense to me. Ultimately, I just want to "extend" the existing URL
by simply attaching another URL segment.
.Yes, this is that point!
When a Url comes into Umbraco, there are a series of rules executed, in order, to try and match the incoming Url and match it to a published content item. These rules are created by implementing an interface called IContentFinder and then adding it inot the collection of ContentFinders in precedence order.
Out of the box, Umbraco has an IContentFinder called ContentFinderByUrlAlias
which is currently responsible for routing your requests for your published topics and disciplines.
The match happens in TryFindContent implementation that receives the raw incoming request and then queries the Umbraco published cache to find a matching segment in the position of the url.... but you can create your own and the rules can be whatever you need.
so if you create a ContentFinderByVirtualTopic, and add it at the front of the content finder collection queue, then you can check the incoming URL
/en-us/topic/tutorials/architecture
and see if the first bit /en-us/topic/ matches the published content item,
if so you know it's a valid Url that your ContentFinder should handle, and if not you return false, to allow the next ContentFinder in the queue to have a go.
If it's one of your Urls, you can then read the rest of the Url and retrifve your architecture tutorials from your external system (I presume that's what you are trying to do?) and then if you don't find any, return 'false' the request will eventually fall through the other content finders and 404, but if you have some, then you can use the PublishedContentWrapped class to create an IPublishedContent item and populate it with your external Article Data, the IPublishedContent item has an underlying DocumentType and a Template, so you can set that here... (in this scenario I'll normally have a dummy DocumentType called VirtualPage or something, and I'll usually have a content item of the type published to use as a base for my virtual content)
Anyway if your ContentFinder returns true, that's it, the virtual content gets sent through to Umbraco and you can 'hijack' it by creating a RenderController that matches it's underlying VirtualDocumentType...
it's a very flexible way of extending Umbraco routing without having to 'hardcode the routes in an MVC way' and shows you can sort of do anything....
but it is also totally possible to create an implementation of IVirtualPageController and 'map' an MVC route to it - but in doing so you have to specify 'what' is the IPublishedContent context if for the route.
So now you have published content you should be able to follow this example:
to map your custom Urls to a specific a Controller and implement FindContent to return one of your published pages as the IPublishedContent context for the request.
anyway hope that give you some more to play around with!
In fact, I was just about to try that again with IVirtualPageController and Co, now that I have a DocType for it.
That I can create an action for each template is also good to know, thank you! I'll have to play around with that to see what I can do with it.
Regarding the article data: No, it doesn't actually come from an external system. I have installed the Umbraco.TheStarterKit, which contains a blog with posts. I just want to be able to assign several "topics" and "disciplines" to these posts. Then I would simply like to have a menu (in the frontend), divided into disciplines and topics, which shows the user the relevant posts if both a discipline and a topic have been selected. If only one discipline has been selected, a list of topics should appear so that one can be selected. And vice versa. And as soon as both have been selected, a list of posts should be displayed in the respective discipline and topic.
Thanks again for the answer and I'll get back to you as soon as I'm finished (hopefully with good news)!
Output of a list of objects within a view with specific routes
Hello everyone.
What I need (Umbraco 13):
I would like to display a list of search results when calling the following URLs.
The URLs are therefore also language-dependent, in this case English and German, but more could be added in the future.
A distinction is also made between topic and discipline. The resulting list is the same, e.g. List of SearchResult objects. Only the logic within is different.
The view should have the master.cshtml defined as the layout, which currently contains the following line:
Means I have to return content somehow.
I have now tried to define custom routes and have them point to a controller, which has 2 actions, which in turn returns the view with the list of search results. But I always got the error message that the model cannot be bound because it is not IPublishedContent. At least when I used the layout. Without the layout it worked without any problems.
I didn't create any document types or content nodes either, as I don't think they help me very much here. But none of it really works.
Has anyone already done something like this? I don't need a full implementation of course, just some guidance. A direction that leads me to the solution. Do I need to create custom routes? Do I have to create document types and solve this via route hijacking? Is there an alternative, perhaps without document types?
UPDATE
My current implementation (quick and dirty, since I'm only evaluating Umbraco currently to see if it fits our needs):
Composer:
Controller:
index.cshtml (of my controller):
master.cshtml (shortened):
Can you post your controller functions?
Updated my initial post
I am not sure if you are passing the model.. there is something in the docs here: https://docs.umbraco.com/umbraco-cms/reference/routing/custom-controllers
To use a specific custom view model, the @inherits directive will need to be updated to reference your custom model using the Umbraco.Cms.Web.Common.Views.UmbracoViewPage
It should be pretty straightforward, I have made a search like it in U13 with a controller and IUmbracoContextAccessor. But I havent gotten so far to output it as a view, since I was using it as a JSON API.
I'm pretty sure I've already tried that. But I'll try it again now, just to be on the safe side. I just have to install .NET 8 (I'm no longer in the office) and will let you know as soon as I know anything.
Yes try to check out those docs, there are many examples of passing models to views. If you dont get any errors here:
I cant see why the data should not be valid, and there might be some MVC issue more than IPublishedContent issue.
In general, I also assume that there should be no error here. If I don't use my master.cshtml as the layout for my index.cshtml, then it also works.
But as soon as I use the master.cshtml, I get the error message. I can copy it as soon as I have installed it and tested your suggestion.
But I don't do anything special in the master.cshtml. And the only reason why I need UmbracoViewPage there is to access the dictionary.
I just found this in my code, I am actually forcing the result to be IPublishedContent here for it to work:
Theoretically, I do that too. At least in my controller. The content list is of type IPublishedContent. And I think that if I only pass one of them to the view, it will work. But after I take the IPublishedContent and wrap it in a ViewModel (simple class, only with the properties Name and URL), it is no longer IPublishedContent.
I now have this in my index.cshtml:
And get the following exception:
Same when I create a new class, containing a property with the list and use that instead. Same error (just with the other class).
yeah, I am trying to get it to work, but get a lot of strange errors.. I have to read those docs a while.
Yes, I'm going to continue to dig through the docs too. Thanks for your help! Something else that might be relevant: In the docs you sent, it says below the line you copied:
But I am not using a Document Type here. So maybe the complete UmbracoPageView is wrong and it has to be done differently.
Yes there is also something here:
In most cases you will need your custom model to build upon the underlying existing PublishedContent model for the page. This can be achieved by making your custom model inherit from a special base class called PublishedContentWrapped
PublishedContentWrapped will take care of populating all the usual underlying Umbraco properties and means the @Model. syntax will continue to work in the layouts used by your template.
"Upon the underlying existing PublishedContent". But the problem is that my list (or class) has no underlying PublishedContent.
I'm afraid that the problem starts before that. If you set such a custom route (as in my Composer), then there is the method "ForUmbracoPage". And the FindContent method should also return a PublishedContent.
So that already implies that this is actually about content, not random classes. Perhaps the complete creation of the custom routes is a waste of time because I generally don't work with document types here?
Perhaps I need to define the custom routes differently after all?
And for your information: "search" is actually the wrong word here. I have a search that also works, with AJAX and calling an API. This is just about "navigation". I simply want to list all content nodes of a document type that have a certain tag (either in the discipline or in the topic group). But under the routes mentioned above and without having to create document types and content nodes. Otherwise I would have to create at least one new content node for each language and each category, hijack its URL and integrate my controller.
g..d.. I was pulling my hair out. I got it to work, its because of this line in master.cshtml
I think the view try to connect to that inherit and then returns that it doesnt exist.
edit: Sorry, I mean the viewmodel doesnt exist. Anyway, when I deleted that line, the mvc worked as it should with Layout.
Hey, sry for the late reply. But I don't have this line ? So, can you tell me where it was initially coming from ?
yes, it was in my master.cshtml.. I cant pass ipublished content to the view at the moment, I can only pass on a hardcoded viewmodel.. But the mvc is ok.
You have it here I think :
Interesting. I have now removed all the UmbracoPageViews (from index and master), but I still get the same exception...
ModelBindingException: Cannot bind source type System.Collections.Generic.List`1[[Project.Umbraco.Models.SearchResultByTagModel, Project.Umbraco, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] to model type Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent.
Let me clean up, then Ill upload to github. I have no idea how it works, but it returns Ipublished content to the view with a viewmodel now.
Ok, thank you for the effort!
ok, see if you can fetch it.
email: [email protected] pass: 1234567890
the output is ~/clubs/
https://github.com/pbldk/umbracothirteenipublishview
I have just uploaded all of it..
Hey, yes, I downloaded it and it works. And I think the reason why it works for you is because your "ClubsPage" is a Document Type/Content Node. Meaning, you are in the context of a "Published Content", because the ClubsPage page itself is published content.
The problem for me is when I now compare ClubsPage with my "Discipline page", for example: My "Page" is not a Document Type and not a Content Node. But ClubsPage is. I try to do all this without creating document types and content nodes by working with custom routes and views.
Because otherwise, in the scenario above, 4 content nodes would already have to be created, namely for /de/disziplin, /en/discipline, /de/thema and /en/topic.
And if several languages are added, I have to create even more. And then hijack the URL for each content node and set up a separate controller.
I am not sure you have to use ipublished content and make content, you should be able to just pass a viewmodel from anything.
There is also some docs about custom routing here:
https://docs.umbraco.com/umbraco-cms/reference/routing/custom-routes
I am sure it can be done. I have to try it out.
Hi Dominic
Umbraco is mainly expecting to be used as a Cms, so you define Document Types that define different types of pages for a website and which types of pages are allowed to be created underneath...
When a page is published, then it's custom properties and core properties are represented by an IPublishedContent object and it is automatically routed by the position of the item in tree, so if you create a Homepage doctype and allow it to be created at root, and allow Standard page doc types underneath, then in the backoffice you can create instances of those page eg about us page and this would be automatically routed via /about-us to the selected template for the standard page doc type...
... I know you don't want to do this but it gives a bit of context...
So on a basic level there is no need to add any custom routing rules or controllers...
The reason I mention this is that for all requests through Umbraco there is the concept of there being a current IPublishedContent tied to the request... And by default this would be the published page in umbraco.
But if you want to mix other external content to a page or create your own view models then you can create a controller that must inherit from Render Controller and follow the naming convention DocTypeAliasController
And then without any Routing rules, any request to a page url based on that document type will be hijacked by the custom controller, you'll have reference to the IPublishedContent for the requested page, but up to you to build a view model and send it to a view...
.. The key thing is you don't need a controller for each Url, just for the underlying Document Type, it will hijack all urs to pages of that type.
... But the flexibility is also there to make your IPublishedContent object come outside of Umbraco...
Which is where you can map your own custom vitual routes, but you have to implement Find Content and either pick a dummy content item published in Umbraco to associate with the request or construct your own IPublishedContent item, which is where PublishedContentWrapped comes in as a starting point.
This is good if you have fixed url patterns that won't change... But if you want something more flexible then you can create an implementation of IContentFinder, and add it to Umbraco incoming request pipeline... Here you can write your own custom rules for mapping a request to umbraco...
... It's hard to explain...
But if you had an external library of products that you wanted to show in umbraco you could map a virtual route to /products/{id}
But you'd be hardcoding that route...
But if you used an IContentFinder you could map a request from /category1/product-id And the category could be edited in Umbraco...
Not sure I've sold the benefits there
... Additionally when you create a site in Umbraco you can create different languages, and allow your Document Types to vary by culture, then by default umbraco routes the content on a unique url for each language culture...
But you can completely ignore the cms and map mvc routes through, and associate dummy Ipublishedcontent, but it might be worth exploring having a Document Type called something like Section and allow it to vary by culture and create two pages called Discipline and Topic, and see what Umbraco gives you around this setup in terms of automatic routing and hijacking, in future if you have new language you just add it to umbraco, and it all automatically routes, another type of Theme, just add a new Section Content item..
Good luck!
Marc
Hello Marc!
First of all, I would like to apologise for the late reply and thank you for your detailed answer!
I understand that Umbraco works with document types and that you should always have some in the best case. I also know that these can vary in the culture. But it's true, I haven't given the impression in my previous answers. Nevertheless, I had hoped to be able to do this without them (without much effort).
But anyway, I have now created document types (for discipline and topic) and content nodes based on these. It works so far.
I now have a menu with all disciplines and topics. If I click on a discipline, I am shown topics. If I click on a topic, disciplines are shown.
So far, so good, everything is working. I still have to change the document types to the same template and the like, but that should work.
But now I still need another URL, namely:
So I have created a TopicsController (derived from RenderController), with an Index and a Discipline Action. I use the HttpGet attribute to tell the Discipline action to listen to the above URL.
My problem is that this action is never called and I don't understand why.
Is the URL defined here simply ignored by Umbraco? Because when I call "/en-us/topic/tutorials", the action is called. However, if I then call "/en-us/topic/tutorials/architecture", the action is not called.
Also, if I do this with the HttpGet attribute, then I have to hardcode the URLs with the respective languages again, which is not the point. Of course, I could now go and declare all disciplines with a new document type within my topics again. But I have ~10 disciplines and ~8 topics. If I have to define all disciplines for each topic, we're already talking about 80 new content nodes here alone. +10 for the menu.
What do I actually want to achieve with all this?
Simply put, a menu structure based on 2 factors: discipline and topic. If both discipline and topic are specified, I want to display all the articles that are in them. If only one of them is specified, the list of the missing one comes up for selection.
Do you have any suggestion how I could achieve this? Any approach, an idea?
Is this the point where I would need the IContentFinder? It would make no sense to me. Ultimately, I just want to "extend" the existing URL by simply attaching another URL segment.
My TopicsController:
Hi Dominic
Yes, it wasn't so much that you need to create DocTypes, but in your evaluation, if you did, you would get a better grok on how the routing works out of the box and therefore how you can alter it...
There is so much I don't know about what you are ultimately trying to do :-P, even though you have explained! but in answer to your question:
In terms of this observation:
This is because when you inherit from RenderController to route hijack the convention is that your Action will match the alias of the template you are rendering this page. In Umbraco a Document Type can have multiple templates and you can create an Action to handle each one.
.Yes, this is that point!
When a Url comes into Umbraco, there are a series of rules executed, in order, to try and match the incoming Url and match it to a published content item. These rules are created by implementing an interface called IContentFinder and then adding it inot the collection of ContentFinders in precedence order.
Out of the box, Umbraco has an IContentFinder called ContentFinderByUrlAlias
https://github.com/umbraco/Umbraco-CMS/blob/e04c4a89adcbe73d35290a993713a5faf4dc55ff/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs#L10
which is currently responsible for routing your requests for your published topics and disciplines.
The match happens in TryFindContent implementation that receives the raw incoming request and then queries the Umbraco published cache to find a matching segment in the position of the url.... but you can create your own and the rules can be whatever you need.
so if you create a ContentFinderByVirtualTopic, and add it at the front of the content finder collection queue, then you can check the incoming URL
/en-us/topic/tutorials/architecture
and see if the first bit /en-us/topic/ matches the published content item, if so you know it's a valid Url that your ContentFinder should handle, and if not you return false, to allow the next ContentFinder in the queue to have a go.
If it's one of your Urls, you can then read the rest of the Url and retrifve your architecture tutorials from your external system (I presume that's what you are trying to do?) and then if you don't find any, return 'false' the request will eventually fall through the other content finders and 404, but if you have some, then you can use the PublishedContentWrapped class to create an IPublishedContent item and populate it with your external Article Data, the IPublishedContent item has an underlying DocumentType and a Template, so you can set that here... (in this scenario I'll normally have a dummy DocumentType called VirtualPage or something, and I'll usually have a content item of the type published to use as a base for my virtual content)
Anyway if your ContentFinder returns true, that's it, the virtual content gets sent through to Umbraco and you can 'hijack' it by creating a RenderController that matches it's underlying VirtualDocumentType...
it's a very flexible way of extending Umbraco routing without having to 'hardcode the routes in an MVC way' and shows you can sort of do anything....
https://docs.umbraco.com/umbraco-cms/reference/routing/request-pipeline/icontentfinder
but it is also totally possible to create an implementation of IVirtualPageController and 'map' an MVC route to it - but in doing so you have to specify 'what' is the IPublishedContent context if for the route.
So now you have published content you should be able to follow this example:
https://docs.umbraco.com/umbraco-cms/reference/routing/custom-routes#custom-route-with-ivirtualpagecontroller
or using ForUmbracoPage
https://docs.umbraco.com/umbraco-cms/reference/routing/custom-routes#custom-route-with-forumbracopage
to map your custom Urls to a specific a Controller and implement FindContent to return one of your published pages as the IPublishedContent context for the request.
anyway hope that give you some more to play around with!
regards
Marc
Hi Marc,
Thanks again for the reply!
In fact, I was just about to try that again with IVirtualPageController and Co, now that I have a DocType for it.
That I can create an action for each template is also good to know, thank you! I'll have to play around with that to see what I can do with it.
Regarding the article data: No, it doesn't actually come from an external system. I have installed the Umbraco.TheStarterKit, which contains a blog with posts. I just want to be able to assign several "topics" and "disciplines" to these posts. Then I would simply like to have a menu (in the frontend), divided into disciplines and topics, which shows the user the relevant posts if both a discipline and a topic have been selected. If only one discipline has been selected, a list of topics should appear so that one can be selected. And vice versa. And as soon as both have been selected, a list of posts should be displayed in the respective discipline and topic.
Thanks again for the answer and I'll get back to you as soon as I'm finished (hopefully with good news)!
is working on a reply...