<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Oskar Zetterberg</title> <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Adding market segment for Customized Commerce 14</title>            <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2024/5/adding-market-segment-for-customized-commerce-14/</link>            <description>&lt;p&gt;Since v.14 of commerce, the &lt;a href=&quot;https://marisks.net/2017/09/28/market-routing-in-episerver-commerce/&quot;&gt;old solution&lt;/a&gt;&amp;nbsp;for adding market segment to the url is not working anymore due to techinal changes of .NET Core. There is a &lt;a href=&quot;/link/5bb45fb903ba4732b02b4394eaf8939f.aspx&quot;&gt;post&lt;/a&gt; about adding market segment to Commerce 14 but that is about CMS and never got around with the commerce part. In that post Johan Bj&amp;ouml;rnfot added a comment suggesting using IContentUrlGeneratorEvents (thanks for the tip) and that is the approach I have used in my solution. No route mappings or overrides of internal components is needed.&lt;/p&gt;
&lt;p&gt;In an initialization class add two methods and attach them to the url generator events :&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public void Initialize(InitializationEngine context)
{
    var urlGeneratorEvents = context.Locate.Advanced.GetInstance&amp;lt;IContentUrlGeneratorEvents&amp;gt;();
    urlGeneratorEvents.GeneratedUrl += OnGeneratedUrl;

    var urlResolverEvents = context.Locate.Advanced.GetInstance&amp;lt;IContentUrlResolverEvents&amp;gt;();
    urlResolverEvents.ResolvingUrl += OnResolvingUrl;
}

private void OnGeneratedUrl(object sender, UrlGeneratorEventArgs e)
{
}

private void OnResolvingUrl(object sender, UrlResolverEventArgs e)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OnGeneratUrl, responsible for adding the market segment to the url.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private void OnGeneratedUrl(object sender, UrlGeneratorEventArgs e)
{
    // Validate if the url is a relative url and if it is a media file which we will not handle
    if (e.Context.Url.Path.StartsWith(&quot;/&quot;) &amp;amp;&amp;amp; !IsMedia(e.Context.Url.Path))
    {
        var segments = e.Context.Url.Path.Split(new [] { &#39;/&#39; }, StringSplitOptions.RemoveEmptyEntries);

        // More validation, if the url does not contain any / or if the first segment have greater length then 2 then we consider it to be a non language
        if (segments.Length &amp;lt; 1 || segments[0].Length &amp;gt; 2)
        {
            return;
        }

        var currentUrlMarketSegment = segments.Length &amp;gt; 1 ? segments[1] : null;

        // Get the market, could be with ICurrentMarket or cookie value. Where ever the current market is stored or can be retrieved by current language
        var marketSegment = GetMarketSegment(e.Context.Language, currentUrlMarketSegment);
        
        if (!string.IsNullOrEmpty(marketSegment))
        {
            var langMarketArg = $&quot;/{segments[0]}/{marketSegment}/&quot;;

            if (!e.Context.Url.Path.StartsWith(langMarketArg))
            {
                var languageArg = $&quot;/{segments[0]}/&quot;;

                // Validate if market segment already exists in the current url. If so, add that to part to be replaced.
                // AvailableMarketSegments just returns all available segments as a string array
                if (currentUrlMarketSegment != null &amp;amp;&amp;amp; currentUrlMarketSegment.Length == 2 &amp;amp;&amp;amp; AvailableMarketSegments.Contains(currentUrlMarketSegment))
                {
                    languageArg += $&quot;{currentUrlMarketSegment}/&quot;;
                }

                // Note, this value will be cached by Optimizley. That is why we need to check an existing market segment above.
                e.Context.Url.Path = e.Context.Url.Path.Replace(languageArg, langMarketArg);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OnResolvingUrl, responsible for removing the market segment from the url so the &quot;normal&quot; url resolving can take place.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private void OnResolvingUrl(object sender, UrlResolverEventArgs e)
{
    // Skip if url is media
    if (IsMedia(e.Context.Url.Path))
    {
        return;
    }

    var segments = e.Context.Url.Path.Split(new [] { &#39;/&#39; }, StringSplitOptions.RemoveEmptyEntries);
    
    if (segments.Length &amp;gt;= 2)
    {
        // Validate if segments are valid language and valid market and replace incoming segments with language only to let Optimizley resolve it internally
        if (AvailableLanguageSegments.Contains(segments[0]) &amp;amp;&amp;amp; AvailableMarketSegments.Contains(segments[1]))
        {
            var replaceArg = segments[0] + &quot;/&quot; + segments[1] + &quot;/&quot;;
            e.Context.RemainingSegments = e.Context.RemainingSegments.ToString()
                .Replace(replaceArg, segments[0] + &quot;/&quot;, StringComparison.OrdinalIgnoreCase).AsMemory();
            e.Context.Url = new Url(e.Context.Url.Uri.OriginalString.Replace(replaceArg, segments[0] + &quot;/&quot;));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take note, as markets are contextual, using the UrlResolver it will resolve market from the current context. So if you are storing urls in some searchindex or something like that, make sure you handle the urls accordingly. We are for example storing urls without language- and market segment and are adding those in the correct context when requested.&lt;/p&gt;
&lt;p&gt;The code can for sure be optimized and improved. Comment with suggestions.&lt;/p&gt;
&lt;p&gt;Hope this will be to use for someone.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2024/5/adding-market-segment-for-customized-commerce-14/</guid>            <pubDate>Thu, 02 May 2024 10:54:30 GMT</pubDate>           <category>Blog post</category></item><item> <title>PageTypeBuilder 2.0 with EPiServer CMS 7 Preview</title>            <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2012/9/PageTypeBuilder-20-with-EPiServer-CMS-7-Preview/</link>            <description>&lt;p&gt;This is a long overdue post about getting PageTypeBuilder 2.0 working with EPiServer 7 Preview.&lt;/p&gt; &lt;p&gt;I&#180;m not sure the changes I made is the best or even correct, but they work. Joel Abrahamsson, please comment on this.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Lets begin.&lt;/p&gt; &lt;p&gt;Get the source for &lt;a href=&quot;http://pagetypebuilder.codeplex.com/&quot;&gt;PageTypeBuilder 2.0&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Update the references from EPiServer CMS 6 to CMS 7.&lt;/p&gt; &lt;p&gt;Changes have to be made in three files, TabDefinitionUpdater.cs, PageDefinitionUpdater.cs and PageTypeRepository.cs.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;First up, PageTypeRepository&lt;/p&gt; &lt;p&gt;Add a new private member to the class and add a constructor&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; EPiServer.DataAbstraction.IContentTypeRepository _contentTypeRepository;
&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; PageTypeRepository()
{
_contentTypeRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance&amp;lt;IContentTypeRepository&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;In each of the Load methods (should be three of them) change PageType.Load(arg) to&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var pageType = (PageType)_contentTypeRepository.Load(arg).CreateWritableClone();&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
where arg is string name, Guid guid or int id.
&lt;p&gt;Last change IEnumerable&amp;lt;IPageType&amp;gt; List() from&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;PageType.List().Select(pageType =&amp;gt; &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; WrappedPageType(pageType)).Cast&amp;lt;IPageType&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;
to&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; _contentTypeRepository.List().Select(pageType =&amp;gt; &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; WrappedPageType((PageType)pageType)).Cast&amp;lt;IPageType&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;Now, compile and you will get four errors about ambiguous references due to referencing the new EPiServer dll. Clear them up by adding changing &lt;/p&gt;
&lt;p&gt;ITabDefinitionRepository to PageTypeBuilder.Abstractions.ITabDefinitionRepository where the errors occurs.&lt;/p&gt;
&lt;p&gt;Compile again and you will have a working PageTypeBuilder although there are 78 warnings all of them marking obsolete methods or calls.&lt;/p&gt;
&lt;p&gt;That’s it.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;</description>            <guid>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2012/9/PageTypeBuilder-20-with-EPiServer-CMS-7-Preview/</guid>            <pubDate>Wed, 19 Sep 2012 14:03:55 GMT</pubDate>           <category>Blog post</category></item><item> <title>LinkItemCollection and the danger with StringDelayedLoadThreshold</title>            <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/9/LinkItemCollection-and-the-danger-with-StringDelayedLoadThreshold/</link>            <description>&lt;p&gt;I have done a company-office-contactperson solution on our website. On a company page you specify in what cities that company reside in the form of a LinkItemCollection. The problem started today after I made a small update to the website (= recycling of the application pool), all the contact options just disappeared. After republish of each of the company pages they reappeared and went away after the next application pool recycle.&lt;/p&gt; Back to the development environment for debugging. Checked everything, the value in the database etc. Nothing was wrong. After more head scratching I remembered reading &lt;a href=&quot;http://world.episerver.com/Blogs/Alexander-Haneng/Dates/2011/9/EPiServer-CMS-Checklist-Deployment-Checklist/&quot;&gt;the deployment checklist by Alexander Haneng&lt;/a&gt; (great read btw). I had changed the stringDelayedLoadThreshold from 0 to 50. When I changed this value back all worked like a charm again.   &lt;p&gt;It all works fine with 50 as value on the actual page itself but when getting children with either FindPagesWithCriteria or GetChildren extension method I get null returned from the LinkItemCollection property. If anyone can shed some light on the differences I would appreciate it.&lt;/p&gt;  &lt;p&gt;So be careful setting the stringDelayedThreshold if you are working with LinkItemCollections.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/9/LinkItemCollection-and-the-danger-with-StringDelayedLoadThreshold/</guid>            <pubDate>Mon, 26 Sep 2011 17:50:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Small bug in XFormStatisticsControl</title>            <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/9/Small-bug-in-XFormStatisticsControl/</link>            <description>Had a problem where the statistics control didn’t render properly when using StatisticsType N (number). After a bit of digging with reflector it all came down to the priavte method GenerateOutputForNumbers and adding the cssstyle attribute to the panel. The calculated number of em is of type float and when that type is converted to string and the value is decimal it will be rendering a “,” when it should render a “.” as css ain&#180;t to keen on commas.   &lt;p&gt;I had to build my own control inheriting from the original one with this small change to that private method:&lt;/p&gt;  &lt;p&gt;panel.Attributes.CssStyle.Add(HtmlTextWriterStyle.Width, num2.ToString().Replace(&amp;quot;,&amp;quot;, &amp;quot;.&amp;quot;) + &amp;quot;em&amp;quot;); instead of panel.Attributes.CssStyle.Add(HtmlTextWriterStyle.Width, num2 + &amp;quot;em&amp;quot;);&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/9/Small-bug-in-XFormStatisticsControl/</guid>            <pubDate>Tue, 06 Sep 2011 11:39:02 GMT</pubDate>           <category>Blog post</category></item><item> <title>EPiServer CMS6 R2 and broken FriendlyUrlRewriteProvider</title>            <link>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/4/EPiServer-CMS6-R2-and-broken-FriendlyUrlRewriteProvider/</link>            <description>&lt;p&gt;Ran into alot of problems yesterday while upgrading our website to the latest EPiServer version, CMS6 R2. The problem: I have made a custom FriendlyUrlRewriteProvider that inherits from EPiServer.Web.FriendlyUrlRewriteProvider that partly stopped working. My custom querystrings was discarded and the link click rendered into a 404 visit.&lt;/p&gt;  &lt;p&gt;After alot of head scratching and dll and config checking I digged in to the EPiServer.dll of both the new and old version (CMS 6 and R2) and compared the two classes, method by method. Finally I realized that I needed to add an override for the method TryConvertToInternal with the same functionality as in ConvertToInternal (should have been obvious but I intend to always check the easiest solutions last).&lt;/p&gt;  &lt;p&gt;Now the pager and all the other rewrites works as expected. This might not be the correct way to handle this but from what I can tell it works as it should.&lt;/p&gt;  &lt;p&gt;Anyone with better solutions are more then welcome to comment on this.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Oskar-Zetterberg/Dates/2011/4/EPiServer-CMS6-R2-and-broken-FriendlyUrlRewriteProvider/</guid>            <pubDate>Wed, 13 Apr 2011 17:00:53 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>