5

I am working with Sitecore 10.3 and SXA, and I need to access the route node from the Layout Service response within my custom code so that I can iterate over all the placeholders in response to append some custom data. Which Sitecore pipeline and processor should I override or patch to achieve this?

{
    "sitecore": {
        "context": {
            "pageEditing": false,
            "site": {
                "name": "SampleSite"
            },
            "pageState": "normal",
            "language": "en",
            "itemPath": "/sample/path/to-item"
        },
        "route": {
            "name": "Sample Route Name",
            "displayName": "Sample Route Display Name",
            "fields": {
                "PublishedDate": {
                    "value": "2020-08-25T00:00:00Z"
                },
                "Authors": [
                    {
                        "id": "11111111-2222-3333-4444-555555555555",
                        "name": "Sample Author"
                    }
                ]
            }
        },
        "placeholders": {
            "jss-masthead": [
                {
                    "uid": "sample-uid",
                    "componentName": "PartialDesignDynamicPlaceholder",
                    "placeholders": {
                        "sxa-header2": [
                            {
                                "uid": "sample-header-uid",
                                "componentName": "GlobalNav",
                                "fields": {
                                    "DesktopImage": {
                                        "value": {
                                            "src": "https://example.com/media/logo.jpg",
                                            "alt": "Logo"
                                        }
                                    }
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}

5 Answers 5

0

You can read the layout json service json output and modify its output inside the ILayoutServiceResultFactory class, that returns the json from layout service.

  1. Create the custom implementation for ILayoutServiceResultFactory to read the layout service json and modify its value before its returned to browser as below.
using Sitecore.Data.Items;
using Sitecore.LayoutService.Configuration;
using Sitecore.LayoutService.Mvc.ActionResults;
using Sitecore.LayoutService.Mvc.Results;
using System.Web.Mvc;

public class CustomLayoutServiceResultFactory : ILayoutServiceResultFactory
{
   public virtual ActionResult Create(
      string content,
      string contentType,
      Item item,
      NamedConfiguration configuration)
   {
      //modify the content variable as per your need.
      DefaultLayoutServiceResult layoutServiceResult = new DefaultLayoutServiceResult(item, content);
      layoutServiceResult.ContentType = contentType;
      return (ActionResult)layoutServiceResult;
   }
}  

Inside above code, content variable contains the layout service json in serialized format.

  1. Override the dependency injection of ILayoutServiceResultFactory interface with the custom implementation created above, using below code.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sitecore.DependencyInjection;
using Sitecore.LayoutService.ItemRendering;
using Sitecore.LayoutService.Mvc.Results;
using SitecoreLayoutServiceExtensions.Pipelines;

public class CustomLayoutServiceConfigurator : IServicesConfigurator
{
   public void Configure(IServiceCollection serviceCollection)
   {
      serviceCollection.Replace(new ServiceDescriptor(typeof(ILayoutServiceResultFactory), typeof(CustomLayoutServiceResultFactory), ServiceLifetime.Singleton));
   }
}

This will inject the object of ILayoutServiceResultFactory interface with custom class created above, replacing the LayoutServiceResultFactory class implementation injected by default.

  1. Add the config patch below to replace dependency injection of ILayoutServiceResultFactory interface with CustomLayoutServiceResultFactory class.
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
   <sitecore>
      <services>
        <configurator type= "SitecoreLayoutServiceExtensions.DependencyInjection.CustomLayoutServiceConfigurator, SitecoreLayoutServiceExtensions"/>
      </services>
   </sitecore>
</configuration>

Using above changes, you will be able to read and modify the layout service json as per your need.

Update:

In case you are looking to add a parameter that is available for all the renderings to available globally without changing the resolvers, you can patch the processor as below.

  1. Create the transform processor as below to add custom parameters such as CacheInfo in the params property.
using Sitecore.Mvc.Presentation;
using Sitecore.XA.Feature.LayoutServices.Integration.Pipelines.TransformParameters;

public class CachingInfoTransformParameters : TransformParametersProcessor
{
   protected override string SupportedParameter
   {
      get
      {
        return "CacheInfo";
      }
   }

    protected override void ProcessParameter(TransformParametersArgs args)
    {
       args.Rendering.RenderingParams[this.SupportedParameter] = RenderingContext.Current.Rendering.Caching.Cacheable.ToString();
    }
}
  1. Patch the above transform processor using the config below.
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
   <sitecore>
     <pipelines>
         <transformParameters>
                <processor type="SitecoreLayoutServiceExtensions.Pipelines.CachingInfoTransformParameters, SitecoreLayoutServiceExtensions " resolve="true" patch:after="*[last()]" />
          </transformParameters>
      </pipelines>
   </sitecore>
</configuration>

Using above way, you can add custom parameters to the rendering that is available globally.

Hope this helps!!! Let me know in case you face any issues.

3

You can use the GetLayoutServiceContextArgs to get the the renderings related data from the context items. The code can be like below:

using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sitecore.Data.Items;
using Sitecore.Annotations;
using Sitecore.Data;
using Sitecore.JavaScriptServices.Configuration;
using Sitecore.JavaScriptServices.ViewEngine.LayoutService.Pipelines.GetLayoutServiceContext;
using Sitecore.LayoutService.ItemRendering.Pipelines.GetLayoutServiceContext;
using mysite.Extensions;


namespace mysite.Pipelines.GetLayoutServiceContext
{
    [UsedImplicitly]
    public class GetrenderingsContent : JssGetLayoutServiceContextProcessor
    {
        public GetrenderingsContent(IConfigurationResolver configurationResolver) : base(configurationResolver)
        {

        }
        protected override void DoProcess(GetLayoutServiceContextArgs args, AppConfiguration application)
        {
            var allRenderingsReferences = args.RenderedItem.Visualization.GetRenderings(Sitecore.Context.Device, false);

            List<string> datasources = new List<string>();
            string renderingsText = string.Empty;
            foreach (var rendering in allRenderingsReferences)
            {
                
                    datasource.Add(rendering.Settings.DataSource);
                   
                
            }
            if (!(string.IsNullOrEmpty(datasource)))
            {
                //your operation here
            }
            if (!(string.IsNullOrEmpty(renderingsText)))
            {
                args.ContextData.Add("renderingsContent", renderingsText);
            }
        }
    }
}

Patch this with a config file

<?xml version="1.0"?>

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"
               xmlns:set="http://www.sitecore.net/xmlconfig/set/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:env="http://www.sitecore.net/xmlconfig/env/">
  <sitecore>
    <pipelines>
      <group groupName="layoutService">
        <pipelines>
          <getLayoutServiceContext>
            <processor type="mysite.Pipelines.GetLayoutServiceContext.GetrenderingsContent, mysite" resolve="true"/>
          </getLayoutServiceContext>
        </pipelines>
      </group>

    </pipelines>
  </sitecore>
</configuration>    

Using this you can get the renderings data.

For more details please refer to: https://vinsitecore.wordpress.com/2024/07/15/extracting-pdf-content-to-text-for-sitecore-headless-solutions-backend-customizations/

2

You can achieve it with the help of Custom Resolver. So you need to create custom resolver in your Sitcore instance and you need to assign it to your rendering.

Patch Configuration

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <services>
            <configurator type="TestWebApplication.ServicesConfigurator,  TestWebApplication" />
        </services>
    </sitecore>
</configuration>

Create ServicesConfigurator class in your project root

using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;
using TestWebApplication.LayoutServices.AboutUs;
using TestWebApplication.LayoutServices.Products;


namespace TestWebApplication
{
    public class ServicesConfigurator : IServicesConfigurator
    {
        public void Configure(IServiceCollection serviceCollection)
        {

            serviceCollection.AddTransient<IAboutUsService, AboutUsService>();           
        }
    }
}

Then Create Resolver.

using Sitecore.LayoutService.Configuration;
using Sitecore.LayoutService.ItemRendering.ContentsResolvers;
using Sitecore.Mvc.Presentation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using TestWebApplication.LayoutServices.AboutUs;

namespace TestWebApplication.ContentResolvers.AboutUS
{
    public class AboutUsServiceResolver : RenderingContentsResolver
    {
        private readonly IAboutUsService rootResolver;

        public AboutUsServiceResolver(IAboutUsService aboutusResolver)
        {
            this.rootResolver = aboutusResolver;
        }

        public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
        {
          return rootResolver.GetAboutUsModel(rendering);
        }
    }
}

Create Interface Class

using Sitecore.Mvc.Presentation;
using TestWebApplicatione.Models;

namespace TestWebApplication.LayoutServices.AboutUs
{
    public interface IAboutUsService
    {
        AboutUsModel GetAboutUsModel(Rendering rendering);
    }
}

Implement your Interface Action Method

using TestWebApplication.Templates;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;
using System;
using TestWebApplication.Helpers;
using TestWebApplicatione.Models;

namespace TestWebApplication.LayoutServices.AboutUs
{
    public class AboutUsService : IAboutUsService
    {
        public AboutUsModel GetAboutUsModel(Rendering rendering)
        {
            AboutUsModel aboutUsModel = null;

            try
            {
                var datasource = Utils.GetRenderingDatasource(rendering);
                if (datasource == null) return null;

                aboutUsModel = new AboutUsModel();
                aboutUsModel.Title = Utils.GetValue(datasource, BaseTemplates.CommonTemplate.TitleFieldId);
                
                // here you can do your custom code 
            }
            catch (Exception ex)
            {
                Sitecore.Diagnostics.Log.Error(ex.Message, ex, this);
            }

            return aboutUsModel;
        }
    }
}

Hope this helps!!!

0

You can extend the Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver class and use the ProcessItem method from the class to enable rendering item fields for advanced Sitecore editors.

Here is a sample implementation.

    public class ProductListContentsResolver : Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver
    {
        protected readonly IProductRepository ProductRepository;
        protected readonly BaseLinkManager LinkManager;

        public ProductListContentsResolver(IProductRepository productRepository, BaseLinkManager linkManager)
        {
            Debug.Assert(productRepository != null);
            Debug.Assert(linkManager != null);
            ProductRepository = productRepository;
            LinkManager = linkManager;
        }

        public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
        {
            var productsRoot = GetContextItem(rendering, renderingConfig);
            var products = ProductRepository.GetProducts(productsRoot);
            return new
            {
                // Use the inherited method to get the JSON serialized item, then reduce the returned fields
                Products = products.Select(item => new
                    {
                        Item = item,
                        Serialized = base.ProcessItem(item, rendering, renderingConfig)
                    })
                    .Select(product => new
                    {
                        // Create object shape expected by ItemLinkField
                        Url = LinkManager.GetItemUrl(product.Item),
                        Id = product.Item.ID,
                        Fields = new
                        {
                            Title = product.Serialized[product.Item.Fields[Templates.Product.Fields.Title].Name],
                            ShortDescription = product.Serialized[product.Item.Fields[Templates.Product.Fields.ShortDescription].Name],
                            Image = product.Serialized[product.Item.Fields[Templates.Product.Fields.Image].Name]
                        }
                    })
            };
        }
    }

Refer to this repo for more details.

Another way of doing it is by customizing the Layout Service output for a Sitecore rendering by configuring a GraphQL query on the rendering.

For more details refer to the Sitecore document for detailed implementations.

https://doc.sitecore.com/xp/en/developers/hd/latest/sitecore-headless-development/customizing-the-layout-service-rendering-output.html#using-the-graphql-api

Hope it helps.

0

To access the route node and iterate through placeholders from the Layout Service response in Sitecore 10.3 with SXA, you can patch a custom processor into the mvc.getXmlBasedLayoutDefinition or generateLayout pipeline. This will allow you to intercept the layout generation and modify it by appending custom data.

Steps:

Override or Patch generateLayout Pipeline:

This pipeline is responsible for generating the layout definition in the Layout Service response. You can add a custom processor to modify the placeholders and add custom data.

Create a Custom Processor:

Your custom processor will iterate through the placeholders and append the desired data. Here's an outline of how you can achieve this:

using Sitecore.Mvc.Pipelines.Response.GetXmlBasedLayoutDefinition;
using Sitecore.LayoutService.Serialization;
using Sitecore.Mvc.Presentation;

public class CustomLayoutProcessor : GetXmlBasedLayoutDefinitionProcessor
{
    public override void Process(GetXmlBasedLayoutDefinitionArgs args)
    {
        // Access the route node
        var layoutDefinition = args.Result;
        
        // Check if the layout definition is valid
        if (layoutDefinition == null)
        {
            return;
        }

        // Access placeholders
        var placeholders = layoutDefinition.Placeholders;

        // Iterate through placeholders to append custom data
        foreach (var placeholder in placeholders)
        {
            // Example: appending custom data
            AppendCustomData(placeholder);
        }
    }

    private void AppendCustomData(PlaceholderDefinition placeholder)
    {
        // Add custom logic to modify or append data to placeholders
        placeholder.CustomData = "CustomValue"; // Example
    }
}

Patch Your Processor in generateLayout Pipeline:

You need to patch your custom processor in the generateLayout pipeline to intercept the Layout Service response:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <generateLayout>
        <processor type="Your.Namespace.CustomLayoutProcessor, YourAssembly" patch:before="processor[@type='Sitecore.LayoutService.Pipelines.GenerateLayout.GenerateLayoutProcessor, Sitecore.LayoutService']"/>
      </generateLayout>
    </pipelines>
  </sitecore>
</configuration>

Access the Route Node and Placeholders:

In your custom processor, you can access the Route node via args.Result and then iterate through the placeholders by accessing args.Result.Placeholders.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.