<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>source code bean &#187; EPiServer</title>
	<atom:link href="http://sourcecodebean.com/archives/category/episerver/feed" rel="self" type="application/rss+xml" />
	<link>http://sourcecodebean.com</link>
	<description>thoughts and ideas from a .net developer</description>
	<lastBuildDate>Sat, 26 May 2012 09:45:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>EPiServer – Continuous Integration and Deployment</title>
		<link>http://sourcecodebean.com/archives/episerver-%e2%80%93-continuous-integration-and-deployment/716</link>
		<comments>http://sourcecodebean.com/archives/episerver-%e2%80%93-continuous-integration-and-deployment/716#comments</comments>
		<pubDate>Tue, 30 Nov 2010 08:28:06 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[EPiServer]]></category>
		<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://sourcecodebean.com/?p=716</guid>
		<description><![CDATA[Two weeks ago I went to the EPiServer Meetup where Bartek Tatkowski talked about EPiServer and CI. This got me inspired to write about how we do CI and deployment in my current project. The site we are building is an EPiServer 6 CMS Enterprise site based on .NET 4.0 and ASP.NET MVC 2.0 (I [...]]]></description>
			<content:encoded><![CDATA[<p>Two weeks ago I went to the <a href="http://www.meetup.com/EPiServer-Stockholm/calendar/15377221/">EPiServer Meetup </a>where <a href="http://twitter.com/tatkowski">Bartek Tatkowski</a> talked about EPiServer and CI. This got me inspired to write about how we do CI and deployment in my current project. The site we are building is an EPiServer 6 CMS Enterprise site based on .NET 4.0 and ASP.NET MVC 2.0 (<a href="http://sourcecodebean.com/archives/first-steps-toward-episerver-and-asp-net-mvc/595">I blogged about it back in Apri</a>l). When we started the project we had very clear goals regarding CI and deployment:</p>
<ul>
<li>The build process should be totally automated</li>
<li>Building the site should result in a release package</li>
<li>The build server should run unit tests, code analysis and test code coverage every time the build is run</li>
<li>The deployment process should be totally automatic (for the test environment and for the production environment)</li>
<li>It should always be possible to rollback an update (files, database, IIS-settings, etc.). Rollback should also be fully automated.</li>
</ul>
<p>I am very happy to say that we have fulfilled all our above goals, let me tell you have we did it.<br />
<span id="more-716"></span></p>
<h2>Build process and the release package</h2>
<p>We are using TeamCity as build server. TeamCity is great build server which support numerous different build agents and it hooks up very easily with our Mercurial version control system. This is what a typical check in/build/package scenario looks like:</p>
<ol>
<li>1. A developer checks in new code</li>
<li>2. TeamCity picks up the change from Mercurial and starts a build</li>
<li>3. Once built TeamCity runs all the units tests, code analysis and code coverage</li>
<li>4. If the unit tests are successful the TeamCity creates a deploy package. The package contains a MSDeploy zip file, configuration files and our deploy tool chain (Powershell scripts and a few executables) – everything you need to deploy the site.</li>
</ol>
<p>By downloading and extracting this file you get a file structure that looks like this:</p>
<p><a href="http://sourcecodebean.com/wp-content/uploads/2010/11/release-package.png"><img class="aligncenter size-full wp-image-721" title="release-package" src="http://sourcecodebean.com/wp-content/uploads/2010/11/release-package.png" alt="" width="208" height="212" /></a></p>
<p><strong>Scripts</strong> – contains various scripts needed to perform deployment, the most important being <code>Deploy.ps1</code> and <code>Deploy-Local.ps1</code>.</p>
<p><strong>SiteConfig</strong> – contains shared settings and one directory per unique environment (AT = Acceptance Test, ST = System Test, Dev-PM = My development environment). In the SiteConfig directory we also find the following files:</p>
<ul>
<li>settings.xml</li>
<li>episerver-config.master.xml</li>
<li>episerver-config-site1.xml</li>
<li>episerver-config-site2.xml</li>
</ul>
<p>The <code>settings.xml</code> is the base of all non-episerver settings. It contains connection strings to databases, paths to where the web site should be installed, where logs should be placed, where VPP files are placed, integration settings to external systems and much more.</p>
<p>In each environment specific directory, such as “AT” it is possible to override these settings by creating a <code>settings.xml</code>. The new <code>settings.xml</code> does not replace the one in the parent directory, it is merged with it and the environment specific file takes precedence over the general.</p>
<p>A simple example let pretend this is our <code>\SiteSettings\settings.xml</code>:</p>
<p><code>&lt;settings&gt;<br />
&nbsp;&nbsp;&lt;webDir&gt;D:\WebSites\&lt;/webDir&gt;<br />
&nbsp;&nbsp;&lt;logDir&gt;D:\Logs&lt;/logDir&gt;<br />
&lt;/settings&gt;</code></p>
<p>Say that I would like to override where the logs are stored in my ST environment. To do this I would create <code>\SiteSettings\ST\settings.xml</code>, containing:</p>
<p><code>&lt;settings&gt;<br />
&nbsp;&nbsp;&lt;logDir&gt;C:\Temp\Logs&lt;/logDir&gt;<br />
&lt;/settings&gt;</code></p>
<p>The merged config would be:</p>
<p><code>&lt;settings&gt;<br />
&nbsp;&nbsp;&lt;webDir&gt;D:\WebSites\&lt;/webDir&gt;<br />
&nbsp;&nbsp;&lt;logDir&gt;C:\Temp\Logs&lt;/logDir&gt;<br />
&lt;/settings&gt;</code></p>
<p>We use this for both development and deployment. When a developer checks out the source code, there is a “<code>Init.ps1</code>” script in the Scripts folder (not included in the release package). This script will setup the website according to the specified settings (in my case the Dev-PM folder). The script configures the local IIS server, creates app pools,  creates the sites, sets permissions on folders, creates local MSMQ queues, configures NServiceBus and so on. Thanks to this script all the developers have a development environment that is nearly identical! Super sweet!</p>
<p>Ok, back to deployment. As you might have figured out, the <code>episerver-config.master.xml</code> serves as base for all episerver sites. Since this is an enterprise installation we can have several sites and each –siteX represents such a site.</p>
<p>All the settings in the files are overridable in the same way as with <code>settings.xml</code>. We use this to, for example, provide the appropriate license file for the specific environment. </p>
<p><strong>SiteWebDeployPackage</strong></p>
<p>This might be the least exciting directory, it simply contains a MS WebDeploy package created by MSBuild. This package contains everything needed to run the sites, except for the site specific configuration files that <code>Deploy.ps1/Deploy-Local.ps1</code> will put in place.</p>
<p><strong>Tools<br />
</strong>This directory contains our “deploy tool chain”. The tool chain includes, MSDeploy.exe (WebDeploy), SimpleScriptRunner.exe (DB migration), 7za.exe (zip) and a bunch of other tools needed to setup and install Msmq and NserviceBus.</p>
<h2>The deploy process</h2>
<p>When we started implementing the automated deployment we were hoping to be able to do most of the heavy lifting in WebDeploy. But after a lot of research and testing we came to the conslusion that it was probably possible to do what we wanted to do in WebDeploy (WebDeploy is very powerfull), but we could not figure out how. The WebDeploy documentation at that time was almost non-existent and had few advanced examples. Instead we made the decision to use PowerShell for most of the work and use WebDeploy only as a method to package transfer files. </p>
<p>This is the steps needed to perform a deployment:</p>
<ol>
<li>1. Download the latest release package from TeamCity</li>
<li>2. Extract it, start PowerShell and cd into the Scripts folder</li>
<li>3. Run &#8220;Deploy.ps1 &lt;environment&gt;&#8221;. The addresses of the servers are confgured in the environment settings</li>
</ol>
<p>This is what Deploy.ps1 does:</p>
<ol>
<li>1. Remotes to the service bus host and takes it offline</li>
<li>2. Remotes to the webserver and takes the site offline</li>
<li>3. Creates a backup of the entire IIS containing all its settings and sites using WebDeploy (used if rollback is carried out)</li>
<li>4. Creates a backup of all databases (used for rollback)</li>
<li>5. Uses SimpleScriptRunner to connect to our database server and apply database schema updates on our databases (not the EPiServer database)</li>
<li>6. Copies the deploy package to the webserver</li>
<li>7. Remotes to the webserver and runs <code>Deploy-Local.ps1</code>. The script installs the files, configures IIS, copies license files, etc.</li>
<li>8. Starts the site and the service bus and runs runs integration tests</li>
<li>9. Print result and exit!</li>
</ol>
<p>If the update of some reason is not working as it should, the update can be easily rolled back by executing  &#8220;Deploy.ps1 &lt;environment&gt; -Rollback&#8221;. This will restore all affected system to exactly the same point as they were before the update.</p>
<p><strong><br />
</strong></p>
<h2>Conclusion</h2>
<p>Automating the build and the deployment process is absolutly worth the effort it takes to implement it. By automating the process we have been able to catch more bugs, develop and release faster and at the same time make the entire process less dependent on certain people in the group &#8211; our knowledge is documented in the build and deployment scripts.</p>
<p>A very nice bonus from this is that we can version control everything related to the site, IIS settings, EPiServer settings, EPiServer Licenses and much more. Also, installing the release on a &#8220;empty&#8221; server is as easy as updating an existing installation.<br />
<br/></p>
<p><strong>The tools that made all this possible:</strong></p>
<p>Microsoft <a href="http://www.iis.net/download/webdeploy">WebDeploy</a> &#8211; Deployment tool from Microsoft.</p>
<p><a href="https://github.com/JamesKovacs/psake">Psake</a>- Build automation tool written in PowerShell (inspired by rake in Ruby).</p>
<p><a href="http://code.google.com/p/simplescriptrunner/">SimpleScriptRunner </a>- Tool to run a directory of numbered sql scripts against an MSSQL database.</p>
<p><a href="http://opendbiff.codeplex.com/">OpenDBDiff </a>- Tool/library to generate SQL-diff scripts.</p>
<p><a href="http://pagetypebuilder.codeplex.com/">PageTypeBuilder</a> &#8211; Automatic synchronization of pagetypes in EPiServer (page types are defined in code)</p>
]]></content:encoded>
			<wfw:commentRss>http://sourcecodebean.com/archives/episerver-%e2%80%93-continuous-integration-and-deployment/716/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>First steps toward EPiServer and ASP.NET MVC</title>
		<link>http://sourcecodebean.com/archives/first-steps-toward-episerver-and-asp-net-mvc/595</link>
		<comments>http://sourcecodebean.com/archives/first-steps-toward-episerver-and-asp-net-mvc/595#comments</comments>
		<pubDate>Mon, 19 Apr 2010 20:33:38 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[EPiServer]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://sourcecodebean.com/?p=595</guid>
		<description><![CDATA[Recently I started a new assignment at a customer where we are going to build their new public web site and their intranet, both of them based on EPiServer CMS 6. The person leading the project is very found of ASP.NET MVC, so my first task was to experiment with EPiServer and ASP.NET MVC, to [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I started a new assignment at a customer where we are going to build their new public web site and their intranet, both of them based on EPiServer CMS 6. The person leading the project is very found of ASP.NET MVC, so my first task was to experiment with EPiServer and ASP.NET MVC,  to try to get a running prototype up. </p>
<p>I did some research online to see what others had done. I found two great posts by <a href="http://joelabrahamsson.com/entry/episerver-and-mvc-retrieving-current-page-using-a-custom-model-binder">Joel Abrahamsson</a> and one from <a href="http://fbrz.wordpress.com/2009/10/10/episerver-and-mvc/">Fabio Fabrizio</a>, who based his solution on Joels experiments. </p>
<p>Joels first attempt was to create a base class from which all Controllers inherited. However, this made the controller do more than it should and made it very hard to test. His second attempt was to create a custom model binder and make every action receive currentPage as a parameter. This approach makes it much easier to create unit tests for the code, since it is now possible to moc the currentPage parameter. </p>
<p>Fabio took a different approach and decided to implement a custom MVC handler and a controller factory. This approach fits very well with MVC and felt like the right way to go, so I decided to create a prototype based on Fabios code. Fabios code was based on EPiServer CMS 5 and ASP.NET MVC 1.0, but I wanted to use EPiServer 6 and ASP.NET MVC 2.0, so I started porting the solution.</p>
<p>Porting the solution to EPiServer 6 and ASP.NET MVC 2.0 required some changes to both code and configuration, which not all of them was trivial. At the moment I have a prototype up running, and it works remarkably well! I will continue my experiments and post my results.</p>
]]></content:encoded>
			<wfw:commentRss>http://sourcecodebean.com/archives/first-steps-toward-episerver-and-asp-net-mvc/595/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>EPiServer friendly URLs for paginated pages (and why the asp:LinkButton must die)</title>
		<link>http://sourcecodebean.com/archives/episerver-friendly-urls-for-paginated-pages-and-why-the-asplinkbutton-must-die/510</link>
		<comments>http://sourcecodebean.com/archives/episerver-friendly-urls-for-paginated-pages-and-why-the-asplinkbutton-must-die/510#comments</comments>
		<pubDate>Mon, 15 Mar 2010 06:05:37 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[EPiServer]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://sourcecodebean.com/?p=510</guid>
		<description><![CDATA[Recently I got a request from a customer to perform some search engine optimizing for an old EPiServer site we are maintaining. One of the optimizations was to fix the paging on their product page. The products are fetched from an external data source and are not stored as pages in EPiServer, thus normal EPiServer [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I got a request from a customer to perform some search engine optimizing for an old EPiServer site we are maintaining. One of the optimizations was to fix the paging on their product page. The products are fetched from an external data source and are not stored as pages in EPiServer, thus normal EPiServer paging controls can not be used. Image a normal paging control like this:</p>
<p>Prev 1, 2 , 3, &#8230;, 99 Next</p>
<p>A fairly normal approach to this would have been to use a query parameter for handling the paging. Like this:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">&lt;a href=&quot;/products/?page=4&quot;&gt;4&lt;/a&gt;</div>
</li>
</ol>
</div>
<p>By using this approach each page would have a unique entry point, using query strings is however not optimal for SEO, but even worse is using asp:LinkButtons. <span id="more-510"></span>Whoever created the site had decided to use asp:LinkButtons. LinkButtons are very convenient to work with in ASP.NET, but the HTML code they generate is not very SEO friendly. This is the HTML that was generated:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&lt;div id=&quot;ctl00_MainContent_pnlPaging&quot;&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &lt;a id=&quot;ctl00_MainContent_rptPages_ctl00_lbPage&quot;
</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp;href=&quot;javascript:__doPostBack(&#8216;ctl00$MainContent$rptPages$ctl00$lbPage&#8217;,&#8221;)&quot;&gt;1&lt;/a&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &lt;a id=&quot;ctl00_MainContent_rptPages_ctl01_lbPage&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;href=&quot;javascript:__doPostBack(&#8216;ctl00$MainContent$rptPages$ctl01$lbPage&#8217;,&#8221;)&quot;&gt;2&lt;/a&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &lt;a id=&quot;ctl00_MainContent_rptPages_ctl02_lbPage&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;href=&quot;javascript:__doPostBack(&#8216;ctl00$MainContent$rptPages$ctl02$lbPage&#8217;,&#8221;)&quot;&gt;3&lt;/a&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &lt;a id=&quot;ctl00_MainContent_lbNext&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;href=&quot;javascript:__doPostBack(&#8216;ctl00$MainContent$lbNext&#8217;,&#8221;)&quot;&gt;Next&lt;/a&gt;
</div>
</li>
<li class="li2">
<div class="de2">&lt;/div&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
</ol>
</div>
<p>We can see that:</p>
<ul>
<li>LinkButtons generates a javascript that is run when the link is clicked, instead of using a normal link</li>
<li>Paging is handled using a postback, the pages will no longer have unique entry points.</li>
</ul>
<p>This is <strong>disastrous </strong>from a SEO perspective.</p>
<p>So lets fix it. First step is to replace the LinkButtons with normal links (asp:Hyperlink) &#8211; and then we are going to do some URL rewrite magic to create SEO-friendly URLs. The code behind was changed to assign links like <code>/products/?page=4</code>.</p>
<p>Now, to get URLs like <code>/products/page/4</code>, we can create a custom url rewrite module in EPiServer. First we create a new class, Rewrite,  that inherits from <code>EPiServer.Web.FriendlyUrlRewriteProvider</code>. In this class we override three methods (don&#8217;t ask we why they have such confusing names, someone at EPiServer must have been under the influsene of something when nameing them):</p>
<ul>
<li><code>ConvertToInternalInternal </code>- Used to convert from <code>/product/page/4/</code> to an internal EPiServer page with the page id as a query parameter</li>
<li><code>ConvertToExternalInternal </code>- Used to convert from an internal EPiServer URL (<code>/PageType.aspx?lotsofqueryparameters=values</code>) to an external (<code>/products/page/4/</code>)</li>
<li><code>ConvertToInternal </code>- Needed to work around URL rewrite caching behaviour in EPiServer, will get into more detail on this later.</li>
</ul>
<p>This is the class:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">namespace Utils</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">public class Rewrite : FriendlyUrlRewriteProvider <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// The regexp to match a paged url</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; <span class="kw4">string</span> _regexpPaging = @<span class="st0">&quot;(.+)/page/([0-9]+)/$&quot;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; protected override bool ConvertToInternalInternal<span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; UrlBuilder url, ref object internalObject<span class="br0">&#41;</span><span class="br0">&#123;</span>&#8230;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; protected override bool ConvertToExternalInternal<span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; UrlBuilder url, object internalObject, Encoding toEncoding<span class="br0">&#41;</span><span class="br0">&#123;</span>&#8230;<span class="br0">&#125;</span> </div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; public override bool ConvertToInternal<span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; UrlBuilder url, out object internalObject<span class="br0">&#41;</span> <span class="br0">&#123;</span>&#8230;<span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/></p>
<p>The first method we implement is the ConvertToInternalInternal which will convert the URL to an internal EPiServer URL.</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">protected override bool ConvertToInternalInternal<span class="br0">&#40;</span>UrlBuilder url, ref object internalObject<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>url == <span class="kw2">null</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// Regexp to match if the URL ends with /page/{Id}/</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; Match match = Regex.<span class="me1">Match</span><span class="br0">&#40;</span>url.<span class="me1">Path</span>, _regexpPaging<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; <span class="co1">// If we have a match, remove the /page/{Id}/ from the end of the URL</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// and add a querystring to the internal URL that is dqcPagingId </span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// (should have a unique name to not clash with some other querystring)</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>match.<span class="me1">Length</span> &gt; <span class="nu0">0</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; url.<span class="me1">Path</span> = match.<span class="me1">Groups</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>.<span class="me1">Value</span> + <span class="st0">&quot;/&quot;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; url.<span class="me1">QueryCollection</span><span class="br0">&#91;</span><span class="st0">&quot;dqcPagingId&quot;</span><span class="br0">&#93;</span> = match.<span class="me1">Groups</span><span class="br0">&#91;</span><span class="nu0">2</span><span class="br0">&#93;</span>.<span class="me1">Value</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; base.<span class="me1">ConvertToInternalInternal</span><span class="br0">&#40;</span>url, ref internalObject<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">true</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// Now when the /page/{Id}/ is removed from the URL, and the querystring is added, we can let EPiServer do its normal URL-rewriting.</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">return</span> base.<span class="me1">ConvertToInternalInternal</span><span class="br0">&#40;</span>url, ref internalObject<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/></p>
<p>While not nessesary, we should override the <code>ConvertToExternalInternal </code>as well. This will let EPiServer automatically convert internal urls containing the <code>dqcPagingId </code>querystring to a external url ending with /page/{Id}/.</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">protected override bool ConvertToExternalInternal<span class="br0">&#40;</span>UrlBuilder url, object internalObject, Encoding toEncoding<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="co1">// First let EPiServer convert the internal URL to an external. This will give us a URL like:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="co1">// /Products/?dqcPagingId=5 (if it is a paged page)</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp;bool isRewritten = base.<span class="me1">ConvertToExternalInternal</span><span class="br0">&#40;</span>url, internalObject, toEncoding<span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// Check if the URLs query string contains dqcPagingId</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="co1">// If it does we add /page/{Id} to the URL and removes the query string</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>url.<span class="me1">Query</span>.<span class="me1">Contains</span><span class="br0">&#40;</span><span class="st0">&quot;dqcPagingId&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; url.<span class="me1">Path</span> = <span class="kw4">string</span>.<span class="me1">Concat</span><span class="br0">&#40;</span>url.<span class="me1">Path</span>, <span class="st0">&quot;page/&quot;</span>, url.<span class="me1">QueryCollection</span><span class="br0">&#91;</span><span class="st0">&quot;dqcPagingId&quot;</span><span class="br0">&#93;</span>, <span class="st0">&quot;/&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; url.<span class="me1">QueryCollection</span>.<span class="me1">Remove</span><span class="br0">&#40;</span><span class="st0">&quot;dqcPagingId&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">return</span> isRewritten;</div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p><br/></p>
<p>Now one can think we would be done, but there is one more method we need to implement. EPiServer uses a cache to cache URL rewrites from external URLs to internal URLs, but it will only cache the querystrings used by EPiServer, not the <code>dqcPagingId </code>we added. This gives a rather unexpected result. The first time the page is loaded everything loads fine, from code behind we can access the <code>dqcPagingId </code>and show the requested page. If you wait 10 senconds (default cache time) or more and reloads the page, it works fine. But if you reload the page sooner, the dqcPagingId will not be set when the page loads. This is because the querystring <code>dqcPagingId </code>is not cached by EPiServer. This took me quite some time to figure out.</p>
<p>The solution to the problem is to override ConvertToInternal to bypass the default caching:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">public override bool ConvertToInternal<span class="br0">&#40;</span>UrlBuilder url, out object internalObject<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// If the URL end on /page/{id}/, bypass cache by calling ConvertToInternalInternal</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; <span class="co1">// A more optimal solution would be to perform some kind of caching here</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Regex.<span class="me1">IsMatch</span><span class="br0">&#40;</span>url.<span class="me1">Path</span>, _regexpPaging<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; internalObject = <span class="kw2">null</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ConvertToInternalInternal<span class="br0">&#40;</span>url, ref internalObject<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">true</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// Else, it is ok to use the cached result</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">return</span> base.<span class="me1">ConvertToInternal</span><span class="br0">&#40;</span>url, out internalObject<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2"><span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
</ol>
</div>
<p><br/></p>
<p>The last step is to add our Rewrite module to the web.config:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &lt;providers&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &lt;add
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;name=&quot;MyUrlRewriter&quot;
</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp;enableSimpleAddress=&quot;true&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;friendlyUrlCacheAbsoluteExpiration=&quot;0:0:10&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;type=&quot;Utils.Rewrite, Utils&quot;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp;description=&quot;URL rewriter for paging&quot; /&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &lt;/providers&gt;
</div>
</li>
<li class="li2">
<div class="de2">&lt;/urlRewrite&gt;
</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
</ol>
</div>
<p><br/></p>
<p>Now we should be all done! Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://sourcecodebean.com/archives/episerver-friendly-urls-for-paginated-pages-and-why-the-asplinkbutton-must-die/510/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Rename a Folder using EPiServer.Web.Hosting.UnifiedDirectory</title>
		<link>http://sourcecodebean.com/archives/rename-a-folder-using-episerver-web-hosting-unifieddirectory/304</link>
		<comments>http://sourcecodebean.com/archives/rename-a-folder-using-episerver-web-hosting-unifieddirectory/304#comments</comments>
		<pubDate>Mon, 05 Oct 2009 00:02:47 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[EPiServer]]></category>

		<guid isPermaLink="false">http://sourcecodebean.com/?p=304</guid>
		<description><![CDATA[While reading the EPiServer 5 SDK documentation, i found this: Rename a Folder There is no Rename method on the EPiServer.Web.Hosting.UnifiedDirectory class. To rename a folder you need to call the MoveTo method as follows: &#160; protected void RenameFolder&#40;string path, string oldName, string name&#41; &#123; &#160; &#160; if &#40;IsFolder&#40;path&#41;&#41; &#160; &#160; &#123; &#160; &#160; &#160; [...]]]></description>
			<content:encoded><![CDATA[<p>While reading the EPiServer 5 SDK documentation, i found this:</p>
<blockquote><p><strong>Rename a Folder</strong></p>
<p>There is no Rename method on the EPiServer.Web.Hosting.UnifiedDirectory class. To rename a folder you need to call the MoveTo method as follows:</p>
<div class="dean_ch" style="white-space: wrap;">
<ol>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">protected <span class="kw4">void</span> RenameFolder<span class="br0">&#40;</span><span class="kw4">string</span> path, <span class="kw4">string</span> oldName, <span class="kw4">string</span> name<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsFolder<span class="br0">&#40;</span>path<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; UnifiedDirectory directory = </div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; System.<span class="me1">Web</span>.<span class="me1">Hosting</span>.<span class="me1">HostingEnvironment</span>.<span class="me1">VirtualPathProvider</span>.<span class="me1">GetDirectory</span><span class="br0">&#40;</span>path<span class="br0">&#41;</span> as UnifiedDirectory;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> e = <span class="nu0">-1</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>path.<span class="me1">IndexOf</span><span class="br0">&#40;</span>oldName, ++e<span class="br0">&#41;</span> &gt; <span class="nu0">-1</span><span class="br0">&#41;</span> ;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; StringBuilder sb = new StringBuilder<span class="br0">&#40;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; sb.<span class="me1">Append</span><span class="br0">&#40;</span>path.<span class="me1">Substring</span><span class="br0">&#40;</span><span class="nu0">0</span>, e &#8211; <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; sb.<span class="me1">Append</span><span class="br0">&#40;</span>name<span class="br0">&#41;</span>;</div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; sb.<span class="me1">Append</span><span class="br0">&#40;</span><span class="st0">&quot;/&quot;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; directory.<span class="me1">MoveTo</span><span class="br0">&#40;</span>sb.<span class="me1">ToString</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp;</div>
</li>
</ol>
</div>
</blockquote>
<p>What a convenient way of renaming a folder <img src='http://sourcecodebean.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  Good thing that you don&#8217;t have to do it too often.</p>
]]></content:encoded>
			<wfw:commentRss>http://sourcecodebean.com/archives/rename-a-folder-using-episerver-web-hosting-unifieddirectory/304/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced

Served from: sourcecodebean.com @ 2012-05-30 05:58:08 -->
