<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by PuneetGarg</title><link href="http://world.optimizely.com" /><updated>2025-11-10T11:24:15.0000000Z</updated><id>https://world.optimizely.com/blogs/puneetgarg/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Algolia Search with Optimizely SAAS</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2025/11/algolia-search-with-optimizely-saas/" /><id>&lt;h1&gt;Algolia + Optimizely SAAS (Remko Next.js) Integration Guide&lt;/h1&gt;
&lt;p&gt;This guide explains how to &lt;strong&gt;integrate Algolia Search with your Optimizely SaaS CMS project&lt;/strong&gt; built on the &lt;strong&gt;Remko Starter Kit (Next.js)&lt;/strong&gt;.&lt;br /&gt;It focuses on the &lt;strong&gt;Crawl Method&lt;/strong&gt;, which automatically indexes your published Optimizely pages into Algolia &amp;mdash; without manual API synchronization.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Overview&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Algolia&lt;/strong&gt; is a powerful, fully managed search-as-a-service platform that delivers lightning-fast, relevant, and typo-tolerant search results.&lt;/p&gt;
&lt;p&gt;When connected with &lt;strong&gt;Optimizely SaaS&lt;/strong&gt;, you can enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Real-time full-text search&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Category or keyword-based filtering&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Faceted navigation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Personalized content discovery&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the &lt;strong&gt;Crawl Method&lt;/strong&gt;, Algolia automatically reads and indexes your Optimizely site content &amp;mdash; no backend coding required.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Prerequisites&lt;/h2&gt;
&lt;p&gt;Make sure you have the following ready before you begin:&lt;/p&gt;
&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;
&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot;&gt;
&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅ Optimizely SaaS CMS&lt;/td&gt;
&lt;td&gt;Project built on &lt;strong&gt;Remko Starter Kit (Next.js)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Node.js&lt;/td&gt;
&lt;td&gt;Version &lt;strong&gt;18+&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Algolia Account&lt;/td&gt;
&lt;td&gt;create from Algolia site&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Optimizely API Credentials&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(Only needed if you plan to fetch content manually)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Setting Up the Algolia Crawler&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Log in to your &lt;strong&gt;Algolia Dashboard&lt;/strong&gt; &amp;rarr; &lt;strong&gt;Crawler&lt;/strong&gt; section.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;&amp;ldquo;Create a new crawler.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add your site URL &amp;mdash; e.g.&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;
&lt;div class=&quot;sticky top-9&quot;&gt;
&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;
&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;https:&lt;span class=&quot;hljs-comment&quot;&gt;//optimizely-something.vercel.app/&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define your crawling rules &amp;mdash; typically, you&amp;rsquo;ll want to include pages that have:&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;
&lt;div class=&quot;sticky top-9&quot;&gt;
&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;
&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;whitespace-pre! language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;meta&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;title&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;content&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;...&quot;&lt;/span&gt; /&amp;gt;&lt;/code&gt;&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;whitespace-pre! language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;meta&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;description&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;content&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;...&quot;&lt;/span&gt; /&amp;gt;&lt;/code&gt;&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;whitespace-pre! language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;meta&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;keywords&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;content&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;...&quot;&lt;/span&gt; /&amp;gt;
&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the crawl. Once complete, Algolia will automatically create an &lt;strong&gt;index&lt;/strong&gt; containing your site&amp;rsquo;s structured content.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Tip:&lt;/em&gt; You can view and fine-tune your index data in &lt;strong&gt;Algolia &amp;rarr; Indices &amp;rarr; [your index name]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Environment Variables&lt;/h2&gt;
&lt;p&gt;These variables connect your frontend with your Algolia app.&lt;/p&gt;
&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;
&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot;&gt;
&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALGOLIA_APP_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identifies your Algolia app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALGOLIA_SEARCH_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safe public key for frontend search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALGOLIA_INDEX_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your index name in Algolia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALGOLIA_ADMIN_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Private admin key (backend use only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPTIMIZELY_API_URL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only required for API-based indexing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OPTIMIZELY_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access token if using Optimizely REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix is required so your keys are available in the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; include your &lt;code&gt;ALGOLIA_ADMIN_KEY&lt;/code&gt; in frontend code &amp;mdash; it&amp;rsquo;s sensitive.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Add Algolia to Your Next.js App&lt;/h2&gt;
&lt;p&gt;Install the required dependencies:&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;
&lt;div class=&quot;sticky top-9&quot;&gt;
&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;
&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;npm install algoliasearch react-instantsearch
&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;nbsp;Implementing the Search Component&lt;/h2&gt;
&lt;p&gt;Create a file like &lt;code&gt;components/Search.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;
&lt;div class=&quot;sticky top-9&quot;&gt;
&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;
&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;import algoliasearch from &#39;algoliasearch/lite&#39;;
import { InstantSearch, SearchBox, Hits } from &#39;react-instantsearch&#39;;

const searchClient = algoliasearch(
  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);

export default function Search() {
  return (
    &amp;lt;InstantSearch
      searchClient={searchClient}
      indexName={process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME!}
    &amp;gt;
      &amp;lt;div className=&quot;p-6&quot;&amp;gt;
        &amp;lt;SearchBox /&amp;gt;
        &amp;lt;Hits hitComponent={Hit} /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/InstantSearch&amp;gt;
  );
}

function Hit({ hit }: any) {
  return (
    &amp;lt;div className=&quot;p-4 border-b border-gray-200&quot;&amp;gt;
      &amp;lt;h3 className=&quot;font-semibold text-lg&quot;&amp;gt;{hit.title}&amp;lt;/h3&amp;gt;
      &amp;lt;p className=&quot;text-gray-600&quot;&amp;gt;{hit.description}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note - you can add pagination , facet and other stuff as well.&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;Deploying to Vercel&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Push your code to GitHub (or GitLab/Bitbucket).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect your repo to &lt;strong&gt;&lt;a class=&quot;decorated-link&quot; href=&quot;https://vercel.com&quot;&gt;Vercel&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Vercel &amp;rarr; &lt;strong&gt;Project Settings &amp;rarr; Environment Variables&lt;/strong&gt;, add the same values as in your &lt;code&gt;.env.local&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;mdash; your search-enabled Optimizely SaaS site goes live!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; Advantages of Using Algolia with Optimizely SaaS&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ultra-Fast Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Delivers instant search results with minimal latency using Algolia&amp;rsquo;s distributed infrastructure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intelligent Relevance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in typo tolerance, synonyms, and ranking formulas improve result accuracy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No Backend Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The crawler eliminates the need for building sync APIs &amp;mdash; setup takes minutes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Highly Customizable UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Algolia&amp;rsquo;s React InstantSearch library allows you to create dynamic and interactive search interfaces.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built-in Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gain insights into user behavior, popular searches, and zero-result queries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalable &amp;amp; Cloud-Hosted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Perfectly complements Optimizely SaaS&amp;rsquo;s headless architecture and cloud hosting model.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secure Key Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use public search keys on frontend, while keeping admin keys private for backend-only tasks.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</id><updated>2025-11-10T11:24:15.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Integrating Address Validation in Optimizely Using Smarty</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2025/5/implementing-address-validation-with-smarty/" /><id>&lt;p&gt;Address validation is a crucial component of any ecommerce platform. It ensures accurate customer data, reduces shipping errors, and improves the overall user experience. In this post, we&amp;rsquo;ll walk through how to integrate address validation in Optimizely (formerly Episerver) using &lt;a class=&quot;&quot; href=&quot;https://www.smarty.com/&quot;&gt;Smarty&lt;/a&gt; with the &lt;code&gt;smartystreets-dotnet&lt;/code&gt; SDK.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Why Smarty?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Smarty provides powerful APIs for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Autocompleting address input&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verifying domestic and international addresses&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validating zip/postal codes&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It offers both US-specific and international endpoints, making it suitable for global ecommerce implementations.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Setting Up Smarty in Optimizely&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;Configure Credentials&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;First, define your configuration model to hold Smarty credentials:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class SmartyConfiguration
{
    public string ApplicationId { get; set; }
    public string ApplicationSecret { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Extend your configuration setup:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static class SmartyConfigurationLoader
{
    public static IConfiguration SmartyConfiguration(this IConfiguration configuration) =&amp;gt;
        configuration.GetSection(&quot;SmartyConfiguration&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;Define Smarty Settings for Your CMS&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Create a model for managing country exclusions:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class SmartySettingsModel
{
    [JsonProperty(&quot;autocompleteExcludedCountries&quot;)]
    public string AutocompleteExcludedCountries { get; set; }

    [JsonProperty(&quot;verificationExcludedCountries&quot;)]
    public string VerificationExcludedCountries { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Display settings in the UI using a ViewComponent:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class SmartySettingsViewComponent : ViewComponent
{
    private readonly ISmartyService _smartyService;

    public SmartySettingsViewComponent(ISmartyService smartyService)
    {
        _smartyService = smartyService;
    }

    public IViewComponentResult Invoke()
    {
        var viewModel = _smartyService.GetSettings();
        return View(&quot;~/Features/Smarty/Components/SmartySettings/Default.cshtml&quot;, viewModel);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. &lt;strong&gt;Implement the Smarty Controller&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The controller handles front-end requests for autocomplete and address validation:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Route(&quot;smarty/autocomplete&quot;)]
public IActionResult Autocomplete(AutocompleteRequestModel model)


[Route(&quot;smarty/street&quot;)]
public IActionResult Street(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both actions use a caching layer to optimize repeated queries.&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;Create the Smarty Service&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The heart of the integration is the &lt;code&gt;SmartyService&lt;/code&gt; that communicates with the Smarty APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;US Autocomplete&lt;/strong&gt; via &lt;code&gt;USAutocompleteProApi&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;International Autocomplete&lt;/strong&gt; via &lt;code&gt;InternationalAutocompleteApi&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;US Street Verification&lt;/strong&gt; via &lt;code&gt;USStreetApi&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;International Address Verification&lt;/strong&gt; via &lt;code&gt;InternationalStreetApi&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example method:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public object Autocomplete(AutocompleteRequestModel request)
{
    // Handles US or international logic
}


public object Street(AddressModel addressModel)
{
    // Determines whether to use US or international client
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fallback strategies like trying with placeholder streets (&amp;ldquo;A&amp;rdquo; or &amp;ldquo;B&amp;rdquo;) for ZIP code validation enhance reliability (if your checkout don&#39;t have full address.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Client-side JavaScript Integration&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;You can call &lt;code&gt;/smarty/autocomplete&lt;/code&gt; or &lt;code&gt;/smarty/street&lt;/code&gt; from your JavaScript to power address suggestions and validation. For example:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;fetch(`/smarty/autocomplete?countryCode=USA&amp;amp;line1=123 Main`)
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; {
    // Render autocomplete suggestions
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Customize your UI interactions based on the response schema.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Benefits of Smarty Integration&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved accuracy&lt;/strong&gt; in shipping addresses&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced failed deliveries&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Better customer experience&lt;/strong&gt; with live autocomplete&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Support for international addresses&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Integrating address validation using Smarty in Optimizely helps elevate your e-commerce platform&amp;rsquo;s user experience. Smarty becomes a powerful ally for global address accuracy with robust APIs and thoughtful error handling.&lt;/p&gt;</id><updated>2025-05-21T17:26:03.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Shared optimizely cart between non-optimizley front end site</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2024/12/shared-cart-with-non-optimizley-sites/" /><id>&lt;p&gt;E-commerce ecosystems often demand a seamless shopping experience where users can shop across multiple sites using a single cart. Sharing a cart between an Optimizely site and a non-Optimizely site can be a challenge, but it&#39;s achievable with the right strategy. Here&#39;s a guide on how to set this up.&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;The goal is to enable users to share their cart and product information seamlessly across platforms. We achieve this by creating APIs that interact with the cart and product information. I&#39;ve not added API&#39;s here.This implementation covers two scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Logged-In Users&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anonymous Users&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;1. Handling Logged-In Users&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For logged-in users, leveraging their &lt;strong&gt;SSOID&lt;/strong&gt; ensures that their context, including customer information and cart data, is consistent across sites. The SSOID is passed in the request header from the front end to the API.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Implementation Steps&lt;/strong&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capture SSOID from Header&lt;/strong&gt;&lt;br /&gt;Use the &lt;code&gt;Request.Headers&lt;/code&gt; to extract the userId (SSOID) in the backend.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Request.Headers.TryGetValue(&quot;userId&quot;, out var ssoId);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fetch Customer Contact&lt;/strong&gt;&lt;br /&gt;Using the SSOID, retrieve the logged-in user&#39;s context and cart data.
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private static CustomerContact GetCustomer(string ssoId)
{
    var currentContact = CustomerContext.Current.CurrentContact;
    if (string.IsNullOrEmpty(ssoId))
        return currentContact;

    currentContact = CustomerContext.Current.GetContactByUserId($&quot;String:{ssoId}&quot;);
    return currentContact;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sync Cart Data&lt;/strong&gt;With the &lt;code&gt;CustomerContact&lt;/code&gt; object, the cart data can be fetched and synced between the sites.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;&lt;strong&gt;Key Notes&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Ensure that the SSOID is securely transmitted in the header.&lt;/li&gt;
&lt;li&gt;Proper error handling is crucial to avoid unexpected failures if the SSOID is missing or invalid.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;2. Handling Anonymous Users&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Anonymous users lack a consistent SSOID, but their session can still be tracked using a cookie (&lt;code&gt;EPiServer_Commerce_AnonymousId&lt;/code&gt;).&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Implementation Steps&lt;/strong&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capture Anonymous ID from Cookies&lt;/strong&gt;&lt;br /&gt;Extract the cookie value that stores the anonymous user ID.&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950&quot;&gt;
&lt;div class=&quot;sticky top-9 md:top-[5.75rem]&quot;&gt;
&lt;div class=&quot;absolute bottom-0 right-2 flex h-9 items-center&quot;&gt;
&lt;div class=&quot;flex items-center rounded bg-token-sidebar-surface-primary px-2 font-sans text-xs text-token-text-secondary dark:bg-token-main-surface-secondary&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; Request.Cookies.TryGetValue(&quot;EPiServer_Commerce_AnonymousId&quot;, out var cookieValue); &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;!whitespace-pre hljs language-csharp&quot;&gt;&lt;/code&gt;&lt;strong&gt;Use Anonymous Cart&lt;/strong&gt;&lt;br /&gt;With the &lt;code&gt;cookieValue&lt;/code&gt;, fetch the anonymous user&#39;s cart from the backend.&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Share Cart Across Sites&lt;/strong&gt;&lt;br /&gt;Sync the cart data between the sites using the retrieved anonymous user ID.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;&lt;strong&gt;Key Notes&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Cookies must be configured for cross-domain access if the sites are hosted on different domains.&lt;/li&gt;
&lt;li&gt;Ensure proper handling of cookie expiration and invalidation scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;API Design&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Below is a high-level example of how the API endpoints could look:&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Get Cart for Logged-In User&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Endpoint&lt;/strong&gt;: &lt;code&gt;GET /api/cart/loggedin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Header:&amp;nbsp; &amp;nbsp;&lt;/strong&gt;&lt;span style=&quot;background-color: #f5f2f0; font-family: Consolas, Monaco, &#39;Andale Mono&#39;, &#39;Ubuntu Mono&#39;, monospace; font-size: 1em; word-spacing: normal;&quot;&gt;userId: &amp;lt;SSOID&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;Get Cart for Anonymous User&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Endpoint&lt;/strong&gt;: &lt;code&gt;GET /api/cart/anonymous&lt;/code&gt;&lt;br /&gt;&lt;strong&gt;Cookie&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950&quot;&gt;
&lt;div class=&quot;sticky top-9 md:top-[5.75rem]&quot;&gt;
&lt;div class=&quot;absolute bottom-0 right-2 flex h-9 items-center&quot;&gt;
&lt;div class=&quot;flex items-center rounded bg-token-sidebar-surface-primary px-2 font-sans text-xs text-token-text-secondary dark:bg-token-main-surface-secondary&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;EPiServer_Commerce_AnonymousId=&amp;lt;cookieValue&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;&lt;strong&gt;Response Example&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Both endpoints would return the cart in a standardized format, e.g.:&lt;/p&gt;
&lt;div class=&quot;contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950&quot;&gt;
&lt;div class=&quot;overflow-y-auto p-4&quot;&gt;&lt;code class=&quot;!whitespace-pre hljs language-json&quot;&gt;&lt;code class=&quot;!whitespace-pre hljs language-json&quot;&gt;&lt;/code&gt;&lt;/code&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;{
    &quot;items&quot;: [
        {
            &quot;productId&quot;: &quot;12345&quot;,
            &quot;name&quot;: &quot;Product A&quot;,
            &quot;quantity&quot;: 2,
            &quot;price&quot;: 50.0
        },
        {
            &quot;productId&quot;: &quot;67890&quot;,
            &quot;name&quot;: &quot;Product B&quot;,
            &quot;quantity&quot;: 1,
            &quot;price&quot;: 25.0
        }
    ],
    &quot;total&quot;: 125.0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;code class=&quot;!whitespace-pre hljs language-json&quot;&gt;&amp;nbsp;&lt;/code&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;By leveraging SSOID for logged-in users and cookies for anonymous users, we ensure a seamless shopping experience across sites. With a robust API that interacts with the cart and product information, users can enjoy a unified cart system, enhancing user satisfaction and driving conversions.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Note:- This is the work only when all sites share the same top main domain.&lt;/p&gt;</id><updated>2024-12-03T18:01:39.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Headless with Content Delivery API</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2024/10/headless-with-content-delivery-api/" /><id>&lt;p&gt;Hello Everyone,&lt;/p&gt;
&lt;p&gt;This blog will help anyone who wants to start or build an architecture of headless using content delivery api.&lt;/p&gt;
&lt;p&gt;When we shifted to headless CMS 12 my first question was how content would be fetched from the front end (I know we have direct API&#39;s for them) it&#39;s about the building JSON for those pages or blocks.&lt;/p&gt;
&lt;p&gt;So I&#39;ll share my code experience step by step (I&#39;m taking an example of a page but we can do the same for blocks)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a normal Page Type.&lt;/li&gt;
&lt;li&gt;Now how will your compiler know which page it has to run we are not directly calling it from view to controller as we are used to it in normal architecture so for that we will create ApiFilters(it will run all every call)
&lt;ul&gt;
&lt;li&gt;Register that Api Filter as Singleton like this &amp;nbsp;_services.AddSingleton&amp;lt;IContentApiModelFilter, ProductCollectionPageApiModelFilter&amp;gt;();&lt;/li&gt;
&lt;li&gt;Inside your filter, you need to check if the current request matches your type If not return and do nothing.&lt;/li&gt;
&lt;li&gt;If it matches your type then we need to create custom code to add our contentarea to our JSON results for that we will create a method where we will do our filtering and binding our model that will be passed in JSON.&lt;/li&gt;
&lt;li&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public void AddContentAreaItemsToContentApiModel(string key, ContentArea contentArea, ContentApiModel contentApiModel)
{
    var items = new Dictionary&amp;lt;string, object&amp;gt;();

    if (contentArea == null || contentArea.FilteredItems == null)
    {
        contentApiModel.Properties.Add(key, items);
        return;
    }

    var Filtereditems = contentArea.FilteredItems;
    var index = 0; //Will be appended to the item name in the contentApiModel to prevent duplicate keys in case we have multiples of the same block type 

    foreach (var contentAreaItem in Filtereditems)
    {
        _contentLoader.TryGet&amp;lt;IContent&amp;gt;(contentAreaItem.ContentLink, out var content);

        if (content == null)
            continue;

        switch (content)
        {
            // all blocks and pages that will be there in the content area create a case for it like this 
            case PageType x:
                {
                    items.Add(nameof(PageType ) + &quot;_&quot; + index, _commerceViewModelBuilder.GetPageDetails(x));
                    index++;
                    break;
                }
            default:
                {
                    index++;
                    break;
                }
        }
    }

    contentApiModel.Properties.Add(key, items);
}
       &lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;this filter will create a key-value pair which will be our JSON result.&lt;/li&gt;
&lt;li&gt;GetPageDetails -&amp;gt; method that will accept that type and create the model we want to add.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add this method to our Page type filter, and call your content area in that to filter all things example&amp;nbsp;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;  public class PageApiModelFilter : ContentApiModelFilter&amp;lt;ContentApiModel&amp;gt;
    {
        public override void Filter(ContentApiModel contentApiModel, ConverterContext converterContext)
        {
            try
            {
                if (!_contentLoader.TryGet(converterContext.ContentReference, out ProductCollectionPage productCollectionPage) || productCollectionPage is not ProductCollectionPage)
                {
                    return;
                }
               
                AddContentAreaItemsToContentApiModel(nameof(productCollectionPage.TopContentArea), productCollectionPage.TopContentArea, contentApiModel);
                AddContentAreaItemsToContentApiModel(nameof(productCollectionPage.MainContentArea), productCollectionPage.MainContentArea, contentApiModel);
                AddContentAreaItemsToContentApiModel(nameof(productCollectionPage.BottomContentArea), productCollectionPage.BottomContentArea, contentApiModel);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
  &lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;4. Now you can call your API from the front end using ID or any other means and in JSON you will have all data that was serialized.&lt;/p&gt;

&lt;p&gt;If you want to remove extra data pre and post serialization you can refer to my other blog here is the link &lt;a href=&quot;/link/a7fcc0c8245a45adb5a2cec79f0687ff.aspx&quot;&gt;https://world.optimizely.com/blogs/puneetgarg/dates/2024/9/remove-unwanted-properties-for-headless-implementation-using-content-delivery-api-/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;
&lt;p&gt;Thank you&amp;nbsp;&lt;/p&gt;



&lt;p&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2024-10-15T15:41:30.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Remove Unwanted properties for Headless Implementation using Content Delivery API </title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2024/9/remove-unwanted-properties-for-headless-implementation-using-content-delivery-api-/" /><id>&lt;p&gt;While working with Headless, whenever we want to send data to the front end, many properties are also shown in JSON that we don&#39;t wish to, which creates a big and unreadable response.&lt;/p&gt;
&lt;p&gt;To refactor that we can create Filter which will kick before and after every call.&lt;/p&gt;
&lt;p&gt;Example:-&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PreSerialization Content filter&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This class prevents the serialization of certain properties before the serialization&lt;/li&gt;
&lt;li&gt;Register that Service as Singleton&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;internal class PreSerializationContentFilter  : ContentFilter&amp;lt;IContent&amp;gt;
{
    public override void Filter(IContent content, ConverterContext converterContext)
    {
        content.Property.Remove(&quot;TopContentArea&quot;);
        content.Property.Remove(&quot;MainContentArea&quot;);
        content.Property.Remove(&quot;BottomContentArea&quot;);

        content.Property.Remove(&quot;MetaTitle&quot;);
        content.Property.Remove(&quot;MetaKeywords&quot;);
        content.Property.Remove(&quot;MetaDescription&quot;);
        content.Property.Remove(&quot;DisableIndexing&quot;);
        content.Property.Remove(&quot;EnableNoFollow&quot;);
        content.Property.Remove(&quot;OgContentType&quot;);
        content.Property.Remove(&quot;Categories&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;PostSerialization ApiModelFilter&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This generic class adds properties to the ContentApiModel after the serialization before sending the request&lt;/li&gt;
&lt;li&gt;Register that Service as Singleton&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; public class PostSerializationApiModelFilter : ContentApiModelFilter&amp;lt;ContentApiModel&amp;gt;
 {
     public override void Filter(ContentApiModel contentApiModel, ConverterContext converterContext)
     {
         try
         {
             // Set those values below as null, and configure ContentApiOption.IncludeNullValues = false in Initialization
             // then, response data will not include those ones.
             contentApiModel.ContentLink = null;
             contentApiModel.Language = null;
             contentApiModel.ExistingLanguages = null;
             contentApiModel.MasterLanguage = null;
             contentApiModel.ParentLink = null;
             contentApiModel.StartPublish = null;
             contentApiModel.StopPublish = null;
             contentApiModel.RouteSegment = null;
             contentApiModel.Changed = null;
             contentApiModel.Created = null;
             contentApiModel.Saved = null;
             contentApiModel.Status = null;
         }
         catch (Exception ex)
         {
             Console.WriteLine(ex.Message);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using these classes you can remove unwanted properties.&lt;/p&gt;
&lt;p&gt;Hope that helps !! :-) &amp;nbsp;&lt;/p&gt;</id><updated>2024-09-04T14:45:50.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Retain Cart when market is change</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2024/1/retain-cart-when-market-is-change/" /><id>&lt;p&gt;&lt;span&gt;A cart is a collection of products that are selected by a website customer.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Working on multi-market site, comes with a problem when we switch from one market to another market it does create new cart for each market.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;strong&gt;Note from optimizely&lt;/strong&gt; :- &lt;em&gt;Carts are market-dependent, so are unique to a user and market, instead of just a user. If a site lets a user switch markets, then switching the market may also switch associate carts to a new or different carts.&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;So, here is my solution for this instead of recreating the whole cart, we can just change the market fields and payment data in cart and market data in cookie(i.e for users).&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;strong&gt;Step-1&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Delete if any existing carts are present to your desitination market.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; var userId = _customerContext.CurrentContactId.ToString();
 var cartName = cart.Name;

var previousCart = _orderRepository.LoadCart&amp;lt;ICart&amp;gt;(new Guid(userId), cartName, selectedShippingAddressRegion);

if (previousCart != null)
    _orderRepository.Delete(previousCart.OrderLink);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step-2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Change Market data in current cart&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;cart.MarketId = new MarketId(destinationMarket);
var marketName = _marketManager.GetAllActiveMarkets().Where(c =&amp;gt; c.IsEnabled)
                .FirstOrDefault(x =&amp;gt; x.MarketId.Value == destinationMarket)?.MarketName;

cart.MarketName = marketName;
this._orderRepository.Save(cart);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step-3&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Update cookie with destinatin market&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; var market = GetMarket(marketId);
 SiteContext.Current.Currency = market.DefaultCurrency;
 _cookieService.Set(MarketCookie, marketId.Value);
 MarketEvent.OnChangeMarket(market, new EventArgs());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step-4&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;Updated cart amount(price) with new price from commerce manager&lt;/span&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;if (cart != null)
{
     UpdateLineItemPrice(cart);
     _orderRepository.Save(cart);
}

 public void UpdateLineItemPrice(ICart cart)
 {
    if (cart == null)
        return;
     foreach (var lineItem in cart.GetAllLineItems())
      {
        lineItem.PlacedPrice = _priceCalculationManager.GetSalePrice(lineItem.Code, cart.MarketId, cart.Currency).UnitPrice.Amount;
       }
        ValidateCart(cart);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Result:- With these steps when we change the market it will carry our current cart to new market with updated market fields and price.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Notes :-&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All service call made with dependency injection and all of them are present with optimizely liberary.&lt;/li&gt;
&lt;li&gt;Depending with impemention of cart , we might need to update shipping address as well it will be similar like what we did with price.&lt;/li&gt;
&lt;li&gt;Price should be present at the variant level in commerce manager with respect to market.&lt;/li&gt;
&lt;/ul&gt;</id><updated>2024-01-16T16:32:33.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Output Cache Options in Optimizely CMS 12</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2023/5/cache-options-in-optimizely-cms-12/" /><id>&lt;h2&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;18pt;&quot;&gt;&lt;strong&gt;Let&#39;s start with the basics what is Output Caching?&lt;/strong&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Output Cache is a feature that allows you to store the rendered output of a webpage in memory or on disk, so subsequent requests for the same page can be served directly from the cache without executing the entire page rendering process again. This helps to improve the performance and scalability of your website.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Before CMS 12 output caching was inbuilt but it got cut off in cut off in September 2021.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;However, Optimizely CMS provides caching mechanisms at various levels to optimize performance. Here are some examples:&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Fragment Caching&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Response caching&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Block Caching&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;If you are on DXP&amp;nbsp; use CDN ( Content Delivery Network )&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;Fragment Caching:-&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Fragment caching can be implemented to improve the performance of your website by caching specific parts or fragments of a page.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Example (Generic):-&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Identify the dynamic content within your page or user control that you want to cache. Let&#39;s assume you have a dynamic content block that displays the current date and time.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Wrap the dynamic content with the &lt;code&gt;CachedFragment&lt;/code&gt; control. This control defines the portion of content that will be cached. Here&#39;s an example:&lt;/span&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;@using EPiServer.Web.Mvc.Html
@using System.Web.Mvc.Html

@{
    var cacheKey = &quot;FragmentCacheKey&quot;;
    var cacheDuration = new TimeSpan(0, 10, 0); // Cache for 10 minutes
}

@Html.Cache(cacheKey, cacheDuration, () =&amp;gt;
{
    // Content to cache goes here
    &amp;lt;div&amp;gt;
        &amp;lt;!-- Your fragment content --&amp;gt;
    &amp;lt;/div&amp;gt;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Save your page or user control. The dynamic content wrapped in the &lt;code&gt;CachedFragment&lt;/code&gt; control will now be cached for subsequent requests within the specified duration.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;Block Caching:-&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Use the Block Caching feature to cache individual blocks and serve their rendered output directly from the cache on subsequent request&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Here&#39;s an example of how to use Block Caching:&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Identify the block type that you want to cache. Let&#39;s assume you have a block type called &lt;code&gt;MyBlock&lt;/code&gt; that represents a frequently accessed block.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Add the &lt;code&gt;[Cache]&lt;/code&gt; attribute to your block class, specifying the caching duration in seconds. Here&#39;s an example:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Cache(Duration = 3600)] // Cache duration of 3600 seconds (1 hour)
public class MyBlock : BlockData
{
    // Block properties
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Use the &lt;code&gt;MyBlock&lt;/code&gt; in your page or content area. The first request to render the page will generate the block&#39;s output, and it will be cached. Subsequent requests within the caching duration will retrieve the cached output instead of executing the block&#39;s code again.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;Note:- &lt;span class=&quot;ui-provider&amp;#32;cex&amp;#32;cey&amp;#32;c&amp;#32;d&amp;#32;e&amp;#32;f&amp;#32;g&amp;#32;h&amp;#32;i&amp;#32;j&amp;#32;k&amp;#32;l&amp;#32;m&amp;#32;n&amp;#32;o&amp;#32;p&amp;#32;q&amp;#32;r&amp;#32;s&amp;#32;t&amp;#32;cez&amp;#32;cfa&amp;#32;w&amp;#32;x&amp;#32;y&amp;#32;z&amp;#32;ab&amp;#32;ac&amp;#32;ae&amp;#32;af&amp;#32;ag&amp;#32;ah&amp;#32;ai&amp;#32;aj&amp;#32;ak&quot;&gt;block cache does not automatically invalidate when you publish content. By default, the block cache is not tied to the publishing process&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;Response caching&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Response caching to cache the entire output of a specific action or controller, improving the performance and reducing the load on your server.&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Adding response caching may cause a stale response to be delivered.&lt;/span&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;

[ResponseCache(Duration = 3600)] // Cache for 1 hour
public class HomeController : Controller
{
    public IActionResult Index()
    {
        // Action logic goes here
        return View();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Using response caching may cause contents to not update instantly after publishing from CMS, and causes differences among web instances of a load-balanced environment.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Response caching should not be used if you have&amp;nbsp;&lt;code class=&quot;rdmd-code&amp;#32;lang-&amp;#32;theme-light&quot;&gt;&lt;span class=&quot;cm-s-neo&quot;&gt;Html.RenderEPiServerQuickNavigatorAsync()&lt;/span&gt;&lt;/code&gt;&amp;nbsp;in Razor view.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Here is the link for the response caching.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-5.0&quot;&gt;https://learn.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-5.0&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;CDN Caching&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;Purge CDN Cache&lt;/strong&gt;&amp;nbsp;clears cached content on server proxies so that visitors can get the latest page versions.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;To configure a CDN (Content Delivery Network) with a DXP (Digital Experience Platform) account in Optimizely CMS 12, you&#39;ll need to follow these steps:&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Choose a CDN provider: Select a CDN provider that suits your needs. Some popular CDN providers include Cloudflare, Akamai, Amazon CloudFront, and Fastly.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Set up CDN with your provider: Sign up for a CDN service and follow their instructions to configure the CDN. This typically involves creating a CDN distribution or configuring an existing one. Refer to your CDN provider&#39;s documentation for detailed instructions.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Obtain CDN URL: Once your CDN is set up, you will be provided with a CDN URL or endpoint that you can use to serve your static assets.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Update DXP configuration: In your Optimizely DXP account, you need to update the configuration to use the CDN for serving static assets. This involves modifying the web.config file in your DXP solution.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;a. Access your DXP solution&#39;s web.config file.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;b. Locate the &lt;code&gt;&amp;lt;episerver.staticContent&amp;gt;&lt;/code&gt; section and add the following configuration to use the CDN URL:&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;bg-black&amp;#32;rounded-md&amp;#32;mb-4&quot;&gt;
&lt;div class=&quot;p-4&amp;#32;overflow-y-auto&quot;&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;&amp;lt;episerver.staticContent&amp;gt; &amp;lt;clientResources baseUrl=&quot;https://yourcdnurl.com&quot; /&amp;gt; &amp;lt;/episerver.staticContent&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Replace &lt;code&gt;https://yourcdnurl.com&lt;/code&gt; with the base URL of your CDN.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Verify CDN integration: After saving the web. config changes, deploy, and test your DXP solution to ensure that the static assets are being served through the CDN. Inspect the source code of your web pages and confirm that the URLs of the static assets are now pointing to the CDN domain.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Here&#39;s an example of how you can configure CDN caching with Optimizely CMS 12 using a popular CDN provider, Cloudflare:&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Enable CDN support: Sign up for a Cloudflare account and follow their setup instructions to configure your Optimizely CMS 12 website to work with Cloudflare as your CDN. This typically involves updating your DNS settings to point your domain to Cloudflare&#39;s servers.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Configure caching headers in Optimizely CMS 12: In the Optimizely CMS 12 administration interface, navigate to the &quot;Admin&quot; section and go to &quot;Configurations&quot;. Select &quot;Manage Websites&quot; and choose your website. Under the &quot;Site Settings&quot; tab, look for the &quot;Static files&quot; section.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Set the appropriate caching headers for your static files. For example, you can set the &quot;Cache-Control&quot; header to &lt;code&gt;public, max-age=86400&lt;/code&gt; to cache static files for 24 hours.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Verify CDN caching: After configuring the caching headers, you can verify that CDN caching is working by checking the caching headers returned for your assets. You can use browser developer tools or tools like cURL or Postman to inspect the response headers.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;For example, you can run the following cURL command to check the caching headers for a specific CSS file:&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;bg-black&amp;#32;rounded-md&amp;#32;mb-4&quot;&gt;
&lt;div class=&quot;p-4&amp;#32;overflow-y-auto&quot;&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;code class=&quot;!whitespace-pre&amp;#32;hljs&amp;#32;language-arduino&quot;&gt;curl -I https:&lt;span class=&quot;hljs-comment&quot;&gt;//www.example.com/css/style.css&lt;/span&gt;
&lt;/code&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Look for the &quot;Cache-Control&quot; and &quot;Expires&quot; headers in the response to ensure they reflect the desired caching behavior.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Cache invalidation: Cloudflare provides several options for cache invalidation:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Manual cache invalidation: You can manually purge or clear specific URLs from the Cloudflare cache whenever the content is updated in Optimizely CMS 12. Cloudflare provides a RESTful API that allows you to make purge requests.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Versioning or fingerprinting: Modify the URLs of your assets whenever they change by appending a version number or fingerprint to the URL. For example:&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;bg-black&amp;#32;rounded-md&amp;#32;mb-4&quot;&gt;
&lt;div class=&quot;p-4&amp;#32;overflow-y-auto&quot;&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;code class=&quot;!whitespace-pre&amp;#32;hljs&amp;#32;language-bash&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;link&lt;/span&gt; rel=&lt;span class=&quot;hljs-string&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; href=&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.example.com/css/style.css?v=2&quot;&lt;/span&gt;&amp;gt;
&lt;/code&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;This technique ensures that the updated asset is considered a new resource by the CDN cache, and it retrieves the latest version.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Time-based cache invalidation: Set shorter cache durations for specific assets that require frequent updates. For example, you can set a cache duration of &lt;code&gt;max-age=300&lt;/code&gt; (5 minutes) for a CSS file that frequently changes.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Choose the appropriate cache invalidation strategy based on your content update frequency and requirements.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Remember to consult Cloudflare&#39;s documentation for specific instructions on cache invalidation methods and API usage.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;By configuring CDN caching with Optimizely CMS 12 and Cloudflare, you can take advantage of CDN&#39;s edge caching to serve your static assets faster and reduce the load on your origin server.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;Thank you for reading it !!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family:&amp;#32;&amp;#39;times&amp;#32;new&amp;#32;roman&amp;#39;,&amp;#32;times,&amp;#32;serif;&amp;#32;font-size:&amp;#32;14pt;&quot;&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;</id><updated>2023-05-16T10:54:39.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Content Migration from one to another property for live site</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2022/4/content-migration-/" /><id>&lt;p&gt;In one of my projects I recently faced an issue where we have to change all the standard category properties to &lt;strong&gt;IList&amp;lt;ContentReference&amp;gt;&amp;nbsp;&lt;/strong&gt;and the site was live so we just couldn&#39;t take the field and get it over with.&lt;/p&gt;
&lt;p&gt;So I create new IList&amp;lt;ContentReference&amp;gt; properties and migrate the data and change the logic. Here are the steps that I followed.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;font-size:&amp;#32;18pt;&quot;&gt;&lt;strong&gt;Step To follow:-&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create new fields -- for all categories while don&#39;t remove old ones once&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a scheduled job that migrates data from the old field to the new field&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Switch the logic from old to new fields as new fields have multiple categories&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;strong&gt;&lt;em&gt;remove old fields (only after running the migration job)&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;&lt;strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;span&gt;&lt;strong&gt;Step-1:-&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;We create new &lt;strong&gt;Ilist&amp;lt;contentreference&amp;gt;&lt;/strong&gt; properties for every standard category property.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; [Display(
            Name = &quot;Something&quot;,
            GroupName = SystemTabNames.Content,
            Order = 10)]
        [Categories]
        public virtual IList&amp;lt;ContentReference&amp;gt; Something { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Step-2:-&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Here we have to write the code so that content that is already existing on the live site migrates from old to new properties.&lt;/p&gt;
&lt;p&gt;Few things that I did while creating a scheduled job:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Get all the pages where I changed the property, So for that, I created a generic method.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; private IEnumerable&amp;lt;T&amp;gt; GetDataList&amp;lt;T&amp;gt;()
            where T : ContentData
        {
            var objectType = _contentTypeRepository.Load&amp;lt;T&amp;gt;();
            var usages = _contentModelUsage.ListContentOfContentType(objectType)
                .Select(x =&amp;gt; x.ContentLink.ToReferenceWithoutVersion())
                .Distinct()
                .Select(x =&amp;gt; _contentLoader.Get&amp;lt;IContentData&amp;gt;(x))
                .OfType&amp;lt;T&amp;gt;()
                .ToList();
            return (IEnumerable&amp;lt;T&amp;gt;)usages;
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;After this method call loop through all the pages and create a writeable clone so that we change the data&lt;/li&gt;
&lt;li&gt;invoke the list and then assign the value to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; OnStatusChanged($&quot;Migration start for ChapterDetailPage&quot;);

            var chapterDetailPages = GetDataList&amp;lt;ChapterDetailPage&amp;gt;();
            if (chapterDetailPages != null)
            {
                foreach (var cdp in chapterDetailPages)
                {
                    //migrate data
                    var page = (ChapterDetailPage)cdp.CreateWritableClone();
                    page.LocationIdNew = new List&amp;lt;ContentReference&amp;gt;();

                    var hit = _getaCatRep.TryGet(new ContentReference(page.LocationID), out stdCat) ? stdCat : null;
                    if (hit != null)
                    {
                        page.LocationIdNew.Add(hit.ContentLink);
                        _contentRepository.Save(page, SaveAction.Publish, AccessLevel.NoAccess);
                    }
                }
            }

            OnStatusChanged($&quot;Migration end for ChapterDetailPage&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;Then Save the page using the&lt;strong&gt; Content Repository.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Step-3&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Change all the existing logic so that pages don&#39;t error.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Step-4&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The last step will be to remove all old properties.&lt;/p&gt;</id><updated>2022-04-26T07:00:15.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Custom radio control form element that displays the input box CMS 12</title><link href="https://world.optimizely.com/blogs/puneetgarg/dates/2022/1/customize-epi-form/" /><id>&lt;p&gt;&lt;span&gt;Episerver Forms does provide a lot of built-in functionality, but sometimes it is necessary to extend that functionality, which might give you some problems or difficulties. So in this post, I&#39;m going to share my experience while customizing the Episerver form.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Note:- I&#39;m doing this on CMS 12 and Episerver form 5.0.1&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In this, we are going to extend the ability of the &lt;em&gt;&lt;strong&gt;Selection element &lt;/strong&gt;&lt;/em&gt;by inserting a text box with them.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span style=&quot;font-size:&amp;#32;14pt;&quot;&gt;1. Custom form element type&lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-size:&amp;#32;12pt;&quot;&gt;Since form elements are blocks, you create them the same way as any form of IContent. Instead of &lt;span&gt;inheriting&amp;nbsp;&lt;/span&gt;from BlockData inherit it from &lt;/span&gt;SelectionElementBlockBase&amp;lt;T&amp;gt;, ValidatableElementBlockBase, and many others, Right now we are going to work with the selection element so we are going to use &lt;em&gt;SelectionElementBlockBase&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; public class InputBoxInChoiceElementBlock : SelectionElementBlockBase&amp;lt;ExtendedOptionItem&amp;gt;
    {
        [Display(GroupName = &quot;Information&quot;, Order = -3000)]
        [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor&amp;lt;ExtendedOptionItem&amp;gt;))]
        public override IEnumerable&amp;lt;ExtendedOptionItem&amp;gt; Items { get; set; }

        [Ignore]
        public override string PlaceHolder { get; set; }
        [Ignore]
        public override bool AllowMultiSelect { get; set; }

        public string GetDefaultSelectedString(ExtendedOptionItem item)
        {
            string defaultValue = this.GetDefaultValue();
            return this.GetDefaultSelectedString(item, defaultValue);
        }

        public string GetDefaultSelectedString(ExtendedOptionItem item, string defaultValue)
        {
            string str = item.Value;
            return (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(defaultValue) || !((IEnumerable&amp;lt;string&amp;gt;)defaultValue.Split(&#39;,&#39;)).Contains&amp;lt;string&amp;gt;(str, (IEqualityComparer&amp;lt;string&amp;gt;)StringComparer.OrdinalIgnoreCase)) &amp;amp;&amp;amp; (!item.Checked.HasValue || !item.Checked.Value || !string.IsNullOrEmpty(defaultValue)) ? string.Empty : &quot;data-f-default-value=\&quot;true\&quot;&quot;;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span style=&quot;font-size:&amp;#32;14pt;&quot;&gt;2. Create a Class that inherits from IOptionItem.&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;This class will have all the properties of the radio button that you want to show i.e Label/Caption, Value, and others.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; public class ExtendedOptionItem : IOptionItem
    {
        [DisplayName(&quot;/episerver/forms/contentediting/optionitem/caption&quot;)]
        [Display(Order = 1000)]
        public virtual string Caption { get; set; }
        [DisplayName(&quot;/episerver/forms/contentediting/optionitem/value&quot;)]
        [Display(Order = 2000)]
        [RegularExpression(&quot;([^,])*&quot;, ErrorMessage = &quot;/episerver/forms/contentediting/optionitem/containsinvalidcharacter&quot;)]
        public virtual string Value { get; set; }
        [DisplayName(&quot;/episerver/forms/contentediting/optionitem/checked&quot;)]
        [Display(Order = 3000)]
        public virtual bool? Checked { get; set; }
    }
    [PropertyDefinitionTypePlugIn]
    public class ExtendedOptionItemProperty : PropertyList&amp;lt;ExtendedOptionItem&amp;gt;
    {

    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span style=&quot;font-size:&amp;#32;14pt;&quot;&gt;&amp;nbsp;3. Your view&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-size:&amp;#32;12pt;&quot;&gt;You need to create your own rendering which somewhat looks like this. I didn&#39;t check for null in this code when you copy this does place your null checks.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;@using (Html.BeginElement(Model, new { id = formElement.Guid, @class = &quot;FormChoice&quot; + cssClasses, data_f_type = &quot;choice&quot;, aria_invalid = Util.GetAriaInvalidByValidationCssClasses(cssClasses) }, true))
{
    &amp;lt;fieldset aria-describedby=&quot;@Util.GetAriaDescribedByElementName(formElement.ElementName)&quot;&amp;gt;
       &amp;lt;legend class=&quot;Form__Element__Caption&quot;&amp;gt;@Model.Label&amp;lt;/legend&amp;gt;        
            @foreach (var item in items)
            {
                var defaultCheckedString = Model.GetDefaultSelectedString(item);
                var checkedString = string.IsNullOrEmpty(defaultCheckedString) ? string.Empty : &quot;checked&quot;;
                &amp;lt;label&amp;gt; &amp;lt;input type=&quot;radio&quot; name=&quot;@formElement.ElementName&quot; value=&quot;@item.Value&quot; class=&quot;FormChoice__Input FormChoice__Input--Radio&quot; @checkedString @defaultCheckedString data-f-datainput /&amp;gt;@item.Caption&amp;lt;/label&amp;gt;
            }        
        &amp;lt;label&amp;gt;
            &amp;lt;input type=&quot;radio&quot; id=&quot;chk&quot; name=&quot;@formElement.ElementName&quot;  class=&quot;FormChoice__Input FormChoice__Input--Radio&quot; data-f-datainput value=&quot;&quot;/&amp;gt;  Other Amount ($) &amp;lt;input type=&quot;number&quot; id=&quot;otherAmount&quot; class=&quot;FormTextbox__Input wrap&quot; data-f-datainput /&amp;gt;
        &amp;lt;/label&amp;gt;
    &amp;lt;/fieldset&amp;gt;
    @Html.ValidationMessageFor(Model)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span style=&quot;font-size:&amp;#32;14pt;&quot;&gt;4. Java Script&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;font-size:&amp;#32;12pt;&quot;&gt;Now we need to customize our text box like when you click on that particular radio button then only it shows and all. Do remember that by default Episerver form stores the value of the radio button, not the input box so we need to update it.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt; $(document).ready(function () {
        // By Default Disable Input
        $(&quot;#otherAmount&quot;).attr(&#39;disabled&#39;, true);
        $(&quot;.wrap&quot;).css(&#39;opacity&#39;, &#39;2&#39;);
        $(&quot;form input:radio&quot;).change(function () {
            if ($(&quot;#chk&quot;).is(&quot;:checked&quot;)) {
                $(&quot;#otherAmount&quot;).attr(&#39;disabled&#39;, false);
                $(&quot;.wrap&quot;).css(&#39;opacity&#39;, &#39;2&#39;);
            }
            else {
                $(&quot;#otherAmount&quot;).attr(&#39;disabled&#39;, true);
                $(&quot;#otherAmount&quot;).val(&#39;&#39;);
                $(&quot;.wrap&quot;).css(&#39;opacity&#39;, &#39;1&#39;);
            }
        });
        //Updating value of radio button
        $(&quot;#otherAmount&quot;).blur(function () {
            if ($(&quot;#chk&quot;).is(&quot;:checked&quot;)) {
                var otherAmountVal = parseInt($(&quot;#otherAmount&quot;).val());
                $(&quot;#chk&quot;).attr(&quot;value&quot;, otherAmountVal);
            }
        });
    });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;That&#39;ll you need to customize the Episerver form.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Thank you&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Puneet Garg&lt;/span&gt;&lt;/p&gt;</id><updated>2022-01-28T11:00:18.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>