Blogs2024-03-27T14:01:31.0000000Z/blogs/Optimizely WorldLessons in Experimentation from Sesame Street/blogs/kevin-schofield/dates/2024/3/seame-street-blog/2024-03-27T14:01:31.0000000Z<p><span style="font-weight: 400;">A few months ago, I was watching Sesame Street with my four-year-old son. The Sesame Street team was building two cardboard ramps for Elmo and Rosita to race ping pong balls. After the first race ended, everyone celebrated Elmo’s victory – but Rosita wanted to know why Elmo’s ball was faster than hers. </span></p>
<p><span style="font-weight: 400;">Fortunately, there was a framework in place to answer these types of questions: </span></p>
<ul>
<li><span style="font-weight: 400;">I wonder… </span></li>
<li><span style="font-weight: 400;">What if... </span></li>
<li><span style="font-weight: 400;">Let’s try! </span></li>
</ul>
<p><span style="font-weight: 400;"><img src="/link/e6b4967039cb4606bae43f29a19f6654.aspx" width="620" alt="" height="256" /></span></p>
<p><span style="font-weight: 400;">Elmo, Rosita, and Julia started talking about the differences in the two ramps. They were both made of cardboard, and they had the same ball roll down them. However, Julia noticed that Elmo’s ramp was higher than Rosita's.</span></p>
<p><span style="font-weight: 400;"><img src="/link/3b75744ddd3a491e92d7f9d1383e48c1.aspx" width="619" alt="" height="315" /></span></p>
<p><span style="font-weight: 400;">This bit of knowledge helped Rosita form a hypothesis. If she built her ramp as high as Elmo’s, her ball just might go as fast as Elmo’s in the next race. From there, the team raised Rosita’s ramp to be the same size and they put this theory to the test. </span></p>
<p><span style="font-weight: 400;">Mission accomplished. Rosita and Elmo tied, all thanks to the Sesame Street team’s experiment. </span></p>
<p><span style="font-weight: 400;">The notion of “experimentation” may trigger different thoughts for different people. To some, an experiment involves scientists working in a lab. Other people will think of an artist who likes to change up the color, depth, or tone of their artwork to spark different reactions. And some people will envision a marketing team launching two ads for the same promotion to see what resonates most with their customers. </span></p>
<p><span style="font-weight: 400;">On the surface, these three hypothetical examples may seem extremely different, but they have one thing in common: they all follow the same basic experimentation framework that was outlined in Sesame Street. </span></p>
<ul>
<li><strong>I wonder</strong><span style="font-weight: 400;"> – The team is starting to define what the problem is. They draw on all they can see and even past experiences/experiments to create a solution. </span></li>
<li><strong>What if</strong><span style="font-weight: 400;"><strong> </strong>– The team creates a hypothesis to solve the problem. They walk through what they think they need to do to solve the problem. </span><span style="font-weight: 400;"><br /></span><strong>Let's try!</strong><span style="font-weight: 400;"> – The team implements their changes to see if that solves the problem. They run the experiment and then socialize the results. </span></li>
</ul>
<p><span style="font-weight: 400;">Experimentation doesn't need to be a massive undertaking, or scary to start. This simple concept provides a framework for thinking and helps lay the foundation for testing in digital platforms with tools like Optimizely Web Experimentation. And the best part? It shows that getting started with experimentation doesn’t have to be complicated! </span></p>New Features AI-Assistant for Optimizelyhttps://optimizely.blog/2024/03/new-features-ai-assistant-for-optimizely/2024-03-24T18:08:28.0000000ZThe AI assistant was launched in May 2023, and since then, the plugin has received 12 updates featuring innovative functionalities. Here, you will find a summary of the most recent updates, along with accompanying screen videos and images.
Converting FormContainerBlock Types in Optimizely Forms for CMS 12https://cjsharp.com/dev/converting-formcontainerblock-types-in-optimizely-forms-for-cms-122024-03-24T00:00:00.0000000Z<p>While this is not something you would commonly do, here's how I approached converting a <code>FormContainerBlock</code> type to our custom <code>ExtendedFormContainerBlock</code> in Optimizely Forms for CMS 12. You may need to do this if you want to add custom properties or functionality to the standard <code>FormContainerBlock</code> type, and you don't want to rebuild or recreate all previously created forms.</p>
Top tip: Better, do not save EPiServer.Foms submissions for sensitive data/blogs/K-Khan-/Dates/2024/3/toptip-stop-indexing-users-uploaded-files/2024-03-22T10:27:12.0000000Z<p><span>If your website utilizes EPiServer.Forms and includes forms where users can upload files, there is a significant probability that the Find/Search Indexing Job will also index those files. Consequently, these files may become accessible through searches facilitated by Find. Editors navigating the Editor area may encounter these files when searching for images, potentially leading to public availability of search results also depending on implementations. To address this issue, a straightforward solution is to cease indexing user-uploaded files. One possible approach to prevent the indexing of uploaded files from forms is outlined in the code below.</span></p>
<pre class="language-csharp"><code>ContentIndexer.Instance.Conventions.ForInstancesOf<IContentMedia>().ShouldIndex(x =>
_contentLoader.GetAncestors(documentFileBase.ParentLink).Select(x=>x.Name).Contains( EPiServer.Forms.Constants.FileUploadFolderName));
</code></pre>
<p>This will stop indexing users' uploaded files, and certainly slow down the indexing job as we will be loading ancestors.</p>
<p><span>It's important to note that despite this adjustment, users' uploaded files will remain accessible to all editors through the Form Submissions View. Depending on the sensitivity of the uploaded user's data, it's imperative to consider this accessibility. Ideally, in cases where user data is sensitive, refrain from saving form submissions within forms due to the limited security associated with form submissions. </span></p>
<p><span>Editors play a pivotal role in designing forms, and their training is crucial, particularly in alignment with the nature of the business, the type of information they will be gathering, and the relevant legislation. Training should ensure that editors understand the intricacies of data collection, its implications, and compliance requirements. </span></p>Keeping local environments in sync with your Cloud environments/blogs/elias-lundmark/dates/2024/3/keeping-local-environments-in-sync-with-your-cloud-environments/2024-03-21T12:08:12.0000000Z<p>We recently announced that we are <a href="/link/2502974a919b43be954568f15310b0f0.aspx">improving the scalability of SQL databases in DXP Cloud Services</a>, this new architecture also enhances our overall security for SQL databases where we are aiming to harden technical controls to maintain confidentiality and integrity of our customers data. This change had an unintended consequence though – it disallows developers from connecting local development environments directly to SQL databases in DXP Cloud Services. We strongly advise against this practice, while the ease of use and flexibility is great, manually managing and storing connection strings and credentials for service users greatly increases the risk of these credentials falling into the wrong hands, allowing potential attackers to access or modify data.</p>
<p>To avoid these risks, our new architecture disallows direct connections from third-party sources to SQL Servers running in DXP Cloud Services. Instead, you should use the paasportal or the API to export your databases and content to use in your local development environments, which are more secure and reliable methods.</p>
<h2>How to export content</h2>
<p>Via the paasportal</p>
<ol>
<li>Navigate to <a href="https://paasportal.episerver.net">https://paasportal.episerver.net</a> and select the project you wish to export a database from</li>
<li>Navigate to the Troubleshoot tab</li>
<li>In the ‘Export Database’ section, select the environment you wish to export the database from, and how long the paasportal should retain this copy.</li>
<li>Once the export is done, click the database file to download it as .bacpac. These files can then be used to import your database to a local SQL server, or an Azure SQL Server.</li>
</ol>
<p>Via API with Powershell</p>
<ol>
<li>Navigate to <a href="https://paasportal.episerver.net">https://paasportal.episerver.net</a> and generate credentials as described here <a href="https://docs.developers.optimizely.com/digital-experience-platform/docs/authentication">https://docs.developers.optimizely.com/digital-experience-platform/docs/authentication</a>.</li>
<li>Authenticate woth Connect-EpiCloud, <em>Connect-EpiCloud -ClientKey <ClientKey> -ClientSecret <ClientSecret> -ProjectId <ProjectId></em></li>
<li>Start a database export with Start-EpiDatabaseExport, for example Start-EpiDatabaseExport -Environment Integration -DatabaseName epicms -Wait</li>
<li>Fetch the download link for the .bacpac with Get-EpiDatabaseExport</li>
</ol>
<p>Via the API you can also download BLOBs from the storage account, where Get-EpiStorageContainer allows you to list all storage containers and GetEpiStorageContainerSasLink creates a SAS URI that can be used to download BLOBs. For example,</p>
<p> Get-EpiStorageContainerSasLink -ProjectId "2372b396-6fd2-40ca-a955-57871fc497c9" `</p>
<p> -Environment "Integration" `</p>
<p> -StorageContainer "mysitemedia" `</p>
<p> -RetentionHours 2</p>Improve user experience with restriction information/blogs/linh-nguyen/dates/2024/3/improve-user-experience-with-restriction-information/2024-03-19T12:50:16.0000000Z<p><span>In </span><a href="https://nuget.optimizely.com/package/?id=EPiServer.CMS.UI&amp;amp;amp;amp;amp;v=12.28.0">EPiServer.CMS.UI 12.28.0</a><span>, an improvement for restriction information is added.</span></p>
<p><span>Previously the restriction information takes quite a lot of space, especially for big sites with a long list of restricted types. </span></p>
<p><span>This new improvement reduces the space significantly while still gives editors information about restricted types they need to know.</span></p>
<p><span><strong>Before</strong>:</span></p>
<p><span><img src="/link/8721334056e4478b86ce88bbdfcc4eb1.aspx" /></span></p>
<p><span><strong>Now</strong>:</span></p>
<p><span><img src="/link/58919e9cea13494eb9dc585e8dedf8c0.aspx" /></span></p>
<p><span>Users can expand the list to view all restricted types, and collapse it if they want.</span></p>
<p><span><img src="/link/edd27fe749474dcea6a1494c30780069.aspx" /></span></p>Introducing Optimizely Graph indexing modes for CMS Content properties/blogs/nguyen-nguyen/dates/2024/3/exclude-cms-content-properties-from-being-indexed-in-optimizely-graph/2024-03-19T08:35:59.0000000Z<p>In the current release of Optimizely Graph CMS integration packages, we introduced a new feature called indexing modes for CMS content properties, you can read how to configure the indexing modes for individual properties <a href="https://docs.developers.optimizely.com/platform-optimizely/v1.4.0-optimizely-graph/docs/installation-and-configuration#property-indexing-modes">here</a>.</p>
<p>The indexing modes can be set to a property in a typed content model by adding a C# attribute to the property. At the time of this writing, only typed models are supported and support for untyped models may come in the future.</p>
<p>The two available indexing modes at the moment are</p>
<ul>
<li><strong>OutputOnly</strong>: the property will be stored in the content in Optimizely Graph, however it cannot be used to filter contents, it will not be possible to find the content by the values of the property using fulltext search, either (AKA not searchable).</li>
<li><strong>Default</strong>: the property can be retrieved, can be used to filter contents (using where), but not searchable.</li>
</ul>
<p>An example of such configuration is</p>
<div>
<div>
<pre class="language-csharp"><code>[GraphProperty(PropertyIndexingMode.OutputOnly)]
[Searchable]
public virtual string Title { get; set; }</code></pre>
</div>
<p>Notice how the property is set to be searchable by the attribute [Searchable] in CMS, however the indexing mode will override the [Searchable] attribute and the Title property ends up not searchable and not filterable.</p>
<p>What is the motivation for these indexing modes?</p>
<p>By default, most properties are searchable and filterable when indexed to Optimizely Graph, however for most of the properties in contents, being able to filter or search by them is not necessary. To make properties filterable and searchable, extra processing and data need to be handled by Optimizely Graph, this affects performance and costs.</p>
<p>So as a CMS/Optimizely Graph customer, it is of your interest to set most of your properties to OutputOnly mode, so that the values can be retrieved and displayed, while reducing load in Optimizely Graph and as a result improving the performance in both indexing and querying operations. Optimizely Graph also has some limitations on the maximum number of properties that are searchable and indexable, so if you run into this issue setting most of your properties to OutputOnly will solve it.</p>
<p>The feature should be available in Optimizely Graph CMS integration package version 3.6.0. Please check it out.</p>
</div>Alt text helper for Optimizely CMS 12 and Optimizely DAMhttps://www.david-tec.com/2024/03/alt-text-helper-for-optimizely-cms-12-and-optimizely-dam/2024-03-18T12:09:52.0000000ZAuthor: Seobility - License: CC BY-SA 4.0 Optimizely CMS 12 offers out the box integration with Optimizely DAM allowing users to easily select images from the DAM. Its also possible to access the meta data for these assets such as the alt text. One of the most common requests I hear is "How do I access the alt text for the image". Implementors can render alt text to a page using ASP.net MVC or...Create A Feature Flag In Optimizely Feature Experimentation Within 10 Mins 😲https://www.jondjones.com/learn-optimizely/experimentation/create-a-feature-flag-in-optimizely-feature-experimentation-within-10-mins/2024-03-17T00:00:00.0000000ZWithin this article, you will learn everything you need to know in order to create a feature flags using Optimizely Feature Experimentation). Optimizely feature experimentation comes with over 92 ready-made SDKs built against the most popular progra...Why AB Tests On SPAs & Dynamic Websites FAIL AND how YOU can save the day!https://www.jondjones.com/learn-optimizely/experimentation/why-ab-tests-on-spas-dynamic-websites-fail-and-how-you-can-save-the-day/2024-03-17T00:00:00.0000000ZIf you are considering running AB Tests on your JavaScript powered website a lot can got wrong. Testing is possible within a SPA, SSG, or SSR powered site, however, without some key features that are easy to overlook AB testing might not be possible....Changing the Visitor Submit Timeout in Optimizely Forms for CMS 12https://cjsharp.com/dev/changing-the-visitor-submit-timeout-in-optimizely-forms-for-cms-122024-03-15T00:00:00.0000000Z<p>When working with Optimizely Forms, you'll find there are multiple ways to configure the functionality of a form. One of those options an editor can configure is "Allow multiple submissions from the same IP/cookie", which is commonly used to prevent/deny a visitor from resubmitting the form, and it works by creating a browser cookie with the form's information.</p>
<p>While this can be a useful option to have to prevent a visitor from continually resubmitting a form, it's not foolproof. A visitor could easily resubmit the form in incognito mode (private browsing), or simply clear their browser cookies to resubmit the form, or just wait until the cookie expires.</p>
<p>But what if you want to allow the user to resubmit the form after a certain amount of time? Let's look at how we can change the timeout for a form resubmission.</p>
Moving IIS URL Rewrite Rules to .NET Core: A Practical Guidehttps://blog.paulmcgann.dev/2024/03/moving-iis-url-rewrite-rules-to-net.html2024-03-12T21:56:00.0000000Z<br />In the fast-paced world of web development, staying up-to-date with the latest technologies is crucial. As businesses evolve, so do their digital needs. One common challenge faced by developers is migrating URL rewrite rules from IIS (Internet Information Services) to .NET Core. If you're embarking on this journey, fear not! This guide will walk you through the process in a clear and practical manner.<br /><br /><b>Understanding the Basics</b><br /><br />URL rewriting plays a crucial role in managing web traffic and optimizing user experience. It involves intercepting incoming requests and redirecting them to different URLs based on predefined rules. In the IIS environment, these rules are typically configured in the web.config file. However, with .NET Core, URL rewriting is handled differently, using middleware within the application itself.<br /><br /><b>Step 1: Assess Your Current Setup</b><br /><br />Before diving into migration, it's essential to understand your existing URL rewrite rules configured in the web.config file. Take stock of each rule, noting down its pattern and rewrite destination.<br /><br /><b>Step 2: Install the Microsoft.AspNetCore.Rewrite Package</b><br /><br />To handle URL rewriting in .NET Core, you'll need to install the Microsoft.AspNetCore.Rewrite package. This can be done easily using the .NET CLI or NuGet Package Manager in Visual Studio.<br /><pre><code class="bash">
dotnet add package Microsoft.AspNetCore.Rewrite
</code></pre>
<br /><b>Step 3: Configure URL Rewriting Middleware</b><br /><br />Once the package is installed, you'll configure the URL rewriting middleware in the Startup.cs file of your .NET Core application.<br />
<pre><code class="csharp">
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Rewrite;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Add any necessary services here
}
public void Configure(IApplicationBuilder app)
{
// Other middleware configurations
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddIISUrlRewrite(iisUrlRewriteStreamReader);
app.UseRewriter(options);
}
// Additional middleware configurations
}
}
</code></pre>
<br /><b>Step 4: Migrate URL Rewrite Rules</b><br /><br />Now, it's time to migrate your existing URL rewrite rules from the web.config file to the IISUrlRewrite.xml file. You can keep the exisiting rewrite element with all your current iis rewrite rules in place, see the below.<div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEikduDTh1GlrFsDUVcYXekicOfjHh3HdnZeZ5-emlChsyEjj3NXMffkK9kyqLHJiRrnEv7cII7Br5yj-GPCdND45Wt46NqOoOX0PRU8hyfPMzqyGK1ly-lIUOa2wiIqFSIAZiCkSvRcWf-sp6i6xSXbiWCdyPhxv4taqpupQ8KQyQ4lhkC0qPhj-mx9oyE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="502" data-original-width="1857" height="157" src="https://blogger.googleusercontent.com/img/a/AVvXsEikduDTh1GlrFsDUVcYXekicOfjHh3HdnZeZ5-emlChsyEjj3NXMffkK9kyqLHJiRrnEv7cII7Br5yj-GPCdND45Wt46NqOoOX0PRU8hyfPMzqyGK1ly-lIUOa2wiIqFSIAZiCkSvRcWf-sp6i6xSXbiWCdyPhxv4taqpupQ8KQyQ4lhkC0qPhj-mx9oyE=w578-h157" width="578" /></a></div><br /><br />
<br /><b>Step 5: Test and Iterate</b><br /><br />After migrating your rules, thorough testing is essential. Verify that all URLs are being rewritten / redirected correctly without any unexpected redirects or errors.<br /><br /><b>Conclusion</b><br /><br />Migrating URL rewrite rules from IIS web.config to .NET Core as part of an CMS 12 upgrade may seem daunting, but with proper guidance, it can be a smooth transition. By following the steps outlined in this guide, you can ensure that your application's URL rewriting functionality remains intact while embracing the capabilities of .NET Core.<br /><br />Remember, migration is an opportunity to optimize and improve your application for the future. So, embrace the change, and happy migrating!</div>Building your own Semantic Search Implementationhttps://jhoose.co.uk/?p=4532024-03-09T21:03:57.0000000ZThe post discusses incorporating semantic search on websites, focusing on technology used, large language models, vector databases, and a demo application. It emphasizes using Python, Java, and JavaScript for AI/ML development and provides code snippets for indexing and searching. The post encourages learning and exploring alternative solutions beyond recognized search providers. <a href="https://jhoose.co.uk/2024/03/09/building-your-own-semantic-search-implementation/">Continue reading <span class="meta-nav">→</span></a>Varchar can be harmful to your performancehttps://vimvq1987.com/?p=30292024-03-07T14:52:07.0000000ZAs string is the most common data type in an application, nvarchar and its variant varchar are probably the most common column types in your database. (We almost always use nvarchar because nchar is meant for fixed length columns which we don’t have). The difference is that nvarchar has encoding of UTF-16/USC-2 while varchar has … <a href="https://vimvq1987.com/varchar-can-be-harmful-to-your-performance/" class="more-link">Continue reading <span class="screen-reader-text">Varchar can be harmful to your performance</span> <span class="meta-nav">→</span></a>Stockholm Optimizely Tech Meetup March 11th 2024https://optimizely.blog/2024/03/stockholm-optimizely-tech-meetup-2024/2024-03-07T06:51:18.0000000ZDo not miss the opportunity to discover the powerful AI assistant for Optimizely CMS and Commerce when I demonstrate its time-saving features at the upcoming Live Optimizely community event in Stockholm.
Exploring SaaS CMS: API Clients and Content Management APIs/blogs/Minesh-Shah/Dates/2024/3/exploring-saas-cms-api-clients-and-content-management-apis/2024-03-06T20:29:56.0000000Z<h2>Introduction</h2>
<p>In continuation of my <a href="/link/807ad30b59c140a6baec9c7633a5a256.aspx">previous post</a> on leveraging the Content Management API and OpenID Connect Authentication on the PaaS-based Optimizely CMS, I delve into the delivery mechanisms within the SaaS-based CMS Platform. Surprisingly, the majority of functionalities are readily available and seamlessly integrated into the system. In this article, I provide a quick preview of the available features and guide on configuring the exposed API for definition and content management.</p>
<p><strong>Note:</strong> It's essential to bear in mind that while I explore these features, the SaaS platform is still in its BETA phase, and APIs are currently at version 0.5. Changes might occur as the SaaS CMS transitions into general availability.</p>
<h2>Configuring the API Client (Equivalent to OpenID Connect Package)</h2>
<p>The API client functionality comes pre-installed in the SaaS CMS, and the setup is near identical to what we found on the PaaS platform with the Open ID Connect package. The tool can be found within Settings and the Access Rights section as highligted in the image below. </p>
<p>Once on the API Client interface we can create a Client ID, the Client Secret is automatically generated please store this safely as there is no way of retrieving once you leave the page. The option to "Allow the client to impersonate users" is self-explanatory; enabling this allows the client to function as another user within the CMS.</p>
<p><img src="/link/2a7d3af9b80749d189b9c3ddc6fa2287.aspx" /></p>
<h3>Using the credentials</h3>
<p>I'll demonstrate how to utilize these credentials using Postman to retrieve a JWT token for subsequent API calls. To obtain a Bearer Auth Token, a GET call needs to be made to the designated URL:</p>
<pre class="language-markup"><code>https://app-xxxprod.cms.optimizely.com/_cms/v0.5/oauth/token</code></pre>
<p><span>Sending the required parameters along with the request, including </span><code>grant_type</code><span>, </span><code>client_id</code><span>, </span><code>client_secret</code><span>, and optionally </span><code>act_as</code><span>, will result in the generation of a token for future requests. Notably, this token expires automatically after 300 seconds.</span></p>
<p><span>Example: </span></p>
<p><span><img src="/link/afad4afd59dd4d66b7dc4837d4a088e6.aspx" /></span></p>
<h2>Authorisation to API using the Bearer Token</h2>
<p>With the Bearer Token generated, subsequent API requests can now be authenticated by passing this token as the "Authorization" Header parameter.</p>
<p>Example:</p>
<p><img src="/link/73a267ca07894182883fda04000e5583.aspx" /></p>
<h2>Content Definitions API</h2>
<p><span>Now armed with the bearer token, we can interact with the Content and Definitions API. The first API we explore is the Content Definitions API. Detailed API reference can be found </span><a href="https://docs.developers.optimizely.com/content-management-system/v1.0.0-SaaS-Core/reference/contenttypes_list">here</a><span>.</span></p>
<h3>Get Content Types</h3>
<p><span>A simple GET request to the following URL provides a list of all content types within the CMS:</span></p>
<pre class="language-markup"><code>https://app-xxxprod.cms.optimizely.com/_cms/v0.5/contenttypes</code></pre>
<p>Example: </p>
<p><img src="/link/c9015e217b714b16ba0c466475e46fa2.aspx" /></p>
<p>To only get the details of a certain known type we pass in the definition name (key) to the URL:</p>
<pre class="language-markup"><code>https://app- xxxprod.cms.optimizely.com/_cms/v0.5/contenttypes/articlepage</code></pre>
<h3>Create Content Type</h3>
<p>To create a content type, a POST request is made to the same URL, passing in the necessary parameters.</p>
<p>Example: </p>
<p><img src="/link/593612ffa8464867aa73b1f11a9f202f.aspx" /></p>
<p><img src="/link/1c718e89bd7144c696bb0078b05f4c24.aspx" /></p>
<h2>Content {Delivery} API</h2>
<p>The API reference to the Content API can be found here : <a href="https://docs.developers.optimizely.com/content-management-system/v1.0.0-SaaS-Core/reference/content_create">Create content (optimizely.com)</a></p>
<h3>Get Content</h3>
<p>To retrieve a content item, a GET request is made to the designated URL, using the Guid of the page as the key. The key is in a UUID format so should not include any dashes e.g. 115988243510434482925671c3ee601a</p>
<pre class="language-markup"><code>https://app- xxxprod.cms.optimizely.com/_cms/v0.5/content/{key}</code></pre>
<p>Example: </p>
<p><img src="/link/418614fbd0f245c4b586f1b1ca1ea884.aspx" /></p>
<h2>Conclusion</h2>
<p>As you can see its very easy to interact with the API’s and retrieve the relevant information you may need, as well as programmatically being able to create Content Models and Instances of these models. Its great to see this has all been included from the get go and provides a lot of scope to decide on how we manage the content definition creation process.</p>When Visual Studio Code Metrics fail/blogs/johan-antila/dates/2024/3/when-visual-studio-code-metrics-fail/2024-03-06T20:11:50.0000000Z<p>Visual Studio Code Metrics- that won't show up.</p>
<p>At Optimizely Expert Services, we are often times asked to do code reviews and assessments of customer solutions and these typically involve using static code analysers and reading code bases. One good tool for finding complex parts of a solution is the Code Metrics tool in Visual Studio that gives you an indication of hot spots in the code base. For example, it can show you the complexity of the code, generate something it calls Maintainability Index, It can list lines of code and display what of this is executable code, the latter is especially handy for projects and namespaces where you have your views. Unfortunately, there's been a few times where I have happened upon a bug in the Visual Studio Roslyn engine that stops the Code Metrics from rendering in the Code Metrics Window. Instead of listing the namespaces and calculated metrics, the window is just blank. <em>In vain, I have struggled</em> to find the reason behind this and to fix it for the specific solution but I have come up empty handed. When it happened again this time and I went searching for a solution, one of the pages I came across mentioned that there was an automated process calculating the metrics and this led me to investigate if there was a command line tool to do this and perhaps, this would work when the built one wouldn't. It turns out that indeed, Microsoft has created such a tool called <a href="https://github.com/dotnet/roslyn-analyzers">Microsoft.CodeAnalysis.NetAnalyzers</a> You can either add a package reference to your project to be able to target it using msbuild or compile a stand alone command line tool called to do this calculation in a command shell. As the nuget package solution didn't work for me, I opted for the latter solution with a command line tool and followed <a href="https://learn.microsoft.com/en-us/visualstudio/code-quality/how-to-generate-code-metrics-data?view=vs-2019">this guide.</a></p>
<p>With the tool compiled, start up a Visual Studio Command Prompt, and run it to generate an xml file: </p>
<pre>metrics.exe /p:project myproject.csproj /out:report.xml</pre>
<p>This generates an XML file with the code metrics in it but that's not the Excel output that the built in tool generates. Now what? Well you have to parse the xml file some way so I opted to parse it to json and iterate over it using a serialized version of the file.</p>
<p><a href="https://github.com/johan-antila-episerver/ParseMetricsXml">I wrote a small tool to do just that</a>, it takes the xml file and outputs a csv file that you can import in Excel. You can find it over at<strong> </strong><a href="https://github.com/johan-antila-episerver/ParseMetricsXml"><strong>GitHub.</strong></a></p>
<p>One final note, to be able to parse the json in a compile time fashion, you need to catch the json content after it has been parsed from XML, copy it and use Visual Studio -> Edit -> Paste Special -> <strong>Paste JSON </strong>as classes to get classes that you now can use in the code.</p>Optimizely Web... 6 Game Changing Features in 2024https://www.jondjones.com/learn-optimizely/experimentation/optimizely-web-6-game-changing-features-in-2024/2024-03-03T00:00:00.0000000ZIf you are interested in learning about what's new within Optimizely Web, you are in the right place. Carry on reading to learn about the 6 greatest and latest features that have been added within Optimizely Web within 2024. This list is surprisingl...Headless forms reloaded (beta)/blogs/martin-ottosen/dates/2024/3/headless-forms-reloaded-beta/2024-03-01T14:48:34.0000000Z<p>Forms is used on the vast majority of CMS installations. But using Forms in a headless setup is a bit of pain since the rendering pipeline is based on ASP.NET MVC. We do have an extension to our Content Delivery API that includes the generated forms HTML in the json model for a page, but if you want the frontend to control the look & feel of the rendered form, you’re fighting an uphill battle. Better then if Forms could just expose the raw forms configuration with a handy front-end SDK with a good default rendering.</p>
<p><span style="background-color: #bfedd2;">Note: This feature is in</span><span style="background-color: #bfedd2;"> Beta, you are welcome to use it, but we do have more changes coming before the official release. Beta packages will be removed once official packages are released.</span></p>
<h1>What's in it?</h1>
<ol>
<li>Forms 5 <a href="https://nuget.optimizely.com/package/?id=EPiServer.Forms">[package]</a> <a href="https://docs.developers.optimizely.com/content-management-system/v1.2.0-forms/docs/forms">[docs]</a></li>
<li>New REST API <a href="https://dev.azure.com/EpiserverEngineering/netCore/_artifacts/feed/headless_forms_beta/NuGet/Optimizely.Headless.Form.Service/overview/">[beta package]</a> <a href="https://docs.developers.optimizely.com/content-management-system/v1.2.0-forms/docs/set-up-headless-optimizely-forms-api">[beta docs]</a></li>
<li>(optional) Graph integration <a href="https://dev.azure.com/EpiserverEngineering/netCore/_artifacts/feed/headless_forms_beta/NuGet/Optimizely.Headless.Form.ContentGraph/overview/">[beta package]</a> <a href="https://docs.developers.optimizely.com/content-management-system/v1.2.0-forms/docs/integrate-optimizely-graph-with-headless-optimizely-forms">[beta docs]</a></li>
<li>(optional) generic JS SDK <a href="https://dev.azure.com/EpiserverEngineering/netCore/_artifacts/feed/headless_forms_beta/Npm/@episerver%2Fforms-sdk/overview/0.1.0">[beta package]</a> <a href="https://docs.developers.optimizely.com/content-management-system/v1.2.0-forms/docs/javascript-sdk-for-headless-optimizely-forms">[beta docs]</a> <a href="https://github.com/episerver/content-headless-form-js-sdk/tree/develop/src/%40episerver/forms-sdk">[repo]</a></li>
<li>(optional) react specific SDK <a href="https://dev.azure.com/EpiserverEngineering/netCore/_artifacts/feed/headless_forms_beta/Npm/@episerver%2Fforms-react/overview/0.1.0">[beta package]</a> <a href="https://docs.developers.optimizely.com/content-management-system/v1.2.0-forms/docs/install-headless-optimizely-forms-javascript-sdk-for-the-client-app">[beta docs]</a> <a href="https://github.com/episerver/content-headless-form-js-sdk/tree/develop/src/%40episerver/forms-react">[repo]</a></li>
</ol>
<p>As a quick example I created the worlds least interesting form using a boilerplate Alloy MVC site, which yes is not headless, but that’s the point really. This is just good-old-forms under the hood.</p>
<p><img src="/link/a8f0d1e500e441709ed0fa81b4ba46ba.aspx" width="1000" alt="" height="699" /></p>
<p>With a reference to <a href="https://pkgs.dev.azure.com/EpiserverEngineering/netCore/_packaging/headless_forms_beta/nuget/v3/index.json">the headless forms beta feed</a> we can add the new REST API and optionally swashbuckle:</p>
<pre class="language-javascript"><code><PackageReference Include="EPiServer.Forms" Version="5.8.0" />
<PackageReference Include="Optimizely.Headless.Form.Service" Version="0.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /></code></pre>
<p>Getting the new endpoint setup is pretty easy, we need to add a service to startup.cs for the new API and optionally a service + some configuration for swagger:</p>
<pre class="language-csharp"><code>// Enable OpenAPI documentation
services.AddSwaggerGen();
//Configure the API headless forms API
services.AddOptimizelyHeadlessFormService(options =>
{
// Enable swagger docs for headless forms API @ /_form/v1/docs/openapi.json
options.EnableOpenApiDocumentation = true;
options.FormCorsPolicy = new FormCorsPolicy
{
AllowOrigins = new string[] { "_" }, //Enter '_' to allow any origins, multiple origins separate by comma
AllowCredentials = true
};
});</code></pre>
<p> </p>
<pre class="language-csharp"><code>// Configure swagger UI to use the headless forms API spec
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/_form/v1/docs/openapi.json", "Optimizely Headless Form API V1");
});</code></pre>
<p>With that we get nice local documentation of the API available at <span style="text-decoration: underline;">/swagger/</span>. In production you would probably want to configure OIDC for access to endpoints and docs.</p>
<p><img src="/link/f5167624812e4b59b980474296a0e526.aspx" width="1000" alt="" height="442" /></p>
<p>To get the full form configuration you pass in the GUID of the form container in <span>UUID N format</span><span> i.e. without any “-“ separators </span></p>
<pre class="language-javascript"><code>GET /_form/v1/form/2df32b30ade54eb3970e4ab5170fb5ff
{
"key": "2df32b30ade54eb3970e4ab5170fb5ff",
"properties": {
"title": "test",
"allowToStoreSubmissionData": true,
"showSummarizedData": false,
"confirmationMessage": "Thank you, come again!",
"allowAnonymousSubmission": false,
"allowMultipleSubmission": true,
"showNavigationBar": true,
"focusOnForm": true
},
"formElements": [
{
"key": "2697b674ec554f7a817ea1f977fc2204",
"contentType": "TextboxElementBlock",
"displayName": "New form element",
"properties": {
"validators": [
{
"type": "RequiredValidator",
"description": null,
"model": {
"message": "This field is required.",
"validationCssClass": "ValidationRequired",
"additionalAttributes": {
"required": "",
"aria-required": "true"
}
}
}
],
"label": "Name",
"placeHolder": "firstname lastname",
"conditions": []
},
"locale": "en",
"localizations": {}
},
{
"key": "3aa836ec620146cf8291263c51b4b44f",
"contentType": "SubmitButtonElementBlock",
"displayName": "New form element",
"properties": {
"validators": [],
"label": "Go!",
"conditions": []
},
"locale": "en",
"localizations": { "label": "Submit" }
}
],
"locale": "en",
"localizations": {
"allowAnonymousSubmissionErrorMessage": "You must be logged in to submit this form. If you are logged in and still cannot post, make sure \"Do not track\" in your browser settings is disabled.",
"allowMultipleSubmissionErrorMessage": "You already submitted this form.",
"malformstepconfigruationErrorMessage": "Improperly formed FormStep configuration. Some steps are attached to pages, while some steps are not attached, or attached to content with no public URL."
}
}</code></pre>
<p>Installing the Forms to Graph integration of course requires Optimizely Graph, and will simply add a new property “FormRenderTemplate” to the Graph model for a FormContainerBlock exposing the exact same json object available from the REST API.</p>
<pre class="language-javascript"><code>query MyQuery {
FormContainerBlock {
items {
FormRenderTemplate
}
}
}</code></pre>
<p>If we want to upgrade to a completely decoupled frontend we can use the react sample from <a href="https://github.com/episerver/content-headless-form-js-sdk">the new forms SDK repository</a>, but I think that warrants it’s own blog post.</p>
<p>Besides any notes you may have here or at <a href="https://feedback.optimizely.com/?category=6852085523081599129">feedback.optimizely.com</a> for this beta release, we have plans to update the current authorization system for form submitters and refine the SDK to give you a better looking starting point.</p>Uploading blobs to Optimizely DXP via PowerShellhttps://blogs.perficient.com/?p=3580102024-03-01T05:51:48.0000000ZWe had a client moving from an On-Prem v11 Optimizely instance to DXP v12 and we had a lot of blobs (over 40 GB) needing uploading to DXP as a part of the conversion.   This was my first experience doing both a version and environment upgrade and I leaned heavily on Optimizely support to […]