K13 page template registration breaking

Aaron Macdonald asked on November 14, 2022 03:58

Hello

We have a problem where we can register a default page template for one page but not another.

Platform

  • K13 v13.0.73
  • .NET Framework
  • CTB Routing

Both pages inherit from a base page and other than that there's nothing unusual about them. We have double checked their code names. Both pages have a page template controller, view, properties class, generated code class, and filter as per documentation.

The registration attributes are as follows, where page1 works and page2 does not:

[assembly: RegisterPageTemplate( identifier: Page1.CLASS_NAME, controllerType: typeof(Page1TemplateController), name: "Page1 Default", Description = "Default template for page1.", IconClass = "icon-l-text-col")]

[assembly: RegisterPageTemplate( identifier: Page2.CLASS_NAME, controllerType: typeof(Page2TemplateController), name: "Page2 Default", Description = "Default template for page2.", IconClass = "icon-l-text-col")]

If we comment out both of the above registrations, we get a different error for each page, which must indicate some kind of discrepancy:

  • Page1 error: "Page template with identifier "namespace.page1" is not registered in the application."
  • Page2 error: "The view on path '/Views/Shared/PageTypes/namespace_page2.cshtml' could not be found and there is no page template registered for selected page."

Could this be a K13 bug? We don't see anything relevant in the version history.


Edit:

Problem persists after:

  • Upgrading to v13.0.90.
  • Removing the base page type inheritance.

Not sure if it's relevant but we've also started seeing an error in the event log as follows:

Event ID:43756
Event type:Error
Event time:11/14/2022 4:22:23 PM
Source:TypeManager
Event code:PREINIT
IP address:
Description:Message: The route template 'getavatar/{avatarguid:guid}/{filename}' is already registered.
Parameter name: descriptor

Exception type: System.ArgumentException
Stack trace:
at CMS.Base.Routing.HttpHandlerRouteTable.Register(RegisterHttpHandlerAttribute descriptor)
at CMS.Core.TypeManager.PreInitializeTypes(Assembly assembly)

Correct Answer

Sean Wright answered on November 17, 2022 14:55

If we comment out both of the above registrations, we get a different error for each page, which must indicate some kind of discrepancy:

This is because Page1 has the database column CMS_Document.DocumentPageTemplateConfiguration already populated with the Page Template JSON for Page1. The value is going to look something like { "identifier": "namespace.page1" } because that is what you specified in your registration attribute.

Since you don't have a Page Template registered with that identifier, it is erroring out.

However, Page2 does not have a value for this column. This means Xperience is using its standard approach for finding a way to render this page.

First it checks to see if any Controllers/Actions are registered for this Page Type. If it can't find any (which is your case), it tries to use its "Route to View" behavior, which looks for a Razor View file (.cshtml) in a specific location. This location is /Views/Shared/PageTypes/PageTypeNamespace_PageTypeName.cshtml. Since you don't have a View there, it is erroring out.

When you create a Page in the Content Tree, Xperience looks to see if any Page Templates have been registered that would be applicable to it by using the registered Page Template Filters. Page Template Filters are only used when working with pages in the Content Tree, not when rendering them on the Live Site.

If no Page Template is registered for the page, Xperience creates the page as a normal page and won't use Page Templates for it, unless it is recreated. This is because it is missing the CMS_Document.DocumentPageTemplateConfiguration value. Since this value doesn't exist, Xperience doesn't falls back to normal Content Tree Routing.

If there is 1 Page Template registered for the page then Xperience auto-selects it when you create the page and stores that template's information in CMS_Document.DocumentPageTemplateConfiguration.

If there are multiple Page Templates registered for that page, then you will be prompted to select 1. The selected Page Template's information will be stored in CMS_Document.DocumentPageTemplateConfiguration.

If a page has CMS_Document.DocumentPageTemplateConfiguration populated, it is a Page Template page, which means if you add/change Page Templates in the future, you can always switch to those. But the page won't ever be a normal page that uses Route to Controller or Route to View behavior.


I have a couple of recommendations for you if you want to use Page Templates.

  1. Don't register a Page Template using the attribute overload that uses a separate Controller. The separate Controller won't bring you much benefit and complicates the setup of the Page Template. Instead, use the overload that only has a View path.

    [assembly: RegisterPageTemplate(
        "PROJECT_IDENTIFIER.HomePage_Default",
        "Home (Default)",
        typeof(HomePageTemplateProperties),
        "Features/Home/HomePage_Default")]
    
  2. Don't use the Page Type class name as the Page Template identifier! The real benefit of Page Templates is that you can have multiple for a single Page/Page Type. I recommend using a Page Template Filter to associate a set of Page Templates with pages of a specific Page Type. Use our Page Template Utilities package to simplify this process.

  3. Check out my blog post covering the PTVC architecture, which is our preferred way of using Page Templates in Xperience. We've used this approach successfully for multiple projects.

  4. If you need to switch a page from being a Route to View or Route to Controller page to a Page Template page (or go in the opposite direction), then add the DocumentPageTemplateConfiguration field to the Page Type fields so that you can set/remove the correct JSON value for this field in the Content tab of the Page. This is especially helpful if the page is under workflow. If it's not under workflow then you can update the CMS_Document.DocumentPageTemplateConfiguration value in the database directly.

1 votesVote for this answer Unmark Correct answer

Recent Answers


Dmitry Bastron answered on November 14, 2022 09:47

Hi Aaron,

  • Do both of your page templates return TemplateResult objects?
  • Are there any template filters?
  • Have you tried these templates on the default DancingGoat website?
0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on November 14, 2022 09:54 (last edited on November 14, 2022 09:54)

Hi Dimitry

  1. These are page templates with their own controllers, so they return a view rather than a TemplateResult.

  2. Yes we have filters in place as per documentation. I note that these are not triggering in the case of page2. Kentico doesn't get to that point, and it seems that it just isn't recognising the registration, which doesn't make sense.

  3. Our custom code won't integrate into the DancingGoat site, but we have inspected that sample site thoroughly and the pattern we are using is very similar to DancingGoat.Article and DancingGoat.ArticleWithSidebar.

0 votesVote for this answer Mark as a Correct answer

Dmitry Bastron answered on November 14, 2022 10:06

For your page 2 it also seems that Kentico can't find a view. Have you tried CustomViewName optional parameter? If you don't specify it, Kentico will be searching for Page2.CLASS_NAME.cshtml in your case.

0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on November 14, 2022 23:23

Dimitry I don't believe that Kentico is even getting to the stage of looking for a view, it simply doesn't recognise the registration for some unknown reason.

My understanding is that CustomViewName is only required for simple page templates, not where a controller is involved as per the above registration attributes.

0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on November 16, 2022 03:47 (last edited on November 16, 2022 06:24)

We've detected the error below in the event log. Changing the page type name does not solve the problem, and the error below reappears with the changed name.

Note that these pages previously had regular CTB controllers in place with RegisterPageRoute attributes.

Could those prior registrations be causing this problem, and if so, how would we remove them?

Event ID:44099
Event type:Error
Event time:11/16/2022 12:38:01 PM
Source:TypeManager
Event code:PREINIT
IP address:
Description:Message: Definition with key 'namespace.page2' cannot be registered because another definition with such key is already present.

Exception type: System.ArgumentException
Stack trace:
at Kentico.Content.Web.Mvc.DefinitionStoreBase`1.Add(TDefinition registeredDefinition)
at CMS.Core.TypeManager.PreInitializeTypes(Assembly assembly)
Machine name:
Event URL:
URL referrer:
User agent:

Edit:

After removing everything associated with page2 and recreating the page type, the page template began working as expected.

So this is a workaround but not ideal. It would be nice to understand what's going wrong and if we can fix it without removing page data.

0 votesVote for this answer Mark as a Correct answer

Aaron Macdonald answered on November 18, 2022 01:56

Hi Sean thanks for the detailed response.

This information should be in the K13 documentation. Otherwise there is no way those new to the platform will know about this behaviour, and why a newly introduced page template does not work.

We were able to resolve the problem by doing the suggested insert into the CMS_Document.DocumentPageTemplateConfiguration field for every page2 instance, without having to delete and recreate content.

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.