1

I'm attempting to use a Content Token named BrandName in a Page Title field in a multi-site headless solution (Sitecore 10.1.1 Headless 18). The hope was that I could setup the Standard Values for the field as follows: $name | $(BrandName). This would replace the $name token with the name of the Route item and the $(BrandName) token with the value specified within the Data folder of the site.

Based on the SXA documentation

The logic of content tokens replacement is implemented in the RenderContentToken processor of the renderField pipeline. You can reference a content token in a rich text, single-line text, and multi-line text field by using the $ key and the name of the content token in parentheses.

I have confirmed that this does work as expected with Rich Text fields, but not with Single Line Text or Multi-line Text fields.

In my example below, the token key is BrandName and the value is Super Huge Brand. The Route name is Home.

{
    "sitecore": {
        "context": {
            "pageEditing": false,
            "site": {
                "name": "Super Huge Brand"
            },
            "pageState": "normal",
            "language": "en",
            "itemPath": "/"
        },
        "route": {
            "name": "Home",
            "displayName": "Home",
            "fields": {
                "Page Design": {
                    "id": "640c1013-1a08-4301-bdae-324e0953b5fd",
                    "url": "/sitecore/content/Client A/Super Huge Brand/Presentation/Page-Designs/Primary-Page-Design",
                    "name": "Primary Page Design",
                    "displayName": "Primary Page Design",
                    "fields": {
                        "PartialDesigns": {
                            "value": "{79FDD9F5-E3ED-4597-BB59-C8C4E8187D10}|{3232D922-6C2E-447C-834D-50E5D1EE8A86}|{D3FFF78A-284E-4135-B826-FD91E8505DBB}|{7296CF96-E7F6-4F04-84C0-032592C57BCF}"
                        }
                    }
                },
                "PageTitle": {
                    "value": "Home | $(BrandName)"
                },
                "RichPageTitle": {
                    "value": "Home | Super Huge Brand"
                },
                "MultiLinePageTitle": {
                    "value": "Home | $(BrandName)"
                }
            },
            "databaseName": "web",
            "deviceId": "fe5d7fdf-89c0-4d99-9aa3-b5fbd009c9f3",
            "itemId": "706a68e8-349c-4a5d-bd38-38f0766171b1",
            "itemLanguage": "en",
            "itemVersion": 1,
            "layoutId": "9a4810a8-5fd1-42ac-97d8-4ad65b56c7ed",
            "templateId": "34e6c385-eb0d-4858-b173-8b0cbc919149",
            "templateName": "App Route",
            "placeholders": {}
        }
    }
}

My question is: Am I doing something wrong or is this a bug that I need to log with Sitecore?

2 Answers 2

4

I'm working on a SXA/JSS solution in Sitecore 10.3 and I had the same issue. To fix this I created a custom field serializer and added it to the layout service.

First, you should create a class that inherits from BaseFieldSerializer and override the WriteValue method, I called this class TokenizedTextFieldSerializer.

public class TokenizedTextFieldSerializer : BaseFieldSerializer
{
    protected Regex RegexTokenInputs { get; } = new Regex("\\$\\(([^$()]+)\\)", RegexOptions.Compiled);
    protected IContext Context { get; } = ServiceLocator.ServiceProvider.GetService<IContext>();
    public TokenizedTextFieldSerializer(IFieldRenderer fieldRenderer)
        : base(fieldRenderer)
    {
    }
    protected override void WriteValue(Field field, JsonTextWriter writer)
    {
        Assert.ArgumentNotNull((object)field, nameof(field));
        Assert.ArgumentNotNull((object)writer, nameof(writer));
    
        string result = field.Value;
        foreach (Match match in this.RegexTokenInputs.Matches(field.Value))
        {
            string tokenValue = this.GetTokenValue(match.Groups[1].Value);
            if (!string.IsNullOrWhiteSpace(tokenValue))
                result = result.Replace(match.Value, tokenValue);
        }
        writer.WriteValue(result);
    }

    private string GetTokenValue(string tokenKey)
    {
        ID keyFieldId = ID.Parse("{02F6FB63-FE92-44E8-AFA2-20747C893502}");
        ID valueFieldId = ID.Parse("{1B9D1028-C20C-407B-9DCD-AFFE86A6F793}");
        if (!string.IsNullOrWhiteSpace(tokenKey))
        {
            ID contentTokenId = ((IEnumerable<ID>)this.Context.Database.DataManager.DataSource.SelectIDs(keyFieldId, tokenKey)).FirstOrDefault<ID>();
            Item tokenItem = this.Context.Database.GetItem(contentTokenId);
            if (tokenItem != null)
            {
                return tokenItem[valueFieldId];
            }
        }
        return string.Empty;
    }
}

Now create a class that inherits from BaseGetFieldSerializer, and override the SetResult method to set an instance of the previous class as the value of args.Result, I called this class GetTokenizedTextFieldSerializer.

public class GetTokenizedTextFieldSerializer : BaseGetFieldSerializer
{
    public GetTokenizedTextFieldSerializer(IFieldRenderer fieldRenderer):base(fieldRenderer)
    {
    }

    protected override void SetResult(GetFieldSerializerPipelineArgs args)
    {
        args.Result = new TokenizedTextFieldSerializer(this.FieldRenderer);
    }
}

Now you should add the GetTokenizedTextFieldSerializer processor to the getFieldSerializer, and patch it before GetDefaultFieldSerializer processor. Do not forget to specify the field types that this field serializer should be applied to, in our case single-line text and multi-line text field types.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
    <pipelines>
      <group groupName="layoutService">
        <pipelines>
            <getFieldSerializer>
                <processor patch:before="*[@type='Sitecore.LayoutService.Serialization.Pipelines.GetFieldSerializer.GetDefaultFieldSerializer, Sitecore.LayoutService']" type="Your.Namespace.GetTokenizedTextFieldSerializer, your.dllName" resolve="true">
                    <FieldTypes hint="list">
                        <fieldType id="1">single-line text</fieldType>
                        <fieldType id="3">multi-line text</fieldType>
                    </FieldTypes>
                </processor>
            </getFieldSerializer>
        </pipelines>
      </group>
    </pipelines>
</sitecore>
</configuration>

Now you can use SXA Content Tokens as you use in a regular MVC solution $(tokenKey).

Here is a good article that explains in more detail how to implement it. https://www.andreandrade.blog/2023/05/how-to-use-sxa-content-tokens-in-jss.html

3

I understand that you are talking about SXA/JSS sites, right?

If yes, then I'm surprised that it works for the Rich Text fields. We've never migrated content tokens to SXA/JSS sites, you won't find the Content Token field under a data for such site:

enter image description here

Implementation details: Sitecore.XA.Feature.ContentTokens.Pipelines.RenderField.RenderContentToken

2
  • 1
    Thanks @Dawid, you are correct. I am using SXA for JSS and adding my own content items into the Data folder where applicable. It sounds like Content Tokens weren't tested against JSS/Headless and I'll have to develop my own implementation if I want to leverage token functionality. Is that a fair assumption? Sep 30, 2021 at 15:00
  • 2
    @ChetCheeto Yes, we didn't add that feature to SXA/JSS sites and in order to have such functionality, you will need to implement it on your own. Sep 30, 2021 at 19:36

Your Answer

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

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