<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Pawan</title><link href="http://world.optimizely.com" /><updated>2025-07-21T19:25:10.0000000Z</updated><id>https://world.optimizely.com/blogs/pawan/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Optimizely Forms: Forcing New Submission on Every Post</title><link href="https://world.optimizely.com/blogs/pawan/dates/2025/7/optimizely-forms---bypass-form-cookies-while-submitting/" /><id>&lt;p&gt;By default, &lt;strong&gt;Optimizely Forms&lt;/strong&gt; uses browser cookies to track submissions and updates the existing submission if the same user submits the form again. While this behavior works well in most scenarios, we had a &lt;strong&gt;custom business requirement&lt;/strong&gt;: for certain forms, &lt;strong&gt;each submission needed to be treated as a new entry&lt;/strong&gt;, regardless of whether it came from the same user or browser session. At the same time, all other forms in the solution were expected to retain the &lt;strong&gt;default cookie-based behavior&lt;/strong&gt;.&lt;br /&gt;&lt;strong&gt;&lt;br /&gt;Initially, this seemed complex&lt;/strong&gt;, but after inspecting the Optimizely Forms framework, we discovered that the &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;ProgressiveSubmitInfoService&lt;/span&gt; class could be leveraged to inject this behavior in a clean and maintainable way, as shown below.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Forms.Core.Internal;
using EPiServer.Forms.Core.Models.Internal;
namespace alloy_example.Customization.Forms;

public class ExtendedProgressiveSubmitInfoService : ProgressiveSubmitInfoService
{
    public override ProgressiveSubmitInfo GetProgressiveSubmitInfo(Guid formContentGuid, HttpContext httpContext,
        string formLanguage)
    {
        //var isPathFound = httpContext.Request.Path.StartsWithSegments(RootPath); // e.g. can be checked based on httpContext 
        if (!string.IsNullOrEmpty(httpContext.Request.Form[&quot;TestElement&quot;]))
        {
            return null;
        }
        return base.GetProgressiveSubmitInfo(formContentGuid, httpContext, formLanguage);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above code check if Element &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;TestElement&lt;/span&gt; is found under httpContext after submission, this it will simply return null and forcing optimizely framework to generate new submissionId for given form submission.&lt;br /&gt;&lt;br /&gt;As last step, make sure to register in DI container as below.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Forms.Core.Internal;

services.AddSingleton&amp;lt;ProgressiveSubmitInfoService, ExtendedProgressiveSubmitInfoService&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This overrides the default service and enables custom logic while keeping the framework&#39;s core behavior intact.&lt;br /&gt;&lt;br /&gt;Hope, it helps someone!&lt;/p&gt;</id><updated>2025-07-21T19:25:10.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Disable Report and Dashboard Menu content editors</title><link href="https://world.optimizely.com/blogs/pawan/dates/2025/7/disable-menu-for-content-editors/" /><id>&lt;p&gt;We had a requirement where &lt;strong&gt;Report and Dashboard&lt;/strong&gt; menus were needed only &lt;strong&gt;for specific roles&lt;/strong&gt;/users Or &lt;strong&gt;Admins&lt;/strong&gt; &amp;mdash; but &lt;strong&gt;not for editors&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Below is default view for editors where&amp;nbsp; you can see these menus:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/72d9c52674db4b4192af12ece44f346d.aspx&quot; alt=&quot;&quot; width=&quot;136&quot; height=&quot;176&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But required view is:&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;/link/3beeaa0e7076471f97bbd5bf2c7c7098.aspx&quot; alt=&quot;&quot; width=&quot;126&quot; height=&quot;173&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;1.&amp;nbsp; To achieve above, we implemented a &amp;nbsp;&lt;strong&gt;custom Interceptor class &lt;/strong&gt;that wraps another &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;IMenuProvider&lt;/span&gt; and overrides the authorization logic as below.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Shell.Navigation;
namespace alloy_example.Customization.Menus;

public class MenuProviderInterceptor : IMenuProvider
{
    private readonly IMenuProvider _menu;

    public MenuProviderInterceptor(IMenuProvider menu)
    {
        _menu = menu;
    }

    public IEnumerable&amp;lt;MenuItem&amp;gt; GetMenuItems()
    {
        var menuItems = _menu.GetMenuItems().ToList();
        menuItems.ForEach(item =&amp;gt;
        {           
            item.AuthorizationPolicy = &quot;episerver:cmsadmin&quot;;
        });
        return menuItems;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;2. we need to hook this interceptor only for specific providers &amp;mdash; like&amp;nbsp;&lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;ReportsMenuProvider&lt;/span&gt; or &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;DashboardMenuProvider&lt;/span&gt; based on requirement.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;services.Intercept&amp;lt;IMenuProvider&amp;gt;(interceptorFactory);

private static IMenuProvider interceptorFactory(IServiceProvider provider1, IMenuProvider provider2)
 {
     var types = new[] { typeof(ReportsMenuProvider), typeof(DashboardMenuProvider) };
     return types.Any(t =&amp;gt; t.Equals(provider2.GetType())) ? new MenuProviderInterceptor(provider2) : provider2;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, &lt;strong&gt;only ReportsMenuProvider and DashboardMenuProvider&lt;/strong&gt; are affected &amp;mdash; all other menus remain untouched. Hope this helps someone!&lt;/p&gt;</id><updated>2025-07-20T14:39:07.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Importing Form Submission Data into Optimizely Forms Using DdsPermanentStorage</title><link href="https://world.optimizely.com/blogs/pawan/dates/2025/7/optimizely-forms--creating-submission-data-dynamically--/" /><id>&lt;div&gt;Recently, we needed to import a large number of records from a third-party system into &lt;strong&gt;Optimizely Forms&lt;/strong&gt;. The data was available in CSV format, and our goal was to insert this data as &lt;strong&gt;form submissions&lt;/strong&gt; within the Optimizely platform.&lt;br /&gt;
&lt;h4&gt;&#128269; The Challenge&lt;/h4&gt;
&lt;p&gt;During our initial analysis, we discovered that &lt;strong&gt;Optimizely Forms does not offer a built-in API&lt;/strong&gt; to directly insert or update form submissions programmatically.&amp;nbsp;While the method&amp;nbsp;&lt;strong&gt;&lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;PerformDataSubmit()&lt;/span&gt;&lt;/strong&gt; handles submission automatically through the UI (taking care of cookies and submission IDs), it relies on &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;HttpContext,&lt;/span&gt; which we &lt;strong&gt;don&amp;rsquo;t have access to in a scheduled job&lt;/strong&gt; environment&amp;mdash;like in our one-time migration scenario.&lt;/p&gt;
&lt;img src=&quot;/link/a4a3fb3cffc34a0daeb9f9a56c4f4775.aspx&quot; width=&quot;629&quot; height=&quot;53&quot; /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h4&gt;✅ The Solution: &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;DdsPermanentStorage&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;After some investigation, we found that the internal class &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;&lt;strong&gt;DdsPermanentStorage&lt;/strong&gt;&lt;/span&gt; can be used to directly interact with the Optimizely Forms&#39; data store. This allowed us to &lt;strong&gt;insert and update form submissions programmatically&lt;/strong&gt; without depending on the httpContext.&lt;br /&gt;&lt;img src=&quot;/link/6116532dea3e48699c54fdff4d2570a9.aspx&quot; width=&quot;639&quot; height=&quot;244&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We utilized this class in a scheduled job and were able to import all records successfully.&lt;/p&gt;
&lt;p&gt;&#128312;&amp;nbsp;&lt;strong&gt;Important:&lt;/strong&gt; To ensure these submissions show up correctly in the &lt;strong&gt;Form Submissions view under Edit Mode&lt;/strong&gt;, you must include the &lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;SYSTEMCOLUMN_Language&lt;/span&gt; field&amp;nbsp;during creation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a7307e8e6c504a03a96544a3fb218634.aspx&quot; width=&quot;648&quot; height=&quot;51&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4&gt;&#128338; Handling Custom Submission DateTime&lt;/h4&gt;
&lt;p&gt;While everything was working well, we noticed that the &lt;strong&gt;submission timestamp&lt;/strong&gt; (&lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;Created&lt;/span&gt; date) was &lt;strong&gt;automatically generated by the system&lt;/strong&gt; at the time of insertion.&amp;nbsp;However, our source data contained &lt;strong&gt;historical submission timestamps&lt;/strong&gt;, and it was &lt;strong&gt;important for reporting and UI display&lt;/strong&gt; that we preserve these original dates.&lt;/p&gt;
&lt;p&gt;To solve this, we went one step further and &lt;strong&gt;overridden the default behavior&lt;/strong&gt; to allow setting a custom submission date. We achieved this by &lt;strong&gt;extending&amp;nbsp;&lt;/strong&gt;&lt;span style=&quot;background-color: rgb(236, 240, 241);&quot;&gt;DdsPermanentStorage&lt;/span&gt; as below.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Data.Dynamic;
using EPiServer.Forms.Core.Data;
using EPiServer.Forms.Core.Models;

namespace Alloy.Customization.ExtendedDdsPermanentStorage;

public class ExtendedDdsPermanentStorage : DdsPermanentStorage
{
    public const string SYSTEMCOLUMN_SubmitTime = &quot;SYSTEMCOLUMN_SubmitTime&quot;;
    protected override void EnsureFormFieldsExistInPostData (FormIdentity formIden, ref PropertyBag postData, out IDictionary&amp;lt;string, Type&amp;gt; typesBag)
    {
        DateTime.TryParse(Convert.ToString(postData[SYSTEMCOLUMN_SubmitTime]), out DateTime submitedTime);
        base.EnsureFormFieldsExistInPostData(formIden, ref postData, out typesBag);
        if (submitedTime &amp;gt; DateTime.MinValue)
        {
           postData[SYSTEMCOLUMN_SubmitTime] = submitedTime;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;&#129513; Dependency Injection Setup&lt;/h4&gt;
&lt;p&gt;Since this custom class may need to be reused in other parts of the solution (or injected), &lt;strong&gt;don&amp;rsquo;t forget to register it in the DI container&lt;/strong&gt; if you&#39;re wrapping it inside a service or using Optimizely&amp;rsquo;s built-in services.&lt;/p&gt;
&lt;p&gt;Hope this post helps someone facing the same challenge!&lt;/p&gt;
&lt;/div&gt;</id><updated>2025-07-16T19:35:00.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>