Generate and Store Sitemap xml as Sitecore content item and display sitemap.xml for rendering host using GraphQL

Disclaimer: This is a bespoke solution, NOT recommended by Sitecore, using GraphQL and was attempted for the Sitecore MVP site since it doesn't have a sitemap.xml and is not built on SXA headless. Note that for XM Cloud, Sitemap must ideally be built using the OOTB capability.

In cricket, I have heard that a good batsman has three different shots for the same ball, this post is inspired by those lines!

My earlier blog post was about generating and rendering sitemap in an one-shot linear approach. In the same post, I had written about a common approach to generate sitemap xml and render it in two separate stages applicable for larger websites. This blog post covers that aspect applicable to headless sites. Although I don't have an XM Cloud instance to test the whole code, with my basic understanding of separation of concerns, I "presume" this is on correct track!

Anyway, first things first:

High-level overview:

- Content editor generates and stores the sitemap xml in Master db for anytime publish

- Published Sitemap XML is accessed on-the-fly by the end-user through GraphQL

- Suitable for larger sites


On the other hand, this is how the linear approach covered in earlier blog post might look since the generation of sitemap happens on the fly per user request:

Both Sitemap XML generation and rendering occurs on-the-fly:
Steps:

1. Setup Sitecore Content Serialization 

            a. For Core db items

2. Generate Sitemap xml on click of CM ribbon button 

            b. Create ribbon button

            c. Link ribbon button to custom code

            d. Create Sitecore template for Sitemap content

            e. Insert Sitemap item using custom code

3. Retrieve and render Sitemap xml for rendering host

            f. GraphQL query for Sitemap item retrieval

            g. Render Sitemap view

a. SCS for Core DB items:

######

######

b. Create ribbon button:

There are many old posts (check references) to create a chunk, menu etc. but if you are a seasoned Sitecore developer, the process is fairly intuitive once you are in Core db.


In the current scenario, the Generate Sitemap XML button resides within the Home strip. Since there is nothing more to group along with this button, the button is a standalone within Sitemap chunk as of now!

End-result of Core setup/SCS pull:


c. Link ribbon button to custom code:

Although there are a lot of references listed below with regard to linking a Core button command to C# code, as a refresher, this is how the Core db button' Click event is linked to C# command (name) using a Sitecore patch config. 


The most important aspect when working with headless architecture is, the Sitecore (CM) side of functionality must be added to the platform project, in this case, the c# class/config is added to Mvp.Project.MvpSite.Platform project.

Patch Config:

*********

*********

C# Event handler:

##########

##########

End-result:


d. Create Sitecore template for Sitemap content


Note: Just fyi, at a later point during my development process, I marked the field as shared and pushed changes to git file system.

Sitemap.module.json:

*********

*********

Result of SCS pull:

Also, Sitemap content item created under Shared content and serialized to file system:


e. Insert Sitemap item using custom code

Now, the next step is to insert the actual MVP sitemap xml to above content item through the SitemapGenerator command event handler.

Code without ORM tools like Glassmapper:

####################

####################

XML Data in Sitemap field:


f. GraphQL query for Sitemap item retrieval

There are so many blog posts for writing GraphQL queries like this exhaustive one by Amit. In those lines, the GraphQL query could be an item query or a search query but believe me, the challenge I faced is not in writing queries but mapping the C# fields to the GraphQL resultset.

Search query, for instance:

########################

########################

The item query could look like this:

#######################

#######################

Or, something like this:

#######################

#######################

The most important aspect is the C# object model since that will change as per the results - this is known to any C# developer. Then, the other aspect  I spent quite many hours receiving an object null reference exception is due to the fact that the name of Sitecore field (SitemapXml in this case) must match the C# property name although class name can be different:


Putting altogether the approach covered in earlier blog post and current one, here below is the high-level technical design for the core rendering method implementation based on SOLID principle. With DI in action from Startup class/ConfigureServices method(in asp.net core), the developer can switch between MVPSitemapUrlProvider (earlier implementation) and MVPSitemapXmlProvider (current implementation) with ease:

g.  Render Sitemap view

The rest of the aspects are same as the earlier blog post except passing the data in proper format using this method before passing back to the controller method:


End-result:

Git Branch

Titbits:

Debugging CM instance Code:


The most important aspect is finding the .net framework on which this dll is running in order to set framework version range in "Attach to" field and you can find that from dotpeek when you load the dll. In this case it was running on .net framework 4.8 hence .NET 4.x was chosen instead of .NET 5+ in above dialog.


Note that the key difference in case of the MVP site debugging is, rendering host is debugged by attaching to dotnet process while cm instance is debugged by attaching to w3wp process and dotnet framework version selection is key as part of this process. In case of rendering host, the version is automatically detected and selected but in case of cm, the selection must be done manually (Attach to field)!

Error:

When i tried to attach debugger to mvp rendering process, received the following error:

"the network connection was lost due to a protocol compatibility error. Ensure that visual studio remote debugger matches the version of Visual studio you are using"


Solution:


After installation of above remote tools compatible with Windows 11 Pro (AMD64 in my case), restart machine, execute docker system prune -a and up.ps1 resolved the issue


Error:

**************************************

Internal Server Error

An unhandled exception occurred while processing the request.

AggregateException: One or more errors occurred. (Object reference not set to an instance of an object.)

System.Threading.Tasks.Task.ThrowIfExceptional(bool includeTaskCanceledExceptions)

NullReferenceException: Object reference not set to an instance of an object.
**************************************

Cause/Solution:
Ensure that the property name matches the Sitecore field name:
In the following case, SitemapXmlData must be SitemapXml, which is the actual Sitecore field name:
References

https://mohdnaeem.wordpress.com/2017/09/08/adding-a-custom-button-to-the-ribbon-in-sitecore-ribbontab/

https://www.sitecorefundamentals.com/how-to-add-custom-buttons-to-the-content-editor-ribbon

https://www.jondjones.com/learn-sitecore-cms/sitecore-developers-guide/customising-sitecores-ui/how-to-add-a-custom-sitecore-button-to-the-editor-ribbon/

https://doc.sitecore.com/xp/en/developers/92/sitecore-experience-manager/create-an-item.html

https://community.sitecore.com/community/en?id=community_blog&sys_id=71e267211bc370d0b8954371b24bcbdb

https://sitecorememories.wordpress.com/2023/01/05/setup-graphql-for-sitecore-10-3-with-nextjs-jss-sitecore-container/

https://community.sitecore.com/community?id=community_question&sys_id=eba4e3211b4770d0b8954371b24bcb91

https://blogs.perficient.com/2013/09/26/accessing-sitecore-items-a-structured-approach/

Comments