<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Scott Reed</title><link href="http://world.optimizely.com" /><updated>2025-04-25T09:29:19.0000000Z</updated><id>https://world.optimizely.com/blogs/scott-reed/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Identifying Spike Requests and Issues in Application Insights</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2025/4/identifying-spike-requests-and-issues-in-application-insights/" /><id>&lt;p&gt;Sometimes within the DXP we see specific Azure App Instances having request spikes causing performance degredation and we need to investigate. I find the performance tab often lacking to narrow down to the specifics of what I want to look at, so this helps me get to the bottom of things easily&lt;/p&gt;
&lt;p&gt;Here&#39;s some of my easy steps to figure these out&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Identify Instance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Open Application Insights and navigate to the Monitoring -&amp;gt; Metrics area and set the following filters&lt;/p&gt;
&lt;p&gt;Metric: Log-based metrics, Server response time, Avg aggregation&lt;/p&gt;
&lt;p&gt;Split by: Cloud role instance&lt;/p&gt;
&lt;p&gt;Set the time range (&lt;strong&gt;IN UTC)&amp;nbsp;&lt;/strong&gt;to when the issue occured and keep narrowing it in until you have a 30 minute or so window&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f125e7aeb05a41d3a95fd6a1c69f3bf0.aspx&quot; width=&quot;717&quot; height=&quot;589&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Make a note of the affending instance that&#39;s causing the issue, in our case &lt;strong&gt;2a9f3a38c4ac&amp;nbsp;&lt;/strong&gt;and the 10 minute time range (IN UTC) when the issue occured&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Extract High Performance Bucket Requests&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Navigate to the Monitoring -&amp;gt; Logs area and set the following KQL, replacing the INSTANCE with your noted instance. Also in the run area set the time frame in UTC to the 10 minute range noted above&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;union isfuzzy=true requests
| where cloud_RoleInstance in (&quot;INSTANCE&quot;)
| order by duration desc
| take 100
| project timestamp, id, name, url, duration, performanceBucket&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will give you a list you can export to CSV of high requests&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/9914b70254f84cbca81bc0b1b5613f68.aspx&quot; width=&quot;736&quot; height=&quot;229&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Viewing Request Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now navigate to Investigate -&amp;gt; Transaction Search&lt;/p&gt;
&lt;p&gt;Set the filter Event Types = Request and in the search copy and of the id values from our list and search. You can then open the request and drill in to dependency issues or profiler traces, in our case to see a slow Find query&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/608148f445114da3b4e99ea09a70bc8d.aspx&quot; width=&quot;739&quot; height=&quot;274&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let me know if you have other easier or fun ways to drill in to these types of issues.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2025-04-25T09:29:19.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Frontend hosting for PaaS &amp; SaaS CMS</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2025/4/frontend-hosting-for-paas--saas-cms/" /><id>&lt;p&gt;Just announced on the DXP, we FINALLY have a front end hosting solution coming as part of the DXP for modern decoupled solutions in both the PaaS and SaaS space&lt;/p&gt;
&lt;p&gt;Supporting&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enterprise search engine&lt;/li&gt;
&lt;li&gt;CDN&lt;/li&gt;
&lt;li&gt;WAP&lt;/li&gt;
&lt;li&gt;Managed service support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/link/1485d3c95df74f3395cfc14de067e79f.aspx&quot; alt=&quot;&quot; width=&quot;604&quot; height=&quot;322&quot; /&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://www.optimizely.com/beta/&quot;&gt;https://www.optimizely.com/beta/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2025-04-22T07:56:29.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Save The Date - London 2025 Summer Meetup</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2025/3/save-the-date---london-2025-summer-meetup/" /><id>&lt;p&gt;Following last years very succesful meetup in London July 2024 &lt;a href=&quot;/link/ca9925b7807b44f5a248fed3dd4d2888.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2024/7/optimizely-london-dev-meetup-11th-july-2024/&lt;/a&gt; we are returning to the Lightwell venue for the 2025 event.&lt;/p&gt;
&lt;p&gt;The agenda is TBD but save the date and time&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10th July 2025 : 6pm until 10pm&lt;/li&gt;
&lt;li&gt;Food and Drink&lt;/li&gt;
&lt;li&gt;Talks to be deterimined (reach out to me on LinkedIn or slack if you&#39;d like to talk)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope you all can make it&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f7e2e5a6a5964183855bc37477153266.aspx&quot; /&gt;&lt;/p&gt;</id><updated>2025-03-25T12:28:45.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>CMS 12 DXP Migrations - Time Zones</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2025/3/cms-12-dxp-migrations---time-zones/" /><id>&lt;p&gt;When it comes to migrating a project from CMS 11 and .NET Framework on the DXP to CMS 12 and .NET Core one thing you need to be aware of is the infrastructure.&lt;/p&gt;
&lt;p&gt;On CMS 11 .NET Framework we are running on direct Windows WebApps in Azure&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/054d1b949fa54dcb8ed35f4a6b6e66b6.aspx&quot; width=&quot;749&quot; height=&quot;242&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Whereas on CMS 12 .NET Core we are running on Docker contained Lixux WebApps&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/b7c9abc8f8f846ab9f9dc8915be2cde8.aspx&quot; width=&quot;754&quot; height=&quot;421&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One of the issues we&#39;ve faced on this during migrations that something to be aware of is the website timezone value which might be set on directly in the settings for the WebApps on the DXP when running under CMS 11. For example for sites we&#39;ve had running in USA&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1a7f957a33f3466183586344a42b09f6.aspx&quot; width=&quot;508&quot; height=&quot;134&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This can cause an issue as when going through the project migration process in the DXP to migrate a project over the new infrastructure running in Docker is set to run under UTC and thefore if you have an C# calls to ToLocalTime() in your code you make get different values on this in the new infrastructure.&lt;/p&gt;
&lt;p&gt;Luckily however there&#39;s an easy way to solve this, you can add a setting in to the &quot;App Settings&quot; area of your new project to tell the Docker container what timezone to run under,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/3476aacf4a9c4575aa139461431fdb2c.aspx&quot; width=&quot;752&quot; height=&quot;259&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Example values supported are as follows (Should support any that Docker supports &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&quot;&gt;https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time Zone Identifier&lt;/strong&gt;&lt;br /&gt;GMT&lt;br /&gt;Pacific/Apia&lt;br /&gt;HST&lt;br /&gt;AST&lt;br /&gt;America/Los_Angeles&lt;br /&gt;America/Phoenix&lt;br /&gt;America/Mazatlan&lt;br /&gt;America/Denver&lt;br /&gt;America/Belize&lt;br /&gt;America/Chicago&lt;br /&gt;America/Mexico_City&lt;br /&gt;America/Regina&lt;br /&gt;America/Bogota&lt;br /&gt;America/New_York&lt;br /&gt;America/Indianapolis&lt;br /&gt;America/Halifax&lt;br /&gt;America/Caracas&lt;br /&gt;America/Santiago&lt;br /&gt;America/St_Johns&lt;br /&gt;America/Sao_Paulo&lt;br /&gt;America/Buenos_Aires&lt;br /&gt;America/Godthab&lt;br /&gt;Atlantic/South_Georgia&lt;br /&gt;Atlantic/Azores&lt;br /&gt;Atlantic/Cape_Verde&lt;br /&gt;Africa/Casablanca&lt;br /&gt;Europe/Dublin&lt;br /&gt;Europe/Berlin&lt;br /&gt;Europe/Belgrade&lt;br /&gt;Europe/Paris&lt;br /&gt;Europe/Warsaw&lt;br /&gt;ECT&lt;br /&gt;Europe/Athens&lt;br /&gt;Europe/Minsk&lt;br /&gt;Europe/Bucharest&lt;br /&gt;Africa/Cairo&lt;br /&gt;Africa/Harare&lt;br /&gt;Europe/Helsinki&lt;br /&gt;Asia/Jerusalem&lt;br /&gt;Asia/Baghdad&lt;br /&gt;Asia/Kuwait&lt;br /&gt;Europe/Moscow&lt;br /&gt;Africa/Nairobi&lt;br /&gt;Asia/Tehran&lt;br /&gt;Asia/Muscat&lt;br /&gt;Asia/Baku&lt;br /&gt;Asia/Kabul&lt;br /&gt;Asia/Yekaterinburg&lt;br /&gt;Asia/Karachi&lt;br /&gt;Asia/Calcutta&lt;br /&gt;Asia/Katmandu&lt;br /&gt;Asia/Almaty&lt;br /&gt;Asia/Dhaka&lt;br /&gt;Asia/Colombo&lt;br /&gt;Asia/Rangoon&lt;br /&gt;Asia/Bangkok&lt;br /&gt;Asia/Krasnoyarsk&lt;br /&gt;Asia/Hong_Kong&lt;br /&gt;Asia/Irkutsk&lt;br /&gt;Asia/Kuala_Lumpur&lt;br /&gt;Australia/Perth&lt;br /&gt;Asia/Taipei&lt;br /&gt;Asia/Tokyo&lt;br /&gt;Asia/Seoul&lt;br /&gt;Asia/Yakutsk&lt;br /&gt;Australia/Adelaide&lt;br /&gt;Australia/Darwin&lt;br /&gt;Australia/Brisbane&lt;br /&gt;Australia/Sydney&lt;br /&gt;Pacific/Guam&lt;br /&gt;Australia/Hobart&lt;br /&gt;Asia/Vladivostok&lt;br /&gt;Pacific/Noumea&lt;br /&gt;Pacific/Auckland&lt;br /&gt;Pacific/Fiji&lt;br /&gt;Pacific/Tongatapu&lt;/p&gt;</id><updated>2025-03-04T09:47:58.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely London Dev Meetup 11th July 2024</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2024/7/optimizely-london-dev-meetup-11th-july-2024/" /><id>&lt;p&gt;On 11th July 2024 in London Niteco and Netcel along with Optimizely ran the London Developer meetup.&lt;/p&gt;
&lt;p&gt;There was an great agenda of talks that we put on and everyone had a great amount of fun.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/bb508683340140abab08e14616e39b76.aspx&quot; width=&quot;821&quot; height=&quot;418&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m not going to go through it all as Graham has already created a cracking summary here &lt;a href=&quot;/link/cbed8a11f3a142b08fa091adacd34698.aspx&quot;&gt;https://world.optimizely.com/blogs/allthingsopti/dates/2024/7/a-day-in-the-life-of-an-optimizely-developer---london-meetup-2024/&lt;/a&gt; but here is all the talk material for anyone who wants to see it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tips and Tricks for CMS12/Commerce 14 - &lt;a href=&quot;/link/e923f00f08f24dfb8961f607971b820c.aspx&quot;&gt;https://world.optimizely.com/globalassets/community/meetups/london/dev-meet-2024---upgrades.pptx&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Get started with Visual Builder - Live demo only!&lt;/li&gt;
&lt;li&gt;Guide to SaaS CMS and Hybrid Headless - &lt;a href=&quot;https://www.youtube.com/watch?v=-cFsPUdIVFY&quot;&gt;https://www.youtube.com/watch?v=-cFsPUdIVFY&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Creating AddOns for PaaS CMS - &lt;a href=&quot;/link/f6859e4b7dbe4f50baff410bce6b25e4.aspx&quot;&gt;https://world.optimizely.com/globalassets/community/meetups/london/creating-addons-for-optimizely.pptx&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Azure AI Speech Integration with Optimizely CMS - &lt;a href=&quot;/link/b4c59c4e505343b88a044fd2ec435ae5.aspx&quot;&gt;https://world.optimizely.com/globalassets/community/meetups/london/azure-ai-speech-integration-with-optimizely-cms.pptx&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Opal in PaaS Commerce + New Stuff - &lt;a href=&quot;/link/2abbdf21e92149d0af82f88cfad91ba8.aspx&quot;&gt;https://world.optimizely.com/globalassets/community/meetups/london/optimizely_commerce-connect_roadmap_3q24.pdf&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Implementing Optimizely flags through Vercel - &lt;a href=&quot;/link/460519ced4de4af7b1f7a27087423c4d.aspx&quot;&gt;https://world.optimizely.com/globalassets/community/meetups/london/dom_opti_vercel_toolbar_flags.pdf&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was a pleasure to host this one and was one of the best we&#39;ve had I think in a long while, hope to see you next year or at the North event :-)&lt;/p&gt;</id><updated>2024-07-19T15:36:57.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>New ODP Connector For Customized Commerce</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2023/6/new-odp-connector-for-customized-commerce/" /><id>&lt;p&gt;When you have both Customized Commerce and the ODP one core aim is to connect the data to the platform so that it can be used within the ODP for functions such as campaigns, reports and real time segmentation.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Although the ODP has a powerful API that allows you to interact with it, the standard approach in Optimizely has been to connect it using the ODP App for Commerce Cloud&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e0c267ab45ab4a2cb4d8dc1dac01c2c0.aspx&quot; width=&quot;696&quot; height=&quot;309&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This connector pulls Products, Customers and Orders in to the ODP using the Service API package which needs installing in to your commerce solution. It then pulls the data at a frequency from Commerce in to the ODP platform at a specifically set regular interval. However this comes with a number of issues&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Problems with high volume of data in larger solutions&lt;/li&gt;
&lt;li&gt;Problems with combatibility with different versions of Service API&lt;/li&gt;
&lt;li&gt;Reliance on Optimizely managing it as an application for features and scheduler&lt;/li&gt;
&lt;li&gt;Import interval slow for use with real time segments&lt;/li&gt;
&lt;li&gt;Lack of ease to control syncing of custom attributes for customers/products&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alternate options have been to use custom code or third parter connectors to directly interact with the ODP via the API stack, however in Commere 14.13.1 there now comes an offical connector &lt;a href=&quot;https://docs.developers.optimizely.com/customized-commerce/docs/integrate-commerce-and-odp&quot;&gt;https://docs.developers.optimizely.com/customized-commerce/docs/integrate-commerce-and-odp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This connector has the following features&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Runs as a scheduled job which you can control the timings of&lt;/li&gt;
&lt;li&gt;Can push products, customers and orders&lt;/li&gt;
&lt;li&gt;Support for Products and Customer custom attributes by the us of an &lt;span&gt;ICustomerCustomFieldsHandler /&lt;/span&gt; IProductCustomFieldsHandler (see the link for code example)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the first version and will be enhanced I&#39;m sure but now give us a better option for offically connecting Customized Commerce and the ODP!&lt;/p&gt;</id><updated>2023-06-29T14:59:45.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>DXP Beta Features &amp; Cloudflare Edge Image Resizing</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2023/6/dxp-beta-features--cloudflare-edge-image-resizing/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;We all love access to the latest and greatest features on the Optimizely DXP platform, as by using the cutting edge we can create faster Optimizely experiences that provide a better customer experience to end users! Optimizely has a lot of great features as part of the DXP and is continuing to innovate by providing new features and opening up areas of the DXP more and more for our use.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Recently it&#39;s very exciting to the see a beta signup page some to Optimizely &lt;a href=&quot;https://www.optimizely.com/beta&quot;&gt;https://www.optimizely.com/beta&lt;/a&gt; that allows us to see beta features in progress and sign up to for use on our own projects, such as just to name a few key ones&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Edge Image Resizing&lt;/li&gt;
&lt;li&gt;ODP real time segmentation&lt;/li&gt;
&lt;li&gt;GA 4 Web Experimentation Reporting&lt;/li&gt;
&lt;li&gt;Vendor Manged Invetory for Configured Commerce (B2B)&lt;/li&gt;
&lt;li&gt;Opti ID Single Sign On across all the Optimizely provided products&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However I&#39;d like to call out one specifically I&#39;m very excitied about on this list&lt;/p&gt;
&lt;h2&gt;Cloudflare Edge Image Resizing&lt;/h2&gt;
&lt;p&gt;In the modern world we are serving websites across many different devices such as mobile, tablet and desktop and it&#39;s been common for some time for developers to be using responsive design and CSS to provide optimized delivery of the content. As part of that as we also want to serve the images at the right size with the smallest physical size and request footprint to users loading on every different device profile.&lt;/p&gt;
&lt;p&gt;For some time now a common solution would be to install a package such as ImageResizer, ImageSharp or build your own (which has been even more needed with changes in CMS 12 with .NET 6)&lt;/p&gt;
&lt;p&gt;However, this gives us a few downsides&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building and maintenance of the library or updating it due to bugs&lt;/li&gt;
&lt;li&gt;Potential issues that may exist in the library&lt;/li&gt;
&lt;li&gt;CPU overhead of the webapps/server running the image resizing code on Azure&lt;/li&gt;
&lt;li&gt;Increase in blob storage assets if caching resized version which can cause environment restoring to be a hassle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cloudflare Edge Image Resizing &lt;a href=&quot;https://developers.cloudflare.com/images/image-resizing/&quot;&gt;https://developers.cloudflare.com/images/image-resizing/&lt;/a&gt; gives us a solution to this as it&#39;s a feature long provided in Cloudflare which can resizie images to WebP and AVIF formats on cloudflares edge platform based upon the parameters defined in the URL.&lt;/p&gt;
&lt;p&gt;This allows us to offload the resizing processing and the caching off the images through to cloudflare eliminating the pain points in using a .NET library. Image configurations &lt;a href=&quot;https://developers.cloudflare.com/images/image-resizing/url-format/&quot;&gt;https://developers.cloudflare.com/images/image-resizing/url-format/&lt;/a&gt; can be defined by passing the OPTIONs part of the URL allowing the height, width and quality to be set.&lt;/p&gt;
&lt;p&gt;On the DXP right now this can be enabled by signing up on the beta page and once given access this will be enabled for the DXP. This means that for any environments such as preproduction and production with custom hostnames (as direct dxcloud domains bypass the CDN) the images will be resized.&lt;/p&gt;
&lt;p&gt;This should be a simple development activity to replace by steps such as&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Removing your resizer package&lt;/li&gt;
&lt;li&gt;Creating an application service that can rewrite image URLs in the desired format&lt;/li&gt;
&lt;li&gt;Deploying to prep for testing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can also test once enabled by directly navigating to the URL in the browser!&lt;/p&gt;
&lt;h2&gt;Picture Renderer&lt;/h2&gt;
&lt;p&gt;A quick update (01-07-2022) that the Picture Rendered by Erik Henningson has been updated to support cloudflare image resizing &lt;a href=&quot;https://github.com/ErikHen/PictureRenderer.Optimizely&quot;&gt;https://github.com/ErikHen/PictureRenderer.Optimizely&lt;/a&gt; so this gives anyone a head start with using this feature for picture element based image resizing&lt;/p&gt;
&lt;p&gt;Hopefully this has been useful! Thanks all!&lt;/p&gt;</id><updated>2023-06-16T09:52:36.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Fixing Optimizely Content Syncing/Caching Issues on the DXP pre CMS.Core 12.13.0</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2023/3/fixing-optimizely-content-syncingcaching-issues-on-the-dxp-pre-cms-core-12-13-0/" /><id>&lt;p&gt;Hi all,&lt;/p&gt;
&lt;p&gt;With our recent deployments to the DXP for .NET 6 projects (one a new build and one an upgrade) our clients had raised issues where there were seeing different data for the same items within the CMS. For one of our clients on Optimizely Commerce they were seeing 1 variant on a product and a different editor was seeing 3 variants.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/6236cd5af9c4438f8be73d891e97b9c0.aspx&quot; width=&quot;972&quot; height=&quot;135&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vs&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/304f81154ef24eb89bc103eb2f9b8680.aspx&quot; width=&quot;971&quot; height=&quot;231&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This&amp;nbsp;was a big issue and raised the problem that not only would this be affecting users, end customer would be seeing the different data.&lt;/p&gt;
&lt;p&gt;We managed to replicate this by faking the users ARRAffinity value so we could see that this was related to specific instances.&lt;/p&gt;
&lt;p&gt;On an investgation by support this has raised that there was a scaling bug on the caching system fixed on bug &lt;a href=&quot;/link/1933ba72787346df9003b7a4c7d1cff8.aspx?epsremainingpath=bug/CMS-26486&quot;&gt;https://world.optimizely.com/support/bug-list/bug/CMS-26486&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;As this is a fairly critical issue I wanted to highlight this and strongly suggest customers on the DXP to upgrade to a minimum version of &lt;span style=&quot;text-decoration:&amp;#32;line-through;&quot;&gt;12.13.0&lt;/span&gt; (12.13.1 See below)&amp;nbsp;as we are doing for our client.&lt;/p&gt;
&lt;p&gt;Thanks!!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update to this: Quan Mai pointed out some other fixes recently which means we should be upgrading to 13.13.1 &lt;a href=&quot;/link/e4389195afa3428b8cf16df6d578ccfc.aspx&quot;&gt;https://world.optimizely.com/blogs/Quan-Mai/Dates/2023/3/upgrade-to-episerver-cms-core-12-13-1-as-soon-as-you-can/&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;</id><updated>2023-03-23T13:53:47.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Restoring Help Text For Description in Latest Optimizely CMS</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2023/3/restoring-help-text-for-display-name/" /><id>&lt;p&gt;It&#39;s been a well known CSS hack for a while to force the showing of the description text for a property in to the editor view rather than a hover as per these articles&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://buildoptimizely.com/episerver/optimizely-cms-12-help-text-and-expanded-input-text-boxes/&quot;&gt;https://buildoptimizely.com/episerver/optimizely-cms-12-help-text-and-expanded-input-text-boxes/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://talk.alfnilsson.se/2014/12/18/display-help-text-in-on-page-editing/&quot;&gt;https://talk.alfnilsson.se/2014/12/18/display-help-text-in-on-page-editing/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;However in a recent version of CMS this broke due to updated UI around the all properties view.&lt;/p&gt;
&lt;p&gt;With the following replacement CSS this will restore a similar features&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;div.dijitTabPaneWrapper div:has(div.oui-pop--over) {
    display: block !important;
}


div.dijitTabPaneWrapper div.click-area {
    display: none !important;
}

div.dijitTabPaneWrapper div.oui-pop--over {
    border: none !important;
    max-width: 600px;
    font-style: italic;
    color: red;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this applied you&#39;ll get an interface that looks like this&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5f3c78b4eb9c4d829d0cc76969784734.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is working in Chrome and Edge and will restore the inline text and remove the popup icon, only for the main properties area.&lt;/p&gt;
&lt;p&gt;I&#39;ve highlighted it red but feel free to modify.&lt;/p&gt;
&lt;p&gt;As per the previous guides this needs to be placed in a CSS file such as this editmode.css&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/8f5e3e159aca4316bdb85d3ddecdd3ff.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And then registered in a module.config in the application root (not wwwroot) such as&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;module&amp;gt;
	&amp;lt;clientResources&amp;gt;
		&amp;lt;add name=&quot;epi-cms.widgets.base&quot; path=&quot;Styles/editmode.css&quot; resourceType=&quot;Style&quot;/&amp;gt;
	&amp;lt;/clientResources&amp;gt;
&amp;lt;/module&amp;gt;&lt;/code&gt;&lt;/pre&gt;</id><updated>2023-03-14T18:04:48.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely DXP Health Checks and Creating Custom Checks</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2023/3/optimizely-dxp-health-checks-and-creating-custom-checks/" /><id>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In this blog I&#39;m going to be talking through the standard Azure health checking system and how the DXP is setup to operate health checks. Then I&#39;m going to explain how to easily create your own health checks to allow monitoring parts of the system so that unhealthy instances can be easily removed.&lt;/p&gt;
&lt;p&gt;Thanks as well to Valdis and Mark Hall who helped me get this point!&lt;/p&gt;
&lt;h2&gt;Azure Heath Checks&lt;/h2&gt;
&lt;p&gt;Within Azure there is a feature called a heath check &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/app-service/monitor-instances-health-check?tabs=dotnet&quot;&gt;https://learn.microsoft.com/en-us/azure/app-service/monitor-instances-health-check?tabs=dotnet&lt;/a&gt; which is designed to allow you to provide information about the state of your application and if it&#39;s healthy or unhealthy. This allows the load balancer to deterimine if there are any unhealthy instances and remove them so that the overall health of the application can recover quickly as shown.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://learn.microsoft.com/en-us/azure/app-service/media/app-service-monitor-instances-health-check/azure-portal-navigation-health-check.png&quot; width=&quot;507&quot; alt=&quot;Health&amp;#32;check&amp;#32;navigation&amp;#32;in&amp;#32;Azure&amp;#32;Portal&quot; height=&quot;329&quot; /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Optimizely DXP Health Checks&lt;/h2&gt;
&lt;p&gt;Optimizely has a specific setup of heathcheck that&#39;s set up in the middleware and configured as standard in the Azure WebApps hosted by the DXP.&lt;/p&gt;
&lt;p&gt;The following is setup as standard within the DXP WebApps&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/899f1d321da542518c237c869e319521.aspx&quot; width=&quot;460&quot; height=&quot;237&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see there is a specific route that has been configured for Optimizely health checks. So how does this all work?&lt;/p&gt;
&lt;p&gt;Within a typical DXP startup.cs you&#39;ll have a call to&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;.AddCmsCloudPlatformSupport(_configuration)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we decomiple this code we see a specific bit of code that sets up this health check configuration&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; public static IServiceCollection AddCloudPlatformHealthCheck(this IServiceCollection services)
 {
   services.TryAddEnumerable(ServiceDescriptor.Singleton&amp;lt;IEndpointRoutingExtension, CloudPlatformHealthCheckEndpointRoutingExtension&amp;gt;());
   services.AddHealthChecks().AddCmsHealthCheck(tags: ((IEnumerable&amp;lt;string&amp;gt;) new string[2]
   {
     &quot;episerver&quot;,
     &quot;content-api&quot;
   })).AddCmsWarmupHealthCheck(tags: ((IEnumerable&amp;lt;string&amp;gt;) new string[1]
   {
     &quot;warmup&quot;
   }));
   return services; 
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code does a few things&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sets up the routing for the /episerver/health&lt;/li&gt;
&lt;li&gt;Adds the CmsHeathCheck which loads the class CmsHealthCheck (This checks some information about the schema / database version)&lt;/li&gt;
&lt;li&gt;Adds the AddCmsWarmupHealthCheck which does checks for a warm up using pings defined in the environments WEBSITE_HEALTHCHECK_MAXPINGFAILURES&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So this does a few things to allow Optimizely to report if the Instance is healthy or unhealthy but what about if we want to add to this?&lt;/p&gt;
&lt;h2&gt;Adding Custom Checks&lt;/h2&gt;
&lt;p&gt;This is actually really easy based upon what we&#39;ve seen here&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a class that inherits IHealthCheck&lt;/li&gt;
&lt;li&gt;Create an extension method to register is part of the IHealthChecksBuilder&lt;/li&gt;
&lt;li&gt;Add it to the startup.cs using the AddHealthChecks() extension&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Create a class that inherits IHealthCheck&lt;/h3&gt;
&lt;p&gt;My class will specifically force an unhealthy state so we can test it in postman but in your own code obviously do a check for whatever you want and return Healthy if it&#39;s all okay!&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class CustomHealthCheck : IHealthCheck
{
    public Task&amp;lt;HealthCheckResult&amp;gt; CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
    {
        return Task.FromResult&amp;lt;HealthCheckResult&amp;gt;(HealthCheckResult.Unhealthy(&quot;Test failed&quot;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Create an extension method to register is part of the IHealthChecksBuilder&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static class CustomHeathCheckExtensions
{
    public static IHealthChecksBuilder AddCustomHealthCheck(
        this IHealthChecksBuilder builder,
        string name = &quot;custom-health-check&quot;,
        HealthStatus status = HealthStatus.Unhealthy,
        IEnumerable&amp;lt;string&amp;gt; tags = null)
    {
        return builder.AddCheck&amp;lt;CustomHealthCheck&amp;gt;(name, new HealthStatus?(status), tags);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Add it to the startup.cs using the AddHealthChecks() extension&lt;/h3&gt;
&lt;p&gt;Add it on the end of the services expressions as shown here for Alloy&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;services
   .AddCmsCloudPlatformSupport(_configuration)
   .AddCmsAspNetIdentity&amp;lt;ApplicationUser&amp;gt;()
   .AddCms()
   .AddAlloy()
   .AddAdminUserRegistration()
   .AddEmbeddedLocalization&amp;lt;Startup&amp;gt;()
   .AddHealthChecks()
   .AddCustomHealthCheck();&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;A quick note on this is that adding .AddCmsCloudPlatformSupport(_configuration) locally will cause an error so a way to test this locally is to comment this out and manually register the .AddCloudPlatformHealthCheck() as shown below&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;services
   //.AddCmsCloudPlatformSupport(_configuration)
   .AddCmsAspNetIdentity&amp;lt;ApplicationUser&amp;gt;()
   .AddCms()
   .AddAlloy()
   .AddAdminUserRegistration()
   .AddEmbeddedLocalization&amp;lt;Startup&amp;gt;()
   .AddCloudPlatformHealthCheck()
   .AddHealthChecks()
   .AddCustomHealthCheck();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then with this is place you&#39;ll get an unhealthy response back, as shown in postman when hitting that endpoint&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/cee128a0273c40ec9f573c07298b06ec.aspx&quot; width=&quot;538&quot; height=&quot;295&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Hopefully this helps explain exactly how the heath checking system is working in a bit more detail and gives you some ideas should you want to be able to have greater control is telling the load balancer when your instance is unhealthy :-)&lt;/p&gt;</id><updated>2023-03-09T16:42:33.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Hiding Visitor Group Criteria In CMS 12</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2022/6/hiding-visitor-group-criteria-in-cms-12/" /><id>&lt;p&gt;In CMS 12 the Visitor Group managment system has been moved to a client side component the same as the standard editing experience and the .NET 5/6 framework has moved away from StructureMap.&lt;/p&gt;
&lt;p&gt;Therefore the old way we had to filter out Visitior Group Criteria we don&#39;t want such as described here &lt;a href=&quot;/link/d74f767710cc4bd6bcd7732a28227bb0.aspx&quot;&gt;https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2019/6/hide-episerver-visitor-group-criteria/&lt;/a&gt; does not work as described.&lt;/p&gt;
&lt;p&gt;Therefore here is a solution you can implement to do the same job, configurable from appSettings.json&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage 1: Implement A Custom Filtered Version of the IVisitorGroupsUIApiService&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This class simply filters the API to return everything apart from types we want removed (from Stage 2)&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using EPiServer.Cms.UI.VisitorGroups.Api.Builders;
using EPiServer.Cms.UI.VisitorGroups.Api.Services;
using EPiServer.Cms.UI.VisitorGroups.Api.ViewModels;
using EPiServer.Framework.Localization;
using EPiServer.Personalization.VisitorGroups;
using Microsoft.Extensions.Configuration;

namespace AlloyTemplates.Business.VisitorGroups
{
    public class FilteredVisitorGroupsUIApiService : IVisitorGroupsUIApiService
    {
        private readonly IVisitorGroupRepository _visitorGroupRepository;
        private readonly IVisitorGroupCriterionRepository _visitorGroupCriterionRepository;
        private readonly VisitorGroupDtoBuilder _visitorGroupDtoBuilder;
        private readonly LocalizationService _localizationService;
        private readonly IVisitorGroupsStatisticsLoggerRegistry _visitorGroupsStatisticsLoggerRegistry;
        private readonly IConfiguration _configuration;

        public FilteredVisitorGroupsUIApiService(
          IVisitorGroupRepository visitorGroupRepository,
          IVisitorGroupCriterionRepository visitorGroupCriterionRepository,
          VisitorGroupDtoBuilder visitorGroupDtoBuilder,
          LocalizationService localizationService,
          IVisitorGroupsStatisticsLoggerRegistry visitorGroupsStatisticsLoggerRegistry,
            IConfiguration configuration
          )
        {
            this._visitorGroupRepository = visitorGroupRepository;
            this._visitorGroupCriterionRepository = visitorGroupCriterionRepository;
            this._visitorGroupDtoBuilder = visitorGroupDtoBuilder;
            this._localizationService = localizationService;
            this._visitorGroupsStatisticsLoggerRegistry = visitorGroupsStatisticsLoggerRegistry;
            _configuration = configuration;
        }

        public IEnumerable&amp;lt;VisitorGroupListViewItemDto&amp;gt; ListAllGroups() =&amp;gt; (IEnumerable&amp;lt;VisitorGroupListViewItemDto&amp;gt;)this._visitorGroupRepository.List().Select&amp;lt;VisitorGroup, VisitorGroupListViewItemDto&amp;gt;((Func&amp;lt;VisitorGroup, VisitorGroupListViewItemDto&amp;gt;)(visitorGroup =&amp;gt; new VisitorGroupListViewItemDto(visitorGroup))).ToList&amp;lt;VisitorGroupListViewItemDto&amp;gt;();

        public VisitorGroupDto GetGroup(Guid id) =&amp;gt; this._visitorGroupDtoBuilder.Build(this._visitorGroupRepository.Load(id));

        public VisitorGroup SaveGroup(VisitorGroup visitorGroup)
        {
            if (visitorGroup.Id == Guid.Empty)
                visitorGroup.Id = Guid.NewGuid();
            this._visitorGroupRepository.Save(visitorGroup);
            return visitorGroup;
        }

        public VisitorGroupDto CopyGroup(Guid id) =&amp;gt; this._visitorGroupDtoBuilder.Build(this._visitorGroupRepository.Copy(this._visitorGroupRepository.Load(id), this._localizationService.GetString(&quot;/shell/cms/visitorgroups/index/copy&quot;)));

        public void DeleteGroup(Guid id) =&amp;gt; this._visitorGroupRepository.Delete(id);

        public IEnumerable&amp;lt;VisitorGroupCriterionDto&amp;gt; ListAllVisitorGroupCriterion()
        {
            var filteredTypes = _configuration.GetSection(&quot;FilteredVisitorGroups:ExcludedCriterionFullTypes&quot;).Get&amp;lt;string[]&amp;gt;();

            var filteredList = filteredTypes == null || !filteredTypes.Any() ? _visitorGroupCriterionRepository.List() : _visitorGroupCriterionRepository.List()
                .Where(item =&amp;gt; !filteredTypes.Contains(item.TypeName));

            return (from criterion in filteredList select _visitorGroupDtoBuilder.Build(criterion)).ToList();
        }

        public void DeleteStatisticsForGroup(Guid id) =&amp;gt; this._visitorGroupsStatisticsLoggerRegistry.RemoveStatistics((IEnumerable&amp;lt;Guid&amp;gt;)new Guid[1]
        {
      id
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure also to register this in your DI code&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;services.AddTransient&amp;lt;IVisitorGroupsUIApiService, FilteredVisitorGroupsUIApiService&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Stage 2: Add settings to appSettings.json&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this section of the root of the of the JSON file we can configure the full type name of the Criterion type we want removed.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;  &quot;FilteredVisitorGroups&quot;: {
    &quot;ExcludedCriterionFullTypes&quot;: [
      &quot;EPiServer.Personalization.VisitorGroups.Criteria.UserProfileCriterion, EPiServer.Cms.UI.AspNetIdentity&quot;
    ] 
  } &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example shows removing the UserProfileCriterion but will work with any type&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a8e90c7df48d42319b373e3abae029d0.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Thanks all :-)&lt;/p&gt;</id><updated>2022-06-20T11:55:04.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizey London Developer Group Casual Christmas Meetup</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/12/optimizey-casual-christmas-meetup/" /><id>&lt;p&gt;&lt;span&gt;As it&#39;s been such a very long time since the London Developer Group got together, we thought it would be nice to have a Christmas (or holiday/December if you don&#39;t celebrate) meetup for all us Optimizely fans, developers, users.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;This is open to anyone really but it is primarily focused towards the Optimizely platform and a chance to meetup /chat with people we&#39;ve not seen for ages.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;There&#39;s no formal presentations as it&#39;s more casual but they&#39;ll be Scott Reed (MVP / Solution Architect @ Niteco) &amp;amp; Mark Everard (Technology Director @ Made to Engage) there to talk to. We&#39;re hoping to catch up and discuss everything going on in the Optimizely world.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;The Meetup will be @ Fountain and Ink, London at 6PM&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Meetup link here &lt;a href=&quot;https://www.meetup.com/EPiServer-London/events/282424657/&quot;&gt;https://www.meetup.com/EPiServer-London/events/282424657/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2021-12-01T15:23:01.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Restore Root CMS episerver Login Path In .NET 5</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/11/restore-root-episerver-login-path-in--net-5/" /><id>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;With the release of CMS 12 there was the removal of dashboards which has caused the base login path to not work when created in a new project via the steps documented here &lt;a href=&quot;/link/893f1ecd15be4522a18bc054c8a59dbd.aspx&quot;&gt;https://world.optimizely.com/documentation/developer-guides/CMS/getting-started/creating-a-starter-project/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now with the out of the box code you have to navigate to &lt;a href=&quot;/episerver/cms&quot;&gt;/episerver/cms&lt;/a&gt; to access the CMS Editing area.&lt;/p&gt;
&lt;p&gt;Although you can change the path to the CMS editing experience with the documented approach &lt;a href=&quot;/link/1760bc24efd441e6a923db50c494b340.aspx&quot;&gt;https://world.optimizely.com/documentation/developer-guides/CMS/configuration/changing-edit-and-admin-view-urls/&lt;/a&gt; it does not work to change this to just /episerver as there needs to be a subpath for the CMS experience.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Using the ASP.NET Rewriting Middleware &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0&quot;&gt;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0&lt;/a&gt; we can add a redirect to reirect &lt;a href=&quot;/episerver&quot;&gt;/episerver&lt;/a&gt; to&lt;a href=&quot;/episerver/cms&quot;&gt; /episerver/cms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;First add the nuget package&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.Rewrite --version 2.2.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add in the redirect to the Startup.cs configure code&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var options = new RewriteOptions().AddRedirect(&quot;episerver$&quot;, &quot;episerver/cms&quot;);
app.UseRewriter(options);&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is something simple but I&#39;ve seen people confused &lt;a href=&quot;/link/e516a2200bc44c8287f124ff425c8cb1.aspx&quot;&gt;https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2021/11/cms12-emptystarter-project-not-working/&lt;/a&gt; therefore I wanted to post about it and propose an easy solution to remedy the problem. Ideally when you go to the root /episerver as everyone is used to doing you want to go to the CMS which this will do.&lt;/p&gt;
&lt;p&gt;Also as a note now that Episerver is no longer the company name it strikes me odd to preserve it so I would follow the approach in &lt;a href=&quot;/link/1760bc24efd441e6a923db50c494b340.aspx&quot;&gt;https://world.optimizely.com/documentation/developer-guides/CMS/configuration/changing-edit-and-admin-view-urls/&lt;/a&gt; anyhow and consider changing the default URL path to remove the old brand. Such as &lt;a href=&quot;/cms/cms&quot;&gt;/cms/cms&lt;/a&gt; with a redirect from &lt;a href=&quot;/cms&quot;&gt;/cms&lt;/a&gt; &amp;amp; &amp;nbsp;&lt;a href=&quot;/episerver&quot;&gt;/episerver&lt;/a&gt; to &lt;a href=&quot;/cms/cms&quot;&gt;/cms/cms&lt;/a&gt; or any name of your choice :-)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Happy coding!! Add any thoughts, errors or suggestions in the comments below!!&lt;/p&gt;</id><updated>2021-11-17T12:57:34.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Run Once Migration Step For Use With Commerce 14</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/11/run-once-migration-step/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;As part of the release of the new .NET 5 version of the platform, Commerce 14 finally got rid of the Commerce Manager needed for the management of some commerce settings. Instead, now we are presented with a number of features that have been added to the standard Commerce Interface when logging in to your main web application. However, as part of this process a number of areas that were previously availible as UI editable settings in Commerce Manager have been removed.&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Importing and exporting catalogs&lt;/li&gt;
&lt;li&gt;Adding countries and regions&lt;/li&gt;
&lt;li&gt;Adding currencies&lt;/li&gt;
&lt;li&gt;Working with business objects&lt;/li&gt;
&lt;li&gt;Working with catalog and order meta classes fields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The recommendation now is to use the API to add these yourselves!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Challenge&lt;/h2&gt;
&lt;p&gt;So when doing a number of demos as part of my CMS 12 and Commerce 14 masterclass I was looking at the best place to add these. As first I was using an IMigrationStep as this seemed a nice approach.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;By implementing IMigrationStep with a name and description you&#39;re presented with the migration in each environment it has not run (as below) and it only ever runs once.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/839213cdcb13434191660f21d20b92be.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However it was raised to me that IMigartionStep is in the Internal namespace and therefore it&#39;s not advisable to use (Even thought I got this approach from the example code in the Alloy CMS12/Commerce 14 preview :p)&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;As well as the IMigrationStep there exists a Migration Step class. The naming is a little confusing because you might think this class would Inherit IMigrationStep, however that&#39;s not the case and the two work differently. You can read about the MigrationStep here &lt;a href=&quot;https://www.jondjones.com/learn-episerver-cms/episerver-developers-tutorials/importing-content-into-episerver-programmatically/episerver-migration-steps-explained/&quot;&gt;https://www.jondjones.com/learn-episerver-cms/episerver-developers-tutorials/importing-content-into-episerver-programmatically/episerver-migration-steps-explained/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;So the migration step isn&#39;t Internal, it&#39;s availible to use but it does have the limitation of running EVERY time the solution initializes. So I decided to replicate what the IMigrationStep does under the covers and use the DDS to persist a record of it running, allowing you to be sure migrations only happen once per environment.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The code below is from the Alloy Preview so the namespaces can be refactored as you see fit&lt;/p&gt;
&lt;h3&gt;Dynamic Data Store Model&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Data;
using EPiServer.Data.Dynamic;

namespace EPiServer.Reference.Commerce.Site.Infrastructure.MigrationSteps
{
    [EPiServerDataStore(AutomaticallyRemapStore = true)]
    public class MigrationStepChange : IDynamicData
    {
        public Identity Id { get; set; }

        public string Name { get; set; }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Run Once Migration Step&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System.Linq;
using EPiServer.Data.Dynamic;
using EPiServer.DataAbstraction.Migration;

namespace EPiServer.Reference.Commerce.Site.Infrastructure.MigrationSteps
{
    public abstract class RunOnceMigrationStep : MigrationStep
    {
        public string Name { get; }

        protected RunOnceMigrationStep()
        {
            Name = GetType().Name;
        }

        protected abstract void RunOnce();

        public override void AddChanges()
        {
            var store = DynamicDataStoreFactory.Instance.CreateStore(typeof(MigrationStepChange));

            var record = store.Items&amp;lt;MigrationStepChange&amp;gt;().FirstOrDefault(r =&amp;gt; r.Name == Name);

            if (record == null)
            {
                RunOnce();
                store.Save(new MigrationStepChange { Name = Name });
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example Code&lt;/h3&gt;
&lt;p&gt;The following code runs once and adds a new currency in to Commerce with the ID TST and the Name of Test Dollar&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.DataAbstraction.Migration;

namespace EPiServer.Reference.Commerce.Site.Infrastructure.MigrationSteps
{
    public class CurrencyMigrationStep : RunOnceMigrationStep
    {
        public static readonly CurrencySetup.CurrencyConversion[] ConversionRatesSco = {
            new CurrencySetup.CurrencyConversion(&quot;TST&quot;, &quot;Test dollar&quot;, 1m) };

        protected override void RunOnce()
        {
            var c = new CurrencySetup();
            c.CreateConversions(ConversionRatesSco);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I modified the CurrencySetup class in the Preview to make it reusable. Here is the code that works for the above example&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using Mediachase.Commerce.Catalog.Managers;
using Mediachase.Commerce.Catalog.Dto;
using Mediachase.Commerce.Core;

namespace EPiServer.Reference.Commerce.Site.Infrastructure
{
    public class CurrencySetup
    {
        public class CurrencyConversion
        {
            public CurrencyConversion(string currency, string name, decimal factor)
            {
                Currency = currency;
                Name = name;
                Factor = factor;
            }

            public readonly string Currency;
            public readonly string Name;
            public readonly decimal Factor;
        }

        public static readonly CurrencyConversion[] ConversionRatesToUsd = {
            new CurrencyConversion(&quot;USD&quot;, &quot;US dollar&quot;, 1m),
            new CurrencyConversion(&quot;SEK&quot;, &quot;Swedish krona&quot;, 0.12m),
            new CurrencyConversion(&quot;AUD&quot;, &quot;Australian dollar&quot;, 0.78m),
            new CurrencyConversion(&quot;CAD&quot;, &quot;Canadian dollar&quot;, 0.81m),
            new CurrencyConversion(&quot;EUR&quot;, &quot;Euro&quot;, 1.07m),
            new CurrencyConversion(&quot;BRL&quot;, &quot;Brazilian Real&quot;, 0.33m),
            new CurrencyConversion(&quot;CLP&quot;, &quot;Chilean Peso&quot;, 0.001637m),
            new CurrencyConversion(&quot;JPY&quot;, &quot;Japanese yen&quot;, 0.008397m),
            new CurrencyConversion(&quot;NOK&quot;, &quot;Norwegian krone&quot;, 0.128333m),
            new CurrencyConversion(&quot;SAR&quot;, &quot;Saudi Arabian Riyal&quot;, 0.734m),
            new CurrencyConversion(&quot;GBP&quot;, &quot;Pound sterling&quot;, 1.49m) };

        public void CreateConversions(CurrencyConversion[] CurrenciesToAdd)
        {
            EnsureCurrencies(CurrenciesToAdd);

            var dto = CurrencyManager.GetCurrencyDto();
            var workingDto = (CurrencyDto) dto.Copy();
            foreach (var conversion in CurrenciesToAdd)
            {
                var toCurrencies = CurrenciesToAdd.Where(c =&amp;gt; c != conversion).ToList();
                AddRates(workingDto, conversion, toCurrencies);
            }
            CurrencyManager.SaveCurrency(workingDto);
        }

        private void EnsureCurrencies(CurrencyConversion[] CurrenciesToAdd)
        {
            bool isDirty = false;
            var dto = CurrencyManager.GetCurrencyDto();
            var workingDto = (CurrencyDto) dto.Copy();

            foreach (var conversion in CurrenciesToAdd)
            {
                if (GetCurrency(workingDto, conversion.Currency) == null)
                {
                    workingDto.Currency.AddCurrencyRow(conversion.Currency, conversion.Name, DateTime.Now);
                    isDirty = true;
                }
            }

            if (isDirty)
            {
                CurrencyManager.SaveCurrency(workingDto);
            }
        }

        private void AddRates(CurrencyDto dto, CurrencyConversion from, IEnumerable&amp;lt;CurrencyConversion&amp;gt; toCurrencies)
        {
            var rates = dto.CurrencyRate;
            foreach (var to in toCurrencies)
            {
                var rate = (double)(from.Factor / to.Factor);
                var fromRow = GetCurrency(dto, from.Currency);
                var toRow = GetCurrency(dto, to.Currency);
                rates.AddCurrencyRateRow(rate, rate, DateTime.Now, fromRow, toRow, DateTime.Now);
            }
        }

        private CurrencyDto.CurrencyRow GetCurrency(CurrencyDto dto, string currencyCode)
        {
            return (CurrencyDto.CurrencyRow)dto.Currency.Select(&quot;CurrencyCode = &#39;&quot; + currencyCode + &quot;&#39;&quot;).SingleOrDefault();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is some simple code wrapping the MigrationStep in a DDS record check but may help some people wondering about a simple approach to working with commerce settings that no longer exist as a UI configuration and must be inserted with code.&lt;/p&gt;
&lt;p&gt;Thanks all, feedback and suggestions in the comments!&lt;/p&gt;</id><updated>2021-11-12T12:30:11.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>.NET 5 Preview - DXP Changes</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/8/-net-5-preview---dxp-changes/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;The .NET 5 preview is a major update to the Optimizely CMS platform. The public preview was released on July 1st as announced by Martin Ottosen here &lt;a href=&quot;/link/1bc8e0286e4348c8b0d527ca35b5cfe9.aspx&quot;&gt;https://world.episerver.com/blogs/martin-ottosen/dates/2021/6/-net-5-public-preview/&lt;/a&gt; and with it comes a number of features as mentioned in my previous blog post.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve not read it you can read the full list here&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Admin UI Changes Breakdown - &lt;a href=&quot;/link/07aa8d8d1ffd43f6942d24df03ac3f41.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--admin-area/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Alloy Project Breakdown - &lt;a href=&quot;/link/8f5c0141ef6641b28d5db1654d69cc17.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--alloy-project/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;DXP Changes - &lt;a href=&quot;/link/9797f87436514ae8b9f500ba4da16d93.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/8/-net-5-preview---dxp-changes/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this series of blog posts, I&amp;rsquo;m going to give an overview of what to expect in this version based upon my own exploration and usage. I will compare what we had to what we have to give developers an easier understanding of where the platform has changed.&lt;/p&gt;
&lt;p&gt;In this third post I am going to give a high-level overview of what is looking to change on the DXP with the .NET 5 version.&lt;/p&gt;
&lt;h2&gt;Linux Web Apps&lt;/h2&gt;
&lt;p&gt;Due to the fact that we&#39;ve been running on full fat .NET for many years we&#39;ve been limited to being only able to run our solutions on a Windows based infrastructure. With the move to .NET 5 the team believes Linux Web Apps will be better in the long term for running apps. I assume as well somewhere down the line support for containers/orchestration will be on the cards with all these changes to be for a more modern DevOps system.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Config Transforms&lt;/h2&gt;
&lt;p&gt;As updated in my Alloy blog post as we do not have any Web.Config we do not have transforms anymore. We do see however in the Alloy demo some .JSON files with configuration names in them however for these files from now on they do not trasnform they just load the correct environment directly from the file. E.g if you have appsettings.Integration that will load the integration settings. This means 2 considerations&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make sure to have any .JSON config files have the exact settings you want per environment&lt;/li&gt;
&lt;li&gt;Make sure that your configuration middleware loads the correct settings you want based on the environment you&#39;re using as you can no longer load settings from web.config.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Deployments&lt;/h2&gt;
&lt;p&gt;I&#39;ve heard a couple of things around deployments to be aware of with this move that&#39;s very important&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With this version of .NET the Deployment API will only be supported. Therefore you&#39;ll have to make sure your DevOps processes are migrated to using it as soon as you can. This may be an opportunity to pre prepare for the version update ahead of time to reduce time/effort in the migration. To note once you have a package on Integration you can use the DXP Pass Portal to deploy it up to prep/prod.&lt;/li&gt;
&lt;li&gt;To start there will be no maintenance page option availible for Linux DXP sites in the deployment process.&lt;/li&gt;
&lt;/ol&gt;</id><updated>2021-08-10T10:36:46.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>.NET 5 Preview Breakdown – Alloy Project</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--alloy-project/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;The .NET 5 preview is a major update to the Optimizely CMS platform. The public preview was released on July 1st as announced by Martin Ottosen here &lt;a href=&quot;/link/1bc8e0286e4348c8b0d527ca35b5cfe9.aspx&quot;&gt;https://world.episerver.com/blogs/martin-ottosen/dates/2021/6/-net-5-public-preview/&lt;/a&gt; and with it comes a number of features as mentioned in my previous blog post.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve not read it you can read the full list here&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Admin UI Changes Breakdown - &lt;a href=&quot;/link/07aa8d8d1ffd43f6942d24df03ac3f41.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--admin-area/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Alloy Project Breakdown - &lt;a href=&quot;/link/8f5c0141ef6641b28d5db1654d69cc17.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--alloy-project/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;DXP Changes - &lt;a href=&quot;/link/9797f87436514ae8b9f500ba4da16d93.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/8/-net-5-preview---dxp-changes/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this series of blog posts, I&amp;rsquo;m going to give an overview of what to expect in this version based upon my own exploration and usage. I will compare what we had to what we have to give developers an easier understanding of where the platform has changed.&lt;/p&gt;
&lt;p&gt;In this second post I am going to give a high-level overview and a beak down of what&amp;rsquo;s difference in the .NET 5 preview&amp;rsquo;s project structure and utilization. This is purely from my own personal breakdown of Alloy .NET 4.7.2 vs Alloy .NET 5 and the areas that I see have changed.&lt;/p&gt;
&lt;h2&gt;.NET Framework (4.*) vs .NET 5 Differences&lt;/h2&gt;
&lt;p&gt;First off, there may be a people viewing this who have no idea about the differences between the full fat .NET framework ASP.NET projects vs how .NET Core/.NET 5 works. So combined with the Optimizely CMS changes will be a bit of .NET 5 information as well to help you along.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Whereas in olden times the setting up of a new project was done purely in Visual Studio and we were all familiar with the CMS platform&amp;rsquo;s Visual Studio Addon or even cloning a version of Quicksilver/foundation this is now different. In the modern world of .NET 5 this is all handled a lot more like package managers are handled such as NPM, Nuget and done via the command line.&lt;/p&gt;
&lt;p&gt;This require a few things to be in place for creating new website based on templates&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Template&lt;/strong&gt; &amp;ndash; This is essentially the same as what you get installing the CMS extension. It installs a template for the project that you can then use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLI &lt;/strong&gt;&amp;ndash; This is installation of the Episerver CLI commands such as you may have used in Powershell when working with Azure/Episerver and using the Deployment API.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once these are installed you can use the CLI to create a new project of the preview. &lt;strong&gt;Be aware that with the Optimizely rebrand I would imagine at some point these will be moving to a different set of template names and CLI commands.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;dotnet new epicmsempty --name ProjectName&lt;/p&gt;
&lt;p&gt;cd projectname&lt;/p&gt;
&lt;p&gt;dotnet-episerver create-cms-database ProjectName.csproj -S . -E&lt;/p&gt;
&lt;h3&gt;Project Structure&lt;/h3&gt;
&lt;h4&gt;Packages&lt;/h4&gt;
&lt;p&gt;The package management in .NET 5 is a lot cleaner in Visual Studio and shows the packages that are installed as well as all the dependant packages that have been installed as sub nodes. This makes it a lot easier to handle the packages on a project and see the difference between packages you have installed and dependant packages&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/751501489711444ba9ccfafc58252807.aspx&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Website Root&lt;/h4&gt;
&lt;p&gt;In .NET 5 there is a special folder in the solution which is the place for you to keep all of the assets for the website. Typically, in the past on a lot of ASP.NET projects developers end up creating their own folder such as &amp;ldquo;Static&amp;rdquo; or such. In this version all of those static assets are nicely grouped in the wwwroot and will be the output files.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ccf5adcc55f646fdac7ddc888c12fbb0.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Dependency Injection&lt;/h3&gt;
&lt;p&gt;Who doesn&amp;rsquo;t like to be SOLID nowadays and using a dependency injection framework to allow us to model and abstract out all of our services, factories and load them in via Constructors. For years we have been using the popular StructureMap as the framework of choice and although it used to be coupled to platform a few years ago it was pulled out to a Nuget package to allow swapping and .NET Standard support.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re on .NET 5 we have the Microsoft built in DI framework which is the standard for projects &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection&quot;&gt;https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection&lt;/a&gt; and this is now the default used&lt;/p&gt;
&lt;h3&gt;Configuration and Initalization&lt;/h3&gt;
&lt;p&gt;The trusty Web.Config is something ASP.NET developer have been used to using for a long time. An XML file that ends up being hundred if not thousands of lines of configuration and setup.&lt;/p&gt;
&lt;p&gt;In .NET 5 this is gone mostly in favour a combination C# based code configuration and some JSON configuration files.&lt;/p&gt;
&lt;p&gt;Another standard in the Optimizely CMS platform is the InitializableModule which was used as a place to configure startup code. Although the attribute still exists in the Framework library the correct place to add startup logic is in the Startup.cs file. &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-5.0&quot;&gt;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-5.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As the framework still has many Initializable Modules these don&amp;rsquo;t look to be going anywhere so for the time being these will still work as expected.&lt;/p&gt;
&lt;h4&gt;Startup&lt;/h4&gt;
&lt;p&gt;Most of the magic happens now in the &lt;strong&gt;Startup.cs &lt;/strong&gt;file moved from both the Web.Config and the InitalizationModules with the following actions happening&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setting up if the scheduler should run (This is the system that run Scheduled Jobs)&lt;/li&gt;
&lt;li&gt;Setting up the correct connection strings (Set up in code however the connectionString is in the appsettings.json file)&lt;/li&gt;
&lt;li&gt;Adding in the Identity provider&lt;/li&gt;
&lt;li&gt;Configuring MVC&lt;/li&gt;
&lt;li&gt;Adding in Alloy configuration (&lt;strong&gt;AddAlloy&lt;/strong&gt;();) such as
&lt;ol&gt;
&lt;li&gt;The view engine used&lt;/li&gt;
&lt;li&gt;The display options (Used to allow blocks to show in different sizes as set by the editor)&lt;/li&gt;
&lt;li&gt;The resolutions (Used for previewing in different resolutions / devices)&lt;/li&gt;
&lt;li&gt;Browser detection (Uses Wangkani)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Adds CMS services (&lt;strong&gt;AddCms();&lt;/strong&gt; such as
&lt;ol&gt;
&lt;li&gt;Host&lt;/li&gt;
&lt;li&gt;UI&lt;/li&gt;
&lt;li&gt;HTML Helpers&lt;/li&gt;
&lt;li&gt;Config&lt;/li&gt;
&lt;li&gt;TinyMCE config)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Adding localization support for the editor&lt;/li&gt;
&lt;li&gt;Configuration of exception handling&lt;/li&gt;
&lt;li&gt;Setup of admin page for first time admin registration&lt;/li&gt;
&lt;li&gt;NET features (static files, routing, authentication, authorization)&lt;/li&gt;
&lt;li&gt;Routing configuration, Mapping of CMS content and use of Razor pages as the View Engine&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There&amp;rsquo;s a lot there that&amp;rsquo;s been ported from Web.Config and a collection of other Initialization modules.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the Alloy example for you to feast your eyes on, see more by setting up the preview for yourself&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Cms.UI.AspNetIdentity;
using EPiServer.Data;
using EPiServer.Framework.Web.Resources;
using EPiServer.Personalization.Commerce.Tracking;
using EPiServer.Reference.Commerce.Site.Features.Market.Services;
using EPiServer.Reference.Commerce.Site.Features.Recommendations.Services;
using EPiServer.Reference.Commerce.Site.Features.Shared.Services;
using EPiServer.Reference.Commerce.Site.Infrastructure;
using EPiServer.Reference.Commerce.Site.Infrastructure.Attributes;
using EPiServer.Reference.Commerce.Site.Infrastructure.Indexing;
using EPiServer.ServiceLocation;
using EPiServer.Tracking.Commerce;
using EPiServer.Web;
using EPiServer.Web.Routing;
using Mediachase.Commerce;
using Mediachase.Commerce.Anonymous;
using Mediachase.Commerce.Core;
using Mediachase.Commerce.Orders;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;

namespace EPiServer.Reference.Commerce.Site
{
    public class Startup
    {
        private readonly IWebHostEnvironment _webHostingEnvironment;
        private readonly IConfiguration _configuration;

        public Startup(IWebHostEnvironment webHostingEnvironment, IConfiguration configuration)
        {
            _webHostingEnvironment = webHostingEnvironment;
            _configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            AppDomain.CurrentDomain.SetData(&quot;DataDirectory&quot;, Path.Combine(_webHostingEnvironment.ContentRootPath, &quot;App_Data&quot;));
            services.Configure&amp;lt;DataAccessOptions&amp;gt;(o =&amp;gt;
            {
                o.ConnectionStrings.Add(new ConnectionStringOptions
                {
                    ConnectionString = _configuration.GetConnectionString(&quot;EcfSqlConnection&quot;),
                    Name = &quot;EcfSqlConnection&quot;
                });
            });

            services.AddCmsAspNetIdentity&amp;lt;ApplicationUser&amp;gt;(o =&amp;gt;
            {
                if (string.IsNullOrEmpty(o.ConnectionStringOptions?.ConnectionString))
                {
                    o.ConnectionStringOptions = new ConnectionStringOptions
                    {
                        Name = &quot;EcfSqlConnection&quot;,
                        ConnectionString = _configuration.GetConnectionString(&quot;EcfSqlConnection&quot;)
                    };
                }
            });

            //UI
            if (_webHostingEnvironment.IsDevelopment())
            {
                
                services.Configure&amp;lt;ClientResourceOptions&amp;gt;(uiOptions =&amp;gt;
                {
                    uiOptions.Debug = true;
                });
            }
            
            services.Configure&amp;lt;JsonOptions&amp;gt;(o =&amp;gt;
            {
                o.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

            //Commerce
            services.AddCommerce();
            //site specific
            services.Configure&amp;lt;IISServerOptions&amp;gt;(options =&amp;gt; options.AllowSynchronousIO = true);
            services.Configure&amp;lt;KestrelServerOptions&amp;gt;(options =&amp;gt; options.AllowSynchronousIO = true);
            services.TryAddEnumerable(Microsoft.Extensions.DependencyInjection.ServiceDescriptor.Singleton(typeof(IFirstRequestInitializer), typeof(UsersInstaller)));
            services.AddDisplayResolutions();
            services.TryAddSingleton&amp;lt;IRecommendationContext, RecommendationContext&amp;gt;();
            services.AddSingleton&amp;lt;ICurrentMarket, CurrentMarket&amp;gt;();
            services.TryAddSingleton&amp;lt;ITrackingResponseDataInterceptor, TrackingResponseDataInterceptor&amp;gt;();
            services.AddHttpContextOrThreadScoped&amp;lt;SiteContext, CustomCurrencySiteContext&amp;gt;();
            services.AddTransient&amp;lt;CatalogIndexer&amp;gt;();
            services.TryAddSingleton&amp;lt;ServiceAccessor&amp;lt;IContentRouteHelper&amp;gt;&amp;gt;(locator =&amp;gt; locator.GetInstance&amp;lt;IContentRouteHelper&amp;gt;);
            services.AddEmbeddedLocalization&amp;lt;Startup&amp;gt;();
            services.Configure&amp;lt;MvcOptions&amp;gt;(o =&amp;gt;
            {
                o.Filters.Add(new ControllerExceptionFilterAttribute());
                o.Filters.Add(new ReadOnlyFilter());
                o.Filters.Add(new AJAXLocalizationFilterAttribute());
                o.ModelBinderProviders.Insert(0, new ModelBinderProvider());
            });

            services.ConfigureApplicationCookie(options =&amp;gt;
            {
                options.LoginPath = &quot;/util/Login&quot;;
            });

            services.Configure&amp;lt;OrderOptions&amp;gt;(o =&amp;gt;
            {
                o.DisableOrderDataLocalization = true;
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAnonymousId();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =&amp;gt;
            {
                endpoints.MapControllerRoute(name: &quot;Default&quot;, pattern: &quot;{controller}/{action}/{id?}&quot;);
                endpoints.MapControllers();
                endpoints.MapContent();
            });
        }
    }

    public static class Extensions
    {
        public static void AddDisplayResolutions(this IServiceCollection services)
        {
            services.AddSingleton&amp;lt;StandardResolution&amp;gt;();
            services.AddSingleton&amp;lt;IpadHorizontalResolution&amp;gt;();
            services.AddSingleton&amp;lt;IphoneVerticalResolution&amp;gt;();
            services.AddSingleton&amp;lt;AndroidVerticalResolution&amp;gt;();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Appsettings.json&lt;/h4&gt;
&lt;p&gt;This is a file which contains a little of what the Web.Config used to contain and allows environment versions to allow for transforms &lt;span style=&quot;text-decoration:&amp;#32;line-through;&quot;&gt;which I assume be supported in the DXP to allow transformation of environment configurations.&amp;nbsp;&amp;nbsp;&lt;/span&gt;After speaking to Optimizely I&#39;m told this will load whatever version matched the environment name .e.g &lt;span&gt;appsettings.Production.json will be loaded if the environment matches production.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The file in Alloy contains&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Logging configuration&lt;/li&gt;
&lt;li&gt;Connection strings&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Bundleconfig.json&lt;/h4&gt;
&lt;p&gt;Whereas we used to do the bundle configuration in code as this is mostly a frontend focused task this has now been moved to JSON as well with the bundleconfig.json.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To note, I did find a stack overflow post saying that this method was removed as standard in .net core 2.1 as it uses a third party tool. With the standard being to add client files in VS to the project which uses LibMan instead. &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I&amp;rsquo;m still unsure on this one but for now I&amp;rsquo;m going with the Alloy Preview setup as the standard.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;.NET 6&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;.NET 6 is expected in november with an longer support lifespan as you can see from the image below from &lt;a href=&quot;https://blog.inedo.com/dotnet/demystifying-lts&quot;&gt;https://blog.inedo.com/dotnet/demystifying-lts&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;From what I&#39;ve heard the plan is to have a .NET 6 support version by the end of the year / early 2022 so that we are on the LTS .NET&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5ef593a84a9c4938975f948c4b21412d.aspx&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Although this is a huge upgrade most of the fundamentals are under the covers and have been carried out by Optimizely in the packages and aspnet CLI. I reality at least in the base CMS project there&amp;rsquo;s not huge amounts different for a developer to think about other than moving the initialization where possible to Startup, a bit of bundling changes and some light structural changes.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d suggest the best possible way to learn is to dive in an have a play &#128522;&lt;/p&gt;
&lt;p&gt;If anything is unclear or wrong in this post then comment and I&amp;rsquo;ll update!! Thanks all!&lt;/p&gt;</id><updated>2021-07-21T13:14:15.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>.NET 5 Preview Breakdown – Admin Area</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--admin-area/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;The .NET 5 preview is a major update to the Optimizely CMS platform. The public preview was released on July 1st as announced by Martin Ottosen here &lt;a href=&quot;/link/1bc8e0286e4348c8b0d527ca35b5cfe9.aspx&quot;&gt;https://world.episerver.com/blogs/martin-ottosen/dates/2021/6/-net-5-public-preview/&lt;/a&gt; and with it comes a number of features.&lt;/p&gt;
&lt;p&gt;In this series of blog posts, I&amp;rsquo;m going to give an overview of what to expect in this version based upon my own exploration and usage. I will compare what we had to what we have to give developers an easier understanding of where the platform has changed. In this first post I am going to give a high-level overview and a break down of what to expect in the CMS Admin as it&amp;rsquo;s a complete rewrite.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve not read it you can read the full list here&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Admin UI Changes Breakdown - &lt;a href=&quot;/link/07aa8d8d1ffd43f6942d24df03ac3f41.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--admin-area/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Alloy Project Breakdown - &lt;a href=&quot;/link/8f5c0141ef6641b28d5db1654d69cc17.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--alloy-project/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;DXP Changes - &lt;a href=&quot;/link/9797f87436514ae8b9f500ba4da16d93.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/8/-net-5-preview---dxp-changes/&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following is an overview of the features as part of the .NET 5 preview&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better performance&lt;/li&gt;
&lt;li&gt;A more modern stack&lt;/li&gt;
&lt;li&gt;A rewritten admin area in MVC/Front end stack&lt;/li&gt;
&lt;li&gt;Cross platform, can run on Windows, Linux and MacOS&lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;li&gt;Migration to Microsoft dependency injection&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Admin Features&lt;/h2&gt;
&lt;p&gt;Previously before the .NET upgrade the admin system was clunky and a traditional asp.net application build on the WebForms technology. It had not been upgraded for some time.&lt;/p&gt;
&lt;p&gt;Now the admin has been reworked from the ground up with the same care and attention that the editing experience get. Fully driven from Dojo&lt;/p&gt;
&lt;h3&gt;Managing Content Types&lt;/h3&gt;
&lt;p&gt;Previously before the preview we had the following for managing content types&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1c3fe006aabe409a8da6f3d292a27315.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This has now been transformed into a client-side driven manager for all content types&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e54d166d30fd40b7adf527d268b81aeb.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/2c103728997d442cb6660f897f3a5097.aspx&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All content types are grouped, to filter to a specific type use the filter icon under the headings to either filter by type or access level&lt;/li&gt;
&lt;li&gt;Build in search feature&lt;/li&gt;
&lt;li&gt;Everything is ordered by display name as default which has always been a pain point in the old admin.&lt;/li&gt;
&lt;li&gt;Properties grouped by tab just as within the CMS&lt;/li&gt;
&lt;li&gt;Properties grid has a search&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Setting Access Rights&lt;/h3&gt;
&lt;p&gt;Setting access rights on the whole has mainly been kept the same but a lot faster as it&amp;rsquo;s in the new client side driven UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/9478cda00c3a4c478f8e7364ca22dbf0.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Has now been turned in to&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/c22d02b3790b4ebd953bcb328df36d33.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Scheduled Jobs&lt;/h3&gt;
&lt;p&gt;Previously we had all of the jobs listed in the left side of the admin area as shown&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/3c4526d03a9d4b62b7a85b9669655922.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This has now been changed in the new UI to be under dedicated section and has had a few improvements in the UI, a few we&amp;rsquo;ve seen in 3rd party packages before.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f5d8b21b8ac54a3dbcd87beb1f749339.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Firstly this gives us a much better view of the jobs including the key information of running, interval and last run at a glance as well as a search which is very welcome. Then when looking at the job itself&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/c70ea766602f4d779b4f1a677435d2e1.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We can see mostly at the top the same information as before but the we now have the introduction of a graph showing execution duration which is very useful out of the box to see how long our jobs are taking.&lt;/p&gt;
&lt;h3&gt;Configurations&lt;/h3&gt;
&lt;p&gt;The config area has been re-named to configurations and now only has 4 core sections&lt;/p&gt;
&lt;h4&gt;Manage Sites&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/link/159bba2127394b8fbf0330e9244e89ea.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is pretty much like for like with no extra features&lt;/p&gt;
&lt;h4&gt;&amp;nbsp;Manage Languages&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/link/0e9b42193e1f43c38ad4429d00c27f85.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Again, this is pretty much like for like with no extra features&lt;/p&gt;
&lt;h4&gt;Manage Categories&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/link/e20f9c2804d949a4b00a70b3455170f7.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Again, pretty much the same, which to me is one of the biggest disappointments. I have used the Geta Categories heavily and was hoping with a rework of the categories system we might see this driven from IContent models to allow use greater control. Sadly, this still remains with the same fixed content and properties for the taxonomy, often making this fairly limiting.&lt;/p&gt;
&lt;p&gt;I appreciate however with the scope of the .NET 5 upgrade this might have been too much to add to their backlog for this version.&lt;/p&gt;
&lt;h4&gt;Manage Tabs&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/link/7b405e5b526c49fba40fd7a6c9a4d0f5.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Again, this is pretty much like for like with no extra features&lt;/p&gt;
&lt;h3&gt;Tools&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/link/c3e70c737560499d9006c0e6d91fa26d.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This whole section is like for like, no improvement for the import/export and the change log/licence information had no need of improvements.&lt;/p&gt;
&lt;h2&gt;Removals&lt;/h2&gt;
&lt;p&gt;The following areas have been removed in the .NET 5 upgrade. I&amp;rsquo;ve made some assumptions on these so correct me if I&amp;rsquo;m wrong in any areas&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Manage Content &amp;ndash; This was a useful place to quick manage access rights however as the same feature exists in the CMS tree is was mostly duplication which I assume is the reason for the removal however this could cause issues with packages or systems that created extra items such as under the global assets root node.&amp;nbsp;&lt;strong&gt;To note I&#39;ve been told this is on the backlog so should be coming back&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;System Settings &amp;ndash; This has some core settings for features that have been moved out to configuration and therefore I assume has been removed in favour of a configuration-based approach&lt;/li&gt;
&lt;li&gt;Edit Fames &amp;ndash; Redundant and not needed&lt;/li&gt;
&lt;li&gt;Edit Customer Property Types &amp;ndash; Should be done in code and no longer needed such as the XHtmlString configuration which is no done in Initialization&lt;/li&gt;
&lt;li&gt;Plug In Manager &amp;ndash; Redundant as packages are controlled via Nuget&lt;/li&gt;
&lt;li&gt;Mirroring &amp;ndash; Legacy feature now redundant&lt;/li&gt;
&lt;li&gt;Replace Page Names in Web Addresses &amp;ndash; Redundant and not needed&lt;/li&gt;
&lt;li&gt;Search Configuration - Redundant and not needed&lt;/li&gt;
&lt;li&gt;Create Page Type / Copy Page Type - Redundant and not needed&lt;/li&gt;
&lt;li&gt;Convert Pages &amp;ndash; This is one that I&amp;rsquo;m surprised is gone as it was very useful and I&amp;rsquo;ll be querying. &lt;strong&gt;To note I&#39;ve been told this is on the backlog so should be coming back&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The .NET 5 upgrade is a massive step forward for so so many reasons. The admin system has long needed refreshing and it&#39;s great to finally see us have it.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;In a later post I&#39;ll cover more about the solution and how to build on the .NET 5 version. Thanks all&lt;/p&gt;
&lt;p&gt;You can find part 2 over at &lt;a href=&quot;/link/8f5c0141ef6641b28d5db1654d69cc17.aspx&quot;&gt;https://world.optimizely.com/blogs/scott-reed/dates/2021/7/-net-5-preview-breakdown--alloy-project/&lt;/a&gt;&lt;/p&gt;</id><updated>2021-07-16T12:31:49.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Become an EMVP (OMVP) It&#39;s Great!!!</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2021/2/become-an-emvp-omvp-its-great/" /><id>&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;As everyone should be aware Episerver having bought Optimizly in 2020 is now rebranding under the Optimizly name &lt;a href=&quot;https://www.episerver.com/campaign/optimizely&quot;&gt;https://www.episerver.com/campaign/optimizely&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With all the investment in to Optimizly (Episerver), the new products and the Optimizly&#39;s push furthur in to the Leader quadrant &lt;a href=&quot;https://www.episerver.com/reports/gartner-magic-quadrant-for-digital-experience-platforms&quot;&gt;https://www.episerver.com/reports/gartner-magic-quadrant-for-digital-experience-platforms&lt;/a&gt; this is the perfect time to not only make the platform your jam but also become one of the experts.&lt;/p&gt;
&lt;p&gt;The page &lt;a href=&quot;/link/0e4c738fd29f4c0ebe73a97178bc995a.aspx&quot;&gt;https://world.episerver.com/emvp/&lt;/a&gt; lists a number of benefits but is a little dry so having been an EMVP for a number of years I&#39;m going to write my take on why YOU should try to become an EMVP (or whatever we end up being named :-p).&lt;/p&gt;
&lt;h2&gt;The Platform&lt;/h2&gt;
&lt;p&gt;Optimizly has been growing it&#39;s platform from the grass roots of it&#39;s starting points in the content management to the great digital experience platform it offers today. This includes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Enterpise CMS platform&lt;/strong&gt; - Features such as WYSIWYG, reusable blocks, rich media, workflow, dynamic forms, projects and much much more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise Commerce platform &amp;amp; B2B Commerce&lt;/strong&gt; - Features such as catalogues, tax, regions, shipping, campaigns, payment integration and much more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Profile Store and Intelligence &lt;/strong&gt;- Allows storage of complex customer tracking data and intelligent recommendation in both the content and commerce space&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Campaign &lt;/strong&gt;- A powerful multichannel marketting system allowing email, text and direct integration. Uses the intellgence of the DXP to personalise the experience&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social &lt;/strong&gt;- Headless social integration for comments, reviews and user based content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DXP Service &lt;/strong&gt;- The default hosting system built by Optimizly on the Azure stack providing a one stop shop for hosting, content delivery, security and deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The current product suite is a great fit for medium to large businesses in all sectors and Optimizly will provide expertese in any solution/journey.&lt;/p&gt;
&lt;p&gt;I personally love the stack, love the way the software is written and find great excitement about the future of the platform.&lt;/p&gt;
&lt;h2&gt;The People&lt;/h2&gt;
&lt;p&gt;The EMVP crowd is an amazing bunch of people. I personally have interacted with many great EMVPs both in person and online and there&#39;s a great amount of talented people pushing this forward. By becoming one of us you&#39;ll not only gain access to great benefits but great industry experts to converse with in our private EMVP Yammer channel. Also when we have meetups or attend socials all the EMVPs are very friendly and enjoy a good chat and a beer (or 5) :-)&lt;/p&gt;
&lt;h2&gt;The Benefits&lt;/h2&gt;
&lt;p&gt;The offical page lists the following&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VIP treatment at all Episerver events.&lt;/li&gt;
&lt;li&gt;Special EMVP-exclusive meetups and events.&lt;/li&gt;
&lt;li&gt;Episerver licenses for personal use.&lt;/li&gt;
&lt;li&gt;Yearly EMVP Summit, typically a 3 day off-site session in a beautiful part of the world&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Howver there are also a number of great benefits not listed&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access to the EMVP private channel which allows conversing with not only other EMVPs but Optimizly top guys.&lt;/li&gt;
&lt;li&gt;Access to regular webinars on the platform and future features&lt;/li&gt;
&lt;li&gt;Access to free courses on the Episerver Academy so that your knowledge and training is kept top notch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And most of all the pride of being one of a great bunch of experts on a great platform&lt;/p&gt;
&lt;h2&gt;How do you become one?&lt;/h2&gt;
&lt;p&gt;So to become one you must try to do as much of the following as possible and then you&#39;ll hopefully get nominated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be Episerver Certified in Content/Commerce&lt;/li&gt;
&lt;li&gt;Be active on the blogs and forums providing great content and helping people regularly&lt;/li&gt;
&lt;li&gt;Contribute to the community. Be it blogs, github project contribution or Nuget packages it&#39;s all about pushing the platform forward and helping others.&lt;/li&gt;
&lt;li&gt;Be friendly. Talk to people, converse, attend events and get to know people. If you&#39;re known you&#39;ll easily become a lover of the platform&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The platform is great, the people are great and it&#39;s a great community to be part of. So be kind, be passionate and you&#39;ll so find yourself in the club.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I look forward to seeing you around!!!&lt;/strong&gt;&lt;/p&gt;</id><updated>2021-02-19T10:56:21.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Handling Delete Content Events Within the Waste Basket / Trash</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2020/11/handling-content-events-with-the-waste-basket/" /><id>&lt;p&gt;When working with episerver content events expsed to you via the&amp;nbsp;&lt;strong&gt;IContentEvents&amp;nbsp;&lt;/strong&gt;service that involve deleting content we have a couple of events&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DeletingContent - Which fires when attempting to delete content and allows you to return a boolean to stop it&lt;/li&gt;
&lt;li&gt;DeletedContent - Which fires after the item has been deleted.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However it&#39;s important to be aware when these events fire...&lt;/p&gt;
&lt;p&gt;Both of these events actually fire when deleting from trash directly or when clicking the &quot;Empty Trash&quot; button. If you were these to fire when the item is moved to trash / waste basket you&#39;d be wrong and you actually need to handle the&amp;nbsp;MovedContent /&amp;nbsp;MovingContent events.&lt;/p&gt;
&lt;p&gt;Now when it comes to working with these two deletion events it&#39;s important to know that there&#39;s an odd kink in how these work depending on if you&#39;ve selected Delete on an item in trash or you&#39;ve clicked the&amp;nbsp;&quot;Empty Trash&quot; button.&lt;/p&gt;
&lt;p&gt;The key difference is&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When handling the event from a direct delete on a specific item in the trash / waste basket, the passed item to the event handler is the ContentLink of the item&lt;/li&gt;
&lt;li&gt;When handing the event from the &quot;Empty Trash&quot; button, the passed item to the event handler in the ContentLink of the&amp;nbsp;&lt;strong&gt;WasteBasket.&amp;nbsp;&lt;/strong&gt;It&#39;s also worth noting that getting the children of the waste basket DID NOT work and I had to use descendants.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So as a code example in a recent project I had to hook in to this event to delete an item from a custom database table sorting some related information and this lead to me implementing the following (&lt;em&gt;&lt;strong&gt;excuse the use of DataFactory / No Dependency Inject it&#39;s due to the project it&#39;s used on&lt;/strong&gt;&lt;/em&gt;).&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;// &amp;lt;summary&amp;gt;
    /// Handles content events from Episerver
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;seealso cref=IInitializableModule&quot; /&amp;gt;
    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ContentEventsInitialization : IInitializableModule
    {
        /// &amp;lt;summary&amp;gt;
        /// Initializes this instance.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;context&quot;&amp;gt;The context.&amp;lt;/param&amp;gt;
        /// &amp;lt;remarks&amp;gt;
        /// Gets called as part of the EPiServer Framework initialization sequence. Note that it will be called
        /// only once per AppDomain, unless the method throws an exception. If an exception is thrown, the initialization
        /// method will be called repeadetly for each request reaching the site until the method succeeds.
        /// &amp;lt;/remarks&amp;gt;
        public void Initialize(InitializationEngine context)
        {
            var events = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();
            events.DeletingContent += DeletingContent;
        }

        /// &amp;lt;summary&amp;gt;
        /// Preloads the specified parameters.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;parameters&quot;&amp;gt;The parameters.&amp;lt;/param&amp;gt;
        public void Preload(string[] parameters)
        {
        }

        /// &amp;lt;summary&amp;gt;
        /// Resets the module into an uninitialized state.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;context&quot;&amp;gt;The context.&amp;lt;/param&amp;gt;
        /// &amp;lt;remarks&amp;gt;
        /// &amp;lt;para&amp;gt;
        /// This method is usually not called when running under a web application since the web app may be shut down very
        /// abruptly, but your module should still implement it properly since it will make integration and unit testing
        /// much simpler.
        /// &amp;lt;/para&amp;gt;
        /// &amp;lt;para&amp;gt;
        /// Any work done by &amp;lt;see cref=&quot;M:EPiServer.Framework.IInitializableModule.Initialize(EPiServer.Framework.Initialization.InitializationEngine)&quot; /&amp;gt; as well as any code executing on &amp;lt;see cref=&quot;E:EPiServer.Framework.Initialization.InitializationEngine.InitComplete&quot; /&amp;gt; should be reversed.
        /// &amp;lt;/para&amp;gt;
        /// &amp;lt;/remarks&amp;gt;
        public void Uninitialize(InitializationEngine context)
        {
        }

        /// &amp;lt;summary&amp;gt;
        /// Deletings the content.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;sender&quot;&amp;gt;The sender.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&quot;e&quot;&amp;gt;The &amp;lt;see cref=&quot;ContentEventArgs&quot;/&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;
        private void DeletingContent(object sender, ContentEventArgs e)
        {
            RemoveAlertsFromDatabase(e);
        }

        /// &amp;lt;summary&amp;gt;
        /// Handles removing of alerts from database when they are deleted.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;e&quot;&amp;gt;The &amp;lt;see cref=&quot;ContentEventArgs&quot;/&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;
        private void RemoveAlertsFromDatabase(ContentEventArgs e)
        {
            var alertsManager = new AlertsManager();

            if (e.ContentLink.ID == ContentReference.WasteBasket.ID)
            {
                var wasteBasketReferences = DataFactory.Instance.GetDescendents(e.ContentLink);

                foreach (var wasteBasketReference in wasteBasketReferences)
                {
                    var item = DataFactory.Instance.Get&amp;lt;IContent&amp;gt;(wasteBasketReference);

                    if (item is AlertItem)
                    {
                        alertsManager.RemoveAlerts(wasteBasketReference.ID);
                    }
                }
            }

            if (e.Content is AlertItem)
            {
                alertsManager.RemoveAlerts(e.ContentLink.ID);
            }
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully this helps anyone with working with delete events when in the trash&lt;/p&gt;</id><updated>2020-11-09T12:55:05.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Pass Portal Content Copying Between Environments</title><link href="https://world.optimizely.com/blogs/scott-reed/dates/2020/9/pass-portal-content-copying-between-environments/" /><id>&lt;p&gt;On logging in to the pass portal for a deployment I noticed a new great feature added to the pass portal.&lt;/p&gt;
&lt;p&gt;Now under each environment there appears a &quot;Copy content&quot; button that appears.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f8be497295734588b5888eaf198570bc.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you click on this it will allow you to restore the blobs/database between environments. The supported options are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Integration -&amp;gt; Pre Production&lt;/li&gt;
&lt;li&gt;Pre Production -&amp;gt; Integration&lt;/li&gt;
&lt;li&gt;Production -&amp;gt; Pre Production&lt;/li&gt;
&lt;li&gt;Production -&amp;gt; Integration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full documentation can be found at&amp;nbsp;&lt;a href=&quot;/link/bbe7d26827ff4299b3d96b8fac647486.aspx&quot;&gt;https://world.episerver.com/documentation/developer-guides/digital-experience-platform/self-service/content-synchronization/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This is great for the continued increase in features that come to the pass portal UI and well as the great Deployment API&amp;nbsp;&lt;a href=&quot;/link/cecd04dddeea47f89a14d7f163913728.aspx&quot;&gt;https://world.episerver.com/documentation/developer-guides/digital-experience-platform/deploying/episerver-digital-experience-cloud-deployment-api/&lt;/a&gt;&amp;nbsp;which has a whole host of extra features in it.&lt;/p&gt;</id><updated>2020-09-08T11:18:35.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>