<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Henrik Fransas</title><link href="http://world.optimizely.com" /><updated>2019-03-08T18:05:40.0000000Z</updated><id>https://world.optimizely.com/blogs/Henrik-Fransas/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Site Settings Url and Episerver Cloud license</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2019/3/site-settings-url-and-episerver-cloud-license/" /><id>&lt;p&gt;When Episerver released their Cloud license, also called the instance bound license that can be used both on prem or in ordinary Azure or Amazon they needed a way to get information on what site that is using the license. This to verify if there is a license for that site and to update/verify the number of servers/instances the site is running on.&lt;/p&gt;
&lt;p&gt;For that information they decided to use the URL property that is set in Admin/Config/Manage Websites and that is all fine, since that url is usually correct. But since the license usually say for example 1 site and 2 instances if you buy a license to be used in production and also in test environment the problem comes to how to determinate what site there is on the test environment and for that Episerver decided that URL in Site Settings must be the same on all environment. (Read more about it here:&amp;nbsp;&lt;a href=&quot;/link/65fa1c181cb943288e034d3ae875ab15.aspx&quot;&gt;https://world.episerver.com/documentation/developer-guides/CMS/Deployment/managing-cloud-licenses/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;This works fine for them and for most part of Episerver core (maybe there is some AddOns that still is not updated) but for us developers and our client implementation this can be a problem. The reason for that is that we used to use that URL to generate for example the external url or simular stuff.&lt;/p&gt;
&lt;p&gt;We used to have (or still have) code that could looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private string GetExternalUrl(string path)
{
	if (SiteDefinition.Current != null &amp;amp;&amp;amp; SiteDefinition.Current.SiteUrl != null)
	{
		return new Uri(SiteDefinition.Current.SiteUrl, path).AbsoluteUri;
	}

	return path;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code have a couple of problems, first it does not handle having multiple language and with the new License rule we will end up with production url&#39;s in test environment as well.&lt;/p&gt;
&lt;p&gt;To fix this we will use the hostname part of the settings instead (that is also what Episerver is using internally now). So first you need to update (if it is not already) inside Admin/Config/Manage Websites/Settings so you have a hostname that is marked as primary (for multi language, one primary per language). Also you need to mark the correct scheme for the hostname so you set it to https if you are using it.&lt;/p&gt;
&lt;p&gt;We then update the code for the function that was in the previous example so it takes some more parameters. We need to send in the language we want the external url for and also the ContentReference to the content. Inside the function we are going to use the interface&amp;nbsp;ISiteDefinitionResolver so we need the current implementation for that so either get it by constructor injection or parameter injection or &lt;a href=&quot;/link/5341f632537c4b0ab6b8fb651bd310f8.aspx?userid=611d36e4-f1d2-e011-837e-0018717a8c82&quot;&gt;Valdis&lt;/a&gt; forbids via ServiceLocator.&lt;/p&gt;
&lt;p&gt;Then the function can look like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private string GetExternalUrl(string path, CultureInfo lang, ContentReference contentLink, ISiteDefinitionResolver siteDefinitionResolver)
{
	var siteDefinition = this.siteDefinitionResolver.GetByContent(contentLink, true, true);
	var hosts = siteDefinition.GetHosts(lang, true).ToList();

	var host = hosts.FirstOrDefault(h =&amp;gt; h.Type == HostDefinitionType.Primary) ?? hosts.FirstOrDefault(h =&amp;gt; h.Type == HostDefinitionType.Undefined);

	var baseUri = siteDefinition.SiteUrl;

	if (host != null &amp;amp;&amp;amp; host.Name.Equals(&quot;*&quot;) == false)
	{
		var uriString = string.Format(&quot;http{0}://{1}&quot;, host.UseSecureConnection.HasValue &amp;amp;&amp;amp; host.UseSecureConnection.Value ? &quot;s&quot; : string.Empty, host.Name);
		
		// Try to create a new base URI from the host with the site&#39;s URI scheme. Name should be a valid
		// authority, i.e. have a port number if it differs from the URI scheme&#39;s default port number.
		if (!Uri.TryCreate(uriString, UriKind.Absolute, out baseUri))
		{
			return path;
		}
	}

	var absoluteUri = new Uri(baseUri, path);
	return absoluteUri.AbsoluteUri;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it is a little bit more code, and yes the code could use a couple of refactoring session but you get a hold on what is needing to be done.&lt;/p&gt;
&lt;p&gt;To explain it a little we first get the correct SiteDefinition (for the site that the content belongs to) and then we look for the hostname with the primary flag on it or we take the first one if there arent any primary. Then we get the baseUri (Site url) from the current SiteDefintion to be used if there is not any hostnames or there&amp;rsquo;s only the star. And in the end we try to create a uri with the information for the primary hostname (and return the relative url if something goes wrong) and return the absolut url for the SiteUrl together with the path that was sent in to the function.&lt;/p&gt;
&lt;p&gt;I hope you find this useable and I have sent in a feature request to Episerver to move the settings for the License to a specific field in admin since I even with code that support hostname like to have the correct Url in Site Settings.&lt;/p&gt;</id><updated>2019-03-08T18:05:40.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Fixing error The block or page &#39;[ContentTypeName]&#39; has not been synchronized yet</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2019/2/fixing-error-the-block-or-page-contenttype-has-not-been-synchronized-yet/" /><id>&lt;p&gt;Sometimes the database schema synchronization for some reason does not work and you end up with an error message like this:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;Initialize action failed for &#39;Initialize on class EPiServer.Initialization.Internal.ModelSyncInitialization, EPiServer, Version=11.11.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7&#39;
System.InvalidOperationException: The block &#39;[Namespace].[ContentTypeName]&#39; has not been synchronized yet
   at EPiServer.DataAbstraction.RuntimeModel.Internal.PropertyDefinitionSynchronizer.LoadBlockDefinitionType(PropertyDefinitionModel map)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.PropertyDefinitionSynchronizer.ResolveType(PropertyDefinitionModel model)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.PropertyDefinitionSynchronizer.CreatePropertyDefinition(PropertyDefinitionModel model, Int32 contentTypeID)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelRegister.CommitProperties(Boolean deleteUnusedTypes)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelScanner.Sync(Boolean commitChanges)
   at EPiServer.Initialization.Internal.ModelSyncInitialization.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.&amp;lt;&amp;gt;c__DisplayClass2_0.&amp;lt;Initialize&amp;gt;b__0()
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Execute(Action a, String key)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you do an update the error will still be there and the only way to solve it (that I have seen) is to reset the affected content type by setting the property ModelType to null.&lt;/p&gt;
&lt;p&gt;You should only do this for you own content types and not system types and you should always tripple check so you do not miss out on the Where clause.&lt;/p&gt;
&lt;p&gt;You can do it with a script like this:&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;Update tblContentType
Set ModelType = null
Where [Name] = &#39;[ContentTypeName]&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer: You should not be updating information inside the Episerver Database if you do not know what you are doing. I give you this tip because it worked for me and if you do it, be sure to take a backup of the database first and run it with care!&lt;/strong&gt;&lt;/p&gt;</id><updated>2019-02-07T13:01:36.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Fixing error on loading critera for visitor group after changing namespace</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2019/1/fixing-error-on-loading-critera-for-visitor-group-after-changing-namespace/" /><id>&lt;p&gt;Sometimes you need to move code and with that change the namespace for a class.&lt;/p&gt;
&lt;p&gt;If you do it for a content type there should be no problem at all since Episerver will update it as long as you have specified a GUID for the content type, and that is something we always do, right.... ;-)&lt;/p&gt;
&lt;p&gt;For some other part of the code it is a little bit harder since it will not be fixed by Episerver and one of these things is if you have created your own Visitor Group&amp;nbsp;Criterion. The reason for that is that Episerver has saved information about the namespace in the database inside the BigTable.&lt;/p&gt;
&lt;p&gt;There is a way to fix this and that Episerver have a SQL View that it use to talk to the database about Visitor Group Criterion that is named: &quot;VisitorGroupCriterion&quot; and we can use this ourself to fix the namespace.&lt;/p&gt;
&lt;p&gt;So to fix this you need to write an SQL-statement that looks like this (You need to change the part OLDNAMESPACE, NEWNAMESPACE, OLDASSEMBLYNAME and NEWASSEMBLYNAME to your own info).&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Update VW_VisitorGroupCriterion
Set TypeName = &#39;NEWNAMESPACE.CustomCriterion, NEWASSEMBLYNAME&#39;
Where TypeName = &#39;OLDNAMESPACE.CustomCriterion, OLDASSEMBLYNAME&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you have done this you need to recycle the application pool or in any other way make Episerver release all it&amp;acute;s cache and will need to update from the database again.&lt;/p&gt;
&lt;p&gt;If you get an error saying that the database can not find VW_VisitorGroupCriterion it means that the criterion has not been used so it should not be any problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer: You should not be updating information inside the Episerver Database if you do not know what you are doing. I give you this tip because it worked for me and if you do it, be sure to take a backup of the database first and run it with care!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;</id><updated>2019-01-28T09:44:26.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>SQL Script to fix problem with content tree not loading or set access rights in admin that is not working</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2018/5/sql-script-to-fix-problem-with-content-tree-not-loading-or-set-access-rights-in-admin-that-is-not-working/" /><id>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;&lt;strong&gt;Disclamber: You should propably never do like this unless it is completly nessesary since you are working directly agains the database of Epi and that can be dangerous so IF you do this, I will take no responsibilty in the script and you do it on your own risk and you should ALWAYS take a backup of the database first!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a post that is the result of problems with upgrading Lionbridge to the latest version that is descibed here:&amp;nbsp;&lt;a href=&quot;/link/10f44ac4fc3544f381ef76be90aa17c1.aspx&quot;&gt;https://world.episerver.com/blogs/Henrik-Fransas/Dates/2018/5/sql-script-to-fix-problem-with-lionbridge-after-upgrade-to-the-version-for-episerver-11/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This script solved it for one of our sites that had problem but for a couple of others it still didn&#39;t work and after a lot of trial and error we found out that we had content in the database that existed in tblContent but not in tblContentLanguage and that produced this error in the view for Setting access rights:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[KeyNotFoundException: The given key was not present in the dictionary.]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This error is since when that page is trying to build up the tree it first gets all the contents and then when looping through those it tries to get the language version of the content and that does not exist so this&amp;nbsp;function in Episervers code (Episerver.dll) produce this error:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;private void ReadCommonMetadata(DbDataReader reader, Dictionary&amp;lt;int, Dictionary&amp;lt;int, IContent&amp;gt;&amp;gt; allFetchedItems)
        {
            while (reader.Read())
            {
                foreach (KeyValuePair&amp;lt;int, IContent&amp;gt; item in allFetchedItems[Convert.ToInt32(reader[&quot;PageLinkID&quot;])])
                {
                    IContent value = item.Value;
                    base.LoadIdentity(value, reader, false);
                    base.AssignMasterLanguage(value as ILocalizable, reader);
                    base.AssignResourceable(value as IResourceable, reader);
                    base.AssignContentResource(value as IContentAsset, reader);
                    base.AssignDeleteInfo(value, reader);
                    PageData pageDatum = value as PageData;
                    if (pageDatum != null)
                    {
                        base.LoadPageMetaData(pageDatum.Property, reader);
                    }
                    IContentSecurable contentSecurable = value as IContentSecurable;
                    if (contentSecurable == null)
                    {
                        continue;
                    }
                    this.LoadACLReferences(contentSecurable.GetContentSecurityDescriptor(), reader);
                }
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where this Convert.ToInt32(reader[&quot;PageLinkID&quot;]) is&amp;nbsp;returning 0 and that key does not exists so the page crash.&lt;/p&gt;
&lt;p&gt;After reading this post that was not exact the same problem but simular error (&lt;a href=&quot;/Modules/Forum/Pages/Thread.aspx?id=149428&quot;&gt;https://world.episerver.com/Modules/Forum/Pages/Thread.aspx?id=149428&lt;/a&gt;) we looked for content that was not saved or deleted correctly and to find those content we run this SQL Script:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Select c.*
From tblContent c
	left outer join tblContentLanguage cl on c.pkID = cl.fkContentID
Where cl.fkContentID is null&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that showed that we had content that did not have a row in the table tblContentLanguage and that is making Episerver GUI to not work since it is expecting all content to have a row there and also in the table tblWorkContent.&lt;/p&gt;
&lt;p&gt;To fix this, we add rows to tblContentLanguage and tblWorkContent by using this SQL script&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;DECLARE @contentId int
DECLARE @workContentId int
Declare @VisibleInMenu int
Declare @Name varchar(100)
Declare @LanguageBranchID int

DECLARE MY_CURSOR CURSOR 
  LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR 
select c.pkID, c.VisibleInMenu, c.fkMasterLanguageBranchID
From tblContent c
	left outer join tblContentLanguage cl on c.pkID = cl.fkContentID
Where cl.fkContentID is null

OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @contentId, @VisibleInMenu, @LanguageBranchID
WHILE @@FETCH_STATUS = 0
BEGIN 
	
	set @Name = &#39;AutocreateForContent&#39; + CAST(@contentId as varchar(10))

    Insert Into [dbo].[tblWorkContent]
	([fkContentID], [fkMasterVersionID], [ContentLinkGUID], [fkFrameID], [ArchiveContentGUID], [ChangedByName], [NewStatusByName], [Name], [URLSegment], [LinkURL], [BlobUri], [ThumbnailUri], [ExternalURL], [VisibleInMenu], [LinkType], [Created], [Saved], [StartPublish], [StopPublish], [ChildOrderRule], [PeerOrder], [ChangedOnPublish], [RejectComment], [fkLanguageBranchID], [CommonDraft], [Status], [DelayPublishUntil])
	Values
	(@contentId, NUll, NUll, NULL, NULL, &#39;&#39;, NULL, @Name, @Name, NULL, NULL, NULL, NULL, @VisibleInMenu, 0, GETUTCDATE(), GETUTCDATE(), GETUTCDATE(), NULL, 1, 100, 0, NULL, @LanguageBranchID, 0, 4, NULL )

	SELECT @workContentId = CAST(SCOPE_IDENTITY() as int)

	Insert Into tblContentLanguage
	([fkContentID], [fkLanguageBranchID], [ContentLinkGUID], [fkFrameID], [CreatorName], [ChangedByName], [ContentGUID], [Name], [URLSegment], [LinkURL], [BlobUri], [ThumbnailUri], [ExternalURL], [AutomaticLink], [FetchData], [Created], [Changed], [Saved], [StartPublish], [StopPublish], [Version], [Status], [DelayPublishUntil])
	Select c.pkID, @LanguageBranchID, null, null, &#39;&#39;, &#39;&#39;, c.ContentGUID, @Name, null, null, null, null, null, 1, 0, GETUTCDATE(), GETUTCDATE(), GETUTCDATE(), GETUTCDATE(), null, @workContentId, 4, null
	From tblContent c
		left outer join tblContentLanguage cl on c.pkID = cl.fkContentID
	Where cl.fkContentID is null

    FETCH NEXT FROM MY_CURSOR INTO @contentId, @VisibleInMenu, @LanguageBranchID
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR

&lt;/code&gt;&lt;/pre&gt;

&lt;/body&gt;
&lt;/html&gt;</id><updated>2018-05-30T08:41:25.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>SQL Script to fix problem with Lionbridge after upgrade to the version for Episerver 11</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2018/5/sql-script-to-fix-problem-with-lionbridge-after-upgrade-to-the-version-for-episerver-11/" /><id>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;&lt;strong&gt;Disclamber: You should propably never do like this unless it is completly nessesary since you are working directly agains the database of Epi and that can be dangerous so IF you do this, I will take no responsibilty in the script and you do it on your own risk and you should ALWAYS take a backup of the database first!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We just upgraded a site that use the Lionbridge connector for translation (&lt;a href=&quot;https://nuget.episerver.com/package/?id=LionbridgeConnector&quot;&gt;https://nuget.episerver.com/package/?id=LionbridgeConnector&lt;/a&gt;) and after that we had problems in for example admin when setting access rights.&lt;/p&gt;
&lt;p&gt;The problem was that Episerver tried to load a dll that did not exist. After some inverstigation we found out that the connector creates three different content types and when we inspected the code we saw that they didn&#39;t defined the GUID for the content type.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Because they did not do that and they had changed the namespace for where these content types were defined Episerver had created three new content types in the database and the old once is still there pointing to the old version of the dll.&lt;/p&gt;
&lt;p&gt;This meens that if the site has done any translation projects all these will point to the old versions of the content types and all new translations will point to the new versions of the content types. This meens that when going into admin and set access right it tries to load content that points to the content with the content types that points to the removed dll and that part of the admin crash.&lt;/p&gt;
&lt;p&gt;We have notified Lionbridge about it and they are working on a new version where they define the GUID and move all content but if you like to fix your site yourself you can run this sql script on the database (and restart the site after) and then you will see all the old translation projects and also be able to set access rights again.&lt;/p&gt;
&lt;p&gt;This script only works between the version for 10 to 11 since it defines the whole path for the content type and also directs a problem where they renamed a property.&lt;br /&gt;It is still problem between the version for 9 to 10 och 9 to 11 but you need to modify this script to get that to work.&lt;br /&gt;&lt;br /&gt;And also, after running this, you need to go into admin and remove the old not working content types (those marked with a !)&lt;/p&gt;
&lt;p&gt;IMPORTANT: Just noticed that they changed the version number for the latest version for 11 (we got a version before it was approved) so run this script to make sure you have correct path:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Select pkID, ContentTypeGUID, Created, ModelType, [Name]
From [dbo].[tblContentType]
Where ModelType like &#39;%lionbridge%&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Declare @TranslationProjectOld int = 0
Declare @TranslationProjectNew int = 0

Declare @TranslationProjectAssetOld int = 0
Declare @TranslationProjectAssetNew int = 0

Declare @WorkspaceOld int = 0
Declare @WorkspaceNew int = 0

Declare @ItemRef int = 0

Select @TranslationProjectOld = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.TranslationProject, LionbridgeConnector.EPiServer100, Version=1.3.12.1000, Culture=neutral, PublicKeyToken=null&#39;

Select @TranslationProjectNew = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.TranslationProject, LionbridgeConnector.EPiServer, Version=1.4.2.1100, Culture=neutral, PublicKeyToken=null&#39;

Select @TranslationProjectAssetOld = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.TranslationProjectAsset, LionbridgeConnector.EPiServer100, Version=1.3.12.1000, Culture=neutral, PublicKeyToken=null&#39;

Select @TranslationProjectAssetNew = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.TranslationProjectAsset, LionbridgeConnector.EPiServer, Version=1.4.2.1100, Culture=neutral, PublicKeyToken=null&#39;

Select @WorkspaceOld = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.Workspace, LionbridgeConnector.EPiServer100, Version=1.3.12.1000, Culture=neutral, PublicKeyToken=null&#39;

Select @WorkspaceNew = pkID
From [dbo].[tblContentType]
Where ModelType = &#39;Lionbridge.Translate.Workspace, LionbridgeConnector.EPiServer, Version=1.4.2.1100, Culture=neutral, PublicKeyToken=null&#39;

Select @ItemRef = pkID
From [tblPropertyDefinition]
Where fkContentTypeID IN (@TranslationProjectNew, @TranslationProjectAssetNew, @WorkspaceNew) and [Name] = &#39;ItemRef&#39;

if(@TranslationProjectOld &amp;lt;&amp;gt; 0 and @TranslationProjectNew &amp;lt;&amp;gt; 0)
Begin
Update cp
	set fkPropertyDefinitionID = (Select newPD.pkID
									From [dbo].[tblPropertyDefinition] pd
										inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @TranslationProjectNew) newPD on pd.[Name] = newPD.[Name]
									Where pd.pkID = cp.fkPropertyDefinitionID)
	From tblContentProperty cp  
		inner Join tblContent c on c.pkID = cp.fkContentID
	Where c.fkContentTypeID = @TranslationProjectOld
End

if(@TranslationProjectAssetOld &amp;lt;&amp;gt; 0 and @TranslationProjectAssetNew &amp;lt;&amp;gt; 0)
Begin
Update cp
	set fkPropertyDefinitionID = IsNull((Select newPD.pkID
											From [dbo].[tblPropertyDefinition] pd
												inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @TranslationProjectAssetNew) newPD on pd.[Name] = newPD.[Name]
											Where pd.pkID = cp.fkPropertyDefinitionID), @ItemRef)
	From tblContentProperty cp  
		inner Join tblContent c on c.pkID = cp.fkContentID
	Where c.fkContentTypeID = @TranslationProjectAssetOld
End

if(@WorkspaceOld &amp;lt;&amp;gt; 0 and @TranslationProjectNew &amp;lt;&amp;gt; 0)
Begin
	Update cp
	set fkPropertyDefinitionID = (Select newPD.pkID
									From [dbo].[tblPropertyDefinition] pd
										inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @WorkspaceNew) newPD on pd.[Name] = newPD.[Name]
									Where pd.pkID = cp.fkPropertyDefinitionID)
	From tblContentProperty cp  
		inner Join tblContent c on c.pkID = cp.fkContentID
	Where c.fkContentTypeID = @WorkspaceOld
End

if(@TranslationProjectOld &amp;lt;&amp;gt; 0 and @WorkspaceNew &amp;lt;&amp;gt; 0)
Begin
Update cp
	set fkPropertyDefinitionID =  (Select newPD.pkID
									From [dbo].[tblPropertyDefinition] pd
										inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @TranslationProjectNew) newPD on pd.[Name] = newPD.[Name]
									Where pd.pkID = cp.fkPropertyDefinitionID)
	From [tblWorkContentProperty] cp  
		inner Join tblWorkContent wc on cp.fkWorkContentID = wc.pkID
		inner Join tblContent c on c.pkID = wc.fkContentID
	Where c.fkContentTypeID = @TranslationProjectOld
End

if(@TranslationProjectAssetOld &amp;lt;&amp;gt; 0 and @TranslationProjectAssetNew &amp;lt;&amp;gt; 0)
Begin
Update cp
	set fkPropertyDefinitionID = ISNULL((Select newPD.pkID
											From [dbo].[tblPropertyDefinition] pd
												inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @TranslationProjectAssetNew) newPD on pd.[Name] = newPD.[Name]
											Where pd.pkID = cp.fkPropertyDefinitionID), @ItemRef)
	From [tblWorkContentProperty] cp  
		inner Join tblWorkContent wc on cp.fkWorkContentID = wc.pkID
		inner Join tblContent c on c.pkID = wc.fkContentID
	Where c.fkContentTypeID = @TranslationProjectAssetOld
End

if(@WorkspaceOld &amp;lt;&amp;gt; 0 and @WorkspaceNew &amp;lt;&amp;gt; 0)
Begin
	Update cp
	set fkPropertyDefinitionID =  (Select newPD.pkID
									From [dbo].[tblPropertyDefinition] pd
										inner join (Select pkID, [Name] From [tblPropertyDefinition] Where fkContentTypeID = @WorkspaceNew) newPD on pd.[Name] = newPD.[Name]
									Where pd.pkID = cp.fkPropertyDefinitionID)
	From [tblWorkContentProperty] cp  
		inner Join tblWorkContent wc on cp.fkWorkContentID = wc.pkID
		inner Join tblContent c on c.pkID = wc.fkContentID
	Where c.fkContentTypeID = @WorkspaceOld
End

if(@TranslationProjectOld &amp;lt;&amp;gt; 0 and @TranslationProjectNew &amp;lt;&amp;gt; 0)
Begin
	Update tblContent
	Set fkContentTypeID = @TranslationProjectNew
	Where fkContentTypeID = @TranslationProjectOld
End

if(@TranslationProjectAssetOld &amp;lt;&amp;gt; 0 and @TranslationProjectAssetNew &amp;lt;&amp;gt; 0)
Begin
	Update tblContent
	Set fkContentTypeID = @TranslationProjectAssetNew
	Where fkContentTypeID = @TranslationProjectAssetOld
End

if(@WorkspaceOld &amp;lt;&amp;gt; 0 and @WorkspaceNew &amp;lt;&amp;gt; 0)
Begin
	Update tblContent
	Set fkContentTypeID = @WorkspaceNew
	Where fkContentTypeID = @WorkspaceOld
End


&lt;/code&gt;&lt;/pre&gt;
&lt;/body&gt;
&lt;/html&gt;</id><updated>2018-05-28T12:18:27.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Report From Episerver Ascend</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2018/3/report-from-episerver-ascend/" /><id>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;&lt;span class=&quot;notranslate&quot;&gt;&lt;span&gt;For the fourth consecutive year, Episerver is holding its grand annual conference, Ascend in Las Vegas.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;notranslate&quot;&gt;&lt;span&gt;Episerver is growing more and more in the US, especially after their acquisition of Ektron a few years ago.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;notranslate&quot;&gt;&lt;span&gt;This made this year a record number of visitors who have attended the conference to be inspired and to take advantage of the latest news on the platform.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;notranslate&quot;&gt;&lt;span&gt;If you are curious about the latest news at Episerver front, here&#39;s a summary of the conference&#39;s highlights for developers and editors.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;notranslate&quot;&gt;Read the whole report here:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://translate.google.se/translate?sl=sv&amp;amp;tl=en&amp;amp;js=y&amp;amp;prev=_t&amp;amp;hl=en&amp;amp;ie=UTF-8&amp;amp;u=https%3A%2F%2Fwww.netrelations.com%2Fsv%2Finspiration%2Fblogg%2Frapport-fran-episerver-ascend%2F&amp;amp;edit-text=&quot;&gt;Report&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</id><updated>2018-03-15T19:26:29.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Getting this error when indexing in Episerver Find: &quot;System.Reflection.AmbiguousMatchException: Ambiguous match found.&quot;</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2017/4/getting-this-error-when-indexing-in-episerver-find-system-reflection-ambiguousmatchexception-ambiguous-match-found/" /><id>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;After spending some time trying to figure out why I get a lot of these errors when Find are indexing my site I feel I need to share the info on why it happened.&lt;/p&gt;
&lt;p&gt;The error are can come if you for example have a override property in a base class but for me I did not have that so I could not figure out why this happened.&lt;/p&gt;
&lt;p&gt;I managed to figure out what pages that was reporting the error and after a lot of commeting out properties I ended up with identifying the problem and that was this line of code:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public virtual string Item { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I change the name to Object and all started to work so Item seem to be a name that Episerver are using somewhere but unlike for example PageName that will generate a warning in code, it works for everything BUT indexing in Episerver Find.....&lt;/p&gt;
&lt;p&gt;This was when I added the property to a local block I got that error, when I reproduced it on Alloy and added it directly on the PageType I got this error instead:&lt;/p&gt;
&lt;p&gt;For this line:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var page = contentLoader.Get&amp;lt;PageData&amp;gt;(contentLink);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I got this error:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[InvalidCastException: Specified cast is not valid.]
   EPiServer.Core.PageData.get_Status() +60
   EPiServer.DataAccess.Internal.ContentDB.AssignVersionStatus(IVersionable versionStatus, DbDataReader reader) +303
   EPiServer.DataAccess.Internal.ContentLoadDB.LoadContentInternal(ContentReference contentLink, Int32 languageBranchId, DbDataReader reader) +588
   EPiServer.DataAccess.Internal.&amp;lt;&amp;gt;c__DisplayClass2_0.&amp;lt;Load&amp;gt;b__0() +208
   EPiServer.Data.Providers.Internal.&amp;lt;&amp;gt;c__DisplayClass28_0`1.&amp;lt;Execute&amp;gt;b__0() +59
   EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute(Func`1 method) +51
   EPiServer.Data.Providers.Internal.SqlDatabaseExecutor.Execute(Func`1 action) +203
   EPiServer.DataAccess.Internal.ContentLoadDB.Load(ContentReference contentLink, Int32 languageBranchID) +109
   EPiServer.Core.Internal.DefaultContentProviderDatabase.Load(ContentReference contentLink, Int32 languageBranchID) +31
   EPiServer.Core.Internal.DefaultContentProvider.LoadContent(ContentReference contentLink, ILanguageSelector languageSelector) +88
   EPiServer.Core.&amp;lt;&amp;gt;c__DisplayClass115_0.&amp;lt;LoadContentFromCacheOrRepository&amp;gt;b__0() +85
   EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThroughWithWait(IObjectInstanceCache cache, String cacheKey, Func`1 readValue, Func`2 evictionPolicy) +710
   EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThrough(IObjectInstanceCache cache, String key, Func`1 readValue, Func`2 evictionPolicy, ReadStrategy readStrategy) +87
   EPiServer.Core.ContentProvider.LoadContentFromCacheOrRepository(ContentReference contentreference, ILanguageSelector selector) +729
   EPiServer.Core.Internal.ProviderPipelineImplementation.GetItem(ContentProvider provider, ContentReference contentLink, LoaderOptions loaderOptions) +247
   EPiServer.Core.Internal.DefaultContentLoader.TryGet(ContentReference contentLink, LoaderOptions loaderOptions, T&amp;amp; content) +259
   EPiServer.Core.Internal.DefaultContentLoader.Get(ContentReference contentLink, LoaderOptions loaderOptions) +73
   EPiServer.Core.Internal.DefaultContentLoader.Get(ContentReference contentLink) +68
   FindIndexBug.Helpers.UrlHelpers.PageLinkUrl(UrlHelper urlHelper, ContentReference contentLink) in C:\Temp\FindIndexBug\FindIndexBug\Helpers\UrlHelpers.cs:27
   ASP._Page_Views_Shared_Blocks_TeaserBlock_cshtml.Execute() in c:\Temp\FindIndexBug\FindIndexBug\Views\Shared\Blocks\TeaserBlock.cshtml:6
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +105
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +90
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +256
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +107
   EPiServer.Web.Mvc.ViewExtensions.Render(IView view, ViewEngineResult viewEngineResult, ViewContext context, TextWriter writer, T data) +454
   EPiServer.Web.Mvc.Internal.DefaultMvcContentRenderer.HandleRenderTemplateWithViewEngine(HtmlHelper helper, IContentData contentData, TemplateModel templateModel) +392
   EPiServer.Web.Mvc.Internal.DefaultMvcContentRenderer.Render(HtmlHelper helper, PartialRequest partialRequestHandler, IContentData contentData, TemplateModel templateModel) +88
   EPiServer.Web.Mvc.MvcContentRenderer.Render(HtmlHelper helper, PartialRequest partialRequestHandler, IContentData contentData, TemplateModel templateModel) +39
   FindIndexBug.Business.Rendering.ErrorHandlingContentRenderer.Render(HtmlHelper helper, PartialRequest partialRequestHandler, IContentData contentData, TemplateModel templateModel) in C:\Temp\FindIndexBug\FindIndexBug\Business\Rendering\ErrorHandlingContentRenderer.cs:37
   EPiServer.Web.Mvc.Html.IContentDataExtensions.RenderContentData(HtmlHelper html, IContentData contentData, Boolean isContentInContentArea, TemplateModel templateModel, IContentRenderer contentRenderer) +469
   EPiServer.Web.Mvc.Html.ContentAreaRenderer.RenderContentAreaItem(HtmlHelper htmlHelper, ContentAreaItem contentAreaItem, String templateTag, String htmlTag, String cssClass) +934
   EPiServer.Web.Mvc.Html.ContentAreaRenderer.RenderContentAreaItems(HtmlHelper htmlHelper, IEnumerable`1 contentAreaItems) +119
   EPiServer.Web.Mvc.Html.ContentAreaRenderer.Render(HtmlHelper htmlHelper, ContentArea contentArea) +265
   EPiServer.Web.Mvc.Html.ContentAreaExtensions.RenderContentArea(HtmlHelper htmlHelper, ContentArea contentArea) +50
   ASP.util_views_shared_displaytemplates_contentarea_ascx.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in C:\Temp\FindIndexBug\FindIndexBug\Util\Views\Shared\DisplayTemplates\ContentArea.ascx:4
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +103
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +9
   System.Web.UI.Control.Render(HtmlTextWriter writer) +10
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +129
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +287
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +27
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +197
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +9
   System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +53
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +129
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +287
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +27
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5640&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I changed it and instead added the property into a local block (that I had in the real solution) and then the site loaded but I get the Ambiguous match when indexing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So, do not create properties with the name Item if you like to use Episerver!&lt;/strong&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</id><updated>2017-04-11T13:22:25.3630000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Extending the Episerver editor interface</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/11/extending-the-episerver-editor-interface/" /><id>&lt;p&gt;This blog post will summarize the sessions I held at Episerver Ascend Nordic and Episerver Ascend Europe and you will also have to opportunity to get hold of the code yourself to try it out.&lt;/p&gt;
&lt;p&gt;I am publishing the code on GitHub and I would love for you all great developers out there to do pull request if you see something that I have done that can be done better or if you have another great way to extend the editor that you like to share.&lt;/p&gt;
&lt;h2&gt;Scenarios&lt;/h2&gt;
&lt;p&gt;The code contains now three different scenarios and will be updated with more.&lt;/p&gt;
&lt;h3&gt;Scenario one: Custom report with excel export&lt;/h3&gt;
&lt;p&gt;I have described that in another blog post that you can find here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/9a00d853183e45788c07d78deff3a68a.aspx&quot;&gt;http://world.episerver.com/blogs/Henrik-Fransas/Dates/2016/3/create-custom-report-in-mvc-with-excel-export/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I will therefore not go into that but you can find the code for it inside this solution.&lt;/p&gt;
&lt;h3&gt;Scenario two: Custom visitor group for browser&lt;/h3&gt;
&lt;p&gt;I have described that in another blog post and it is still working in version 10. You can find it here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/cfb21e1a4f7948d19ca8389a00bd7b93.aspx&quot;&gt;http://world.episerver.com/blogs/Henrik-Fransas/Dates/2015/2/building-custom-criteria-for-visitor-groups-in-episerver-7.5/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I will therefore not go into that but you can find the code for it inside this solution.&lt;/p&gt;
&lt;h3&gt;Scenario three: Implement custom admin part to editor&lt;/h3&gt;
&lt;p&gt;Sometimes you have a solution that has content that are not Episerver content, or you need some extra admin view for more complex things that are hard to do as Episerver properties. For those times, you can use the technique that I show in this post and I will show here a simple example that are good to get inspired from.&lt;/p&gt;
&lt;p&gt;In this example, I have added a new table to the Episerver database that are called UserProfile where I save first name, last name and phone number of the users in the database (the one created by the sql membership provider). This is a pretty simple example but is a good base that you can take inspiration from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the table&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, I create a table inside the existing database and to do that use this code:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[UserProfile](
	[UserId] [uniqueidentifier] NOT NULL,
	[FirstName] [nvarchar](200) NULL,
	[LastName] [nvarchar](200) NULL,
	[PhoneNumber] [varchar](50) NULL,
 CONSTRAINT [PK_UserProfile] PRIMARY KEY CLUSTERED 
(
	[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Create the menu&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even though we want to use MVC with bootstrap and angularjs we want to use and keep the menu so the first thing we need to do is to create a class that implements the IMenuProvider and are decorated with the MenuProvider attribute. It looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using EPiServer.Security;
using EPiServer.Shell.Navigation;

namespace ExtendingEditUi.Business.Providers
{
    [MenuProvider]
    public class ProfileMenuProvider : IMenuProvider
    {
        public IEnumerable&amp;lt;MenuItem&amp;gt; GetMenuItems()
        {
            var toolbox = new SectionMenuItem(&quot;Profile-Admin&quot;, &quot;/global/profileadmin&quot;)
            {
                IsAvailable = (request) =&amp;gt; PrincipalInfo.HasEditorAccess
            };

            var profies = new UrlMenuItem(&quot;User profiles&quot;, &quot;/global/profileadmin/profiles&quot;, &quot;/profileadmin/profiles&quot;)
            {
                IsAvailable = (request) =&amp;gt; PrincipalInfo.HasEditorAccess
            };

            return new MenuItem[] { toolbox, profies };
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this file, we start by creating a SectionMenuItem and that is the one that will be shown in the main menu part (next to CMS, Find and so on). It is important to set the parameter IsAvailable on that to the proper level so you do not expose the menu to people that should not be able to see it. In this example, I make it visible to everyone that has edit access&lt;/p&gt;
&lt;p&gt;Then we create a UrlMenuItem that are the submenu and for this example we are only creating one. We set the access-level on this one as well and as you see it is possible to have different level for different sub menu items. This constructor also has three parameters and the last one is the actual url that will be used when a user click on that menu item.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implement routing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you are using Episerver then epi will take over all the routing for that website and by default it will not be possible to be routed to anything that are not created content. Since our view for this will be regular mvc we need to implement our own routing. You can read more about how Episerver are implementing routing here: &lt;a href=&quot;/link/06be7a206b204c16b9d6a649fecaf22f.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Routing/Routing/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One very important thing to remember here is that by doing our own routing we are skipping some of the build in security in Episerver so it is very important that we think of this and implement the built-in security in Episerver our self.&lt;/p&gt;
&lt;p&gt;Since we are also going to use Web Api inside this solution we also need to set up routing for that.&lt;/p&gt;
&lt;p&gt;You can do this in an initialization module or in global.asax and in this code, I&amp;nbsp;do it in global.asax.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;        protected override void RegisterRoutes(RouteCollection routes)
        {
            base.RegisterRoutes(routes);

            routes.MapHttpRoute(&quot;webapi&quot;, &quot;api/{controller}/{action}/{id}&quot;, new { id = RouteParameter.Optional });

            routes.MapRoute(&quot;ProfileAdmin&quot;, &quot;profileadmin/{action}&quot;, new { controller = &quot;ProfileAdmin&quot;, action = &quot;index&quot; });
            routes.MapRoute(&quot;ExistingPagesReport&quot;, &quot;existingpagesreport/{action}&quot;, new { controller = &quot;ExistingPagesReport&quot;, action = &quot;Index&quot; });
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start by implementing the incoming routes and then we add a MapHttpRoute to be able to handle all web api calls. We name it to webapi and make it handle all calls to /api/*&lt;/p&gt;
&lt;p&gt;Then we add a MapRoute to handle the calls to the new custom view we are creating and we set it to handle all request to profileadmin/* and set the default controller to ProfileAdmin controller we are going to create.&lt;/p&gt;
&lt;p&gt;The last route is to handle request to our custom report that are described in scenario one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the controller&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since we are using MVC we need a controller to handle the request to the custom view so we create an ordinary MVC controller.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System.Web.Mvc;

namespace ExtendingEditUi.Controllers
{
    [Authorize(Roles = &quot;Administrators, WebAdmins, WebEditors&quot;)]
    public class ProfileAdminController : Controller
    {
        // GET: ProfileAdmin
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Profiles()
        {
            return View();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the controller there are two actions and if you compare it to the Episerver controller there are no currentPage as parameter and we cannot have that since there are no currentPage for this. The standard Index action are not used in this example but if you have a bigger implementation you might use this as an index view to list all the other views available.&lt;/p&gt;
&lt;p&gt;I am decorating the class with the Authorize attribute and this is very important to do since if you do not do that, it will be public available for everybody and you probably do not want that! In this example, I make it available for administrators and editors.&lt;/p&gt;
&lt;p&gt;For this simple example, I am not using any logic in the controller at all but you can do it and if you need to pass some data to the view then create a viewmodel for that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the view&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To make it easier to separate the logic I have created a layout for this page even that it is only one page. You will probably have more than one page so it is a good practice to have a layout for your views.&lt;/p&gt;
&lt;p&gt;The layout:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@using EPiServer.Framework.Web.Resources
@using EPiServer.Shell.Navigation

@{
    Layout = null;
}

&amp;lt;!DOCTYPE html&amp;gt;

&amp;lt;html ng-app=&quot;ProfilesApp&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;

    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=Edge&quot; /&amp;gt;

    &amp;lt;!-- Shell --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;ShellCore&quot;))
    @Html.Raw(ClientResources.RenderResources(&quot;ShellWidgets&quot;))

    &amp;lt;!-- LightTheme --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;ShellCoreLightTheme&quot;))

    &amp;lt;!-- Navigation --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;Navigation&quot;))

    &amp;lt;!-- Dojo Dashboard --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;DojoDashboardCompatibility&quot;, new[] { ClientResourceType.Style }))

    &amp;lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;https://code.jquery.com/jquery-3.1.1.min.js&quot; integrity=&quot;sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js&quot; integrity=&quot;sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;link href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u&quot; crossorigin=&quot;anonymous&quot;&amp;gt;
    &amp;lt;script src=&quot;~/Static/js/ProfileAdmin/profiles.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;link href=&quot;~/Static/css/ProfileAdmin/profiles.css&quot; rel=&quot;stylesheet&quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    @Html.Raw(Html.ShellInitializationScript())
    @Html.Raw(Html.GlobalMenu())
    &amp;lt;div class=&quot;container-fluid&quot; ng-controller=&quot;profilesCtrl&quot;&amp;gt;
        @RenderBody()
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here there are some magic, or more hacky coding since the only way I figured out how to be able to implement the menu, styles and css were to look in the master pages there are in the cms.zip file below modules/_protected folder. This is not good since I have no control of knowing what will happen if Episerver decides to change or delete some files I use here. I am still feeling so comfortable with this solution that I use it in real solutions I have in production since the code are coming from the admin-part and those parts has not been changed in forever&amp;hellip; But I will try to figure out if there are a better way and if there are, I will update this code.&lt;/p&gt;
&lt;p&gt;There are a couple of key things in this file and the first is the implementation of the Episerver parts and that are pretty much all the RenderResources and the Html.Raw you see in the file. I need the RenderResources an initialization script to be able to render the menu and other parts correctly.&lt;/p&gt;
&lt;p&gt;After that I am using CDN&amp;rsquo;s for the bootstrap and angularjs parts and it is up to you what you want to do, I like using CDN, but there is a risk of the CDN being down so if you want to be 100% of always have access to the files then you should download them.&lt;/p&gt;
&lt;p&gt;I am using AngularJs in this solution and if you have never used AngularJs please read this and you will understand what I am doing:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.w3schools.com/angular/&quot;&gt;http://www.w3schools.com/angular/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I define my angular app in the html head tag and then my angular controller in the wrapping div of the content and this works fine in this simple example but if you have a lot of different controllers and so on you so look more into where to define them.&lt;/p&gt;
&lt;p&gt;The view:&lt;/p&gt;
&lt;p&gt;In this view, I am going to present a search field and after the user has done a search I will show a list of profiles and also make it possible to edit them inside the list.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@{
    Layout = &quot;Layout/ProfilesLayout.cshtml&quot;;
}

&amp;lt;div class=&quot;row&quot;&amp;gt;
    &amp;lt;div class=&quot;col-sm-12 col-md-12 main&quot;&amp;gt;
        &amp;lt;div id=&quot;ListView&quot;&amp;gt;
            &amp;lt;h1&amp;gt;Administrate user profiles&amp;lt;/h1&amp;gt;
            &amp;lt;div class=&quot;row no-margin main-form&quot;&amp;gt;
                &amp;lt;form name=&quot;searchProfiles&quot; id=&quot;searchProfiles&quot;&amp;gt;
                    &amp;lt;table class=&quot;table table-striped table-bordered&quot;&amp;gt;
                        &amp;lt;thead&amp;gt;
                            &amp;lt;tr&amp;gt;
                                &amp;lt;th&amp;gt;
                                    &amp;lt;label for=&quot;searchString&quot;&amp;gt;Name or Username&amp;lt;/label&amp;gt;
                                &amp;lt;/th&amp;gt;
                            &amp;lt;/tr&amp;gt;
                        &amp;lt;/thead&amp;gt;
                        &amp;lt;tbody&amp;gt;
                            &amp;lt;tr&amp;gt;
                                &amp;lt;td&amp;gt;
                                    &amp;lt;input type=&quot;text&quot; ng-model=&quot;searchString&quot; id=&quot;searchString&quot; class=&quot;form-control&quot;&amp;gt;
                                &amp;lt;/td&amp;gt;
                            &amp;lt;/tr&amp;gt;
                        &amp;lt;/tbody&amp;gt;
                        &amp;lt;tfoot&amp;gt;
                            &amp;lt;tr&amp;gt;
                                &amp;lt;td&amp;gt;&amp;lt;input type=&quot;button&quot; class=&quot;btn btn-default btn-md&quot; value=&quot;Search&quot; ng-click=&quot;search()&quot; /&amp;gt;&amp;lt;/td&amp;gt;
                            &amp;lt;/tr&amp;gt;
                        &amp;lt;/tfoot&amp;gt;
                    &amp;lt;/table&amp;gt;
                &amp;lt;/form&amp;gt;
                &amp;lt;table class=&quot;table table-striped table-bordered&quot;&amp;gt;
                    &amp;lt;thead&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;th class=&quot;col-md-1&quot;&amp;gt;&amp;lt;/th&amp;gt;
                            &amp;lt;th class=&quot;col-md-2&quot;&amp;gt;UserName&amp;lt;/th&amp;gt;
                            &amp;lt;th class=&quot;col-md-2&quot;&amp;gt;Email&amp;lt;/th&amp;gt;
                            &amp;lt;th class=&quot;col-md-2&quot;&amp;gt;FirstName&amp;lt;/th&amp;gt;
                            &amp;lt;th class=&quot;col-md-2&quot;&amp;gt;LastName&amp;lt;/th&amp;gt;
                            &amp;lt;th class=&quot;col-md-3&quot;&amp;gt;Phone&amp;lt;/th&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    &amp;lt;/thead&amp;gt;
                    &amp;lt;tbody&amp;gt;
                        &amp;lt;tr class=&quot;pointer&quot; ng-repeat-start=&quot;userProfile in userProfiles&quot;&amp;gt;
                            &amp;lt;td class=&quot;col-md-1&quot;&amp;gt;&amp;lt;button type=&quot;button&quot; class=&quot;btn btn-default&quot; ng-click=&quot;EditUserProfile(userProfile)&quot;&amp;gt;&amp;lt;span class=&quot;glyphicon glyphicon-pencil&quot; aria-hidden=&quot;true&quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;&amp;lt;/td&amp;gt;
                            &amp;lt;td class=&quot;col-md-2&quot;&amp;gt;{{userProfile.UserName}}&amp;lt;/td&amp;gt;
                            &amp;lt;td class=&quot;col-md-2&quot;&amp;gt;{{userProfile.Email}}&amp;lt;/td&amp;gt;
                            &amp;lt;td class=&quot;col-md-2&quot;&amp;gt;{{userProfile.FirstName}}&amp;lt;/td&amp;gt;
                            &amp;lt;td class=&quot;col-md-2&quot;&amp;gt;{{userProfile.LastName}}&amp;lt;/td&amp;gt;
                            &amp;lt;td class=&quot;col-md-2&quot;&amp;gt;{{userProfile.PhoneNumber}}&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                        &amp;lt;tr ng-show=&quot;currentObject != null &amp;amp;&amp;amp; editObject != null &amp;amp;&amp;amp; currentObject.UserId == userProfile.UserId&quot; ng-repeat-end=&quot;&quot;&amp;gt;
                            &amp;lt;td colspan=&quot;6&quot;&amp;gt;
                                &amp;lt;div class=&quot;panel panel-default&quot;&amp;gt;
                                    &amp;lt;div class=&quot;panel-body&quot;&amp;gt;
                                        &amp;lt;div class=&quot;main-form&quot;&amp;gt;
                                            &amp;lt;form name=&quot;editProfileForm&quot; id=&quot;editProfileForm&quot; role=&quot;form&quot; ng-submit=&quot;submitEditUserProfile()&quot;&amp;gt;
                                                &amp;lt;div class=&quot;form-group&quot;&amp;gt;
                                                    &amp;lt;label&amp;gt;FirstName&amp;lt;/label&amp;gt;
                                                    &amp;lt;input class=&quot;form-control&quot; type=&quot;text&quot; name=&quot;FirstName&quot; id=&quot;FirstName&quot; ng-model=&quot;currentObject.FirstName&quot;&amp;gt;
                                                &amp;lt;/div&amp;gt;
                                                &amp;lt;div class=&quot;form-group&quot;&amp;gt;
                                                    &amp;lt;label&amp;gt;LastName&amp;lt;/label&amp;gt;
                                                    &amp;lt;input class=&quot;form-control&quot; type=&quot;text&quot; name=&quot;LastName&quot; id=&quot;LastName&quot; ng-model=&quot;currentObject.LastName&quot;&amp;gt;
                                                &amp;lt;/div&amp;gt;
                                                &amp;lt;div class=&quot;form-group&quot;&amp;gt;
                                                    &amp;lt;label&amp;gt;Phone&amp;lt;/label&amp;gt;
                                                    &amp;lt;input class=&quot;form-control&quot; type=&quot;text&quot; name=&quot;PhoneNumber&quot; id=&quot;PhoneNumber&quot; ng-model=&quot;currentObject.PhoneNumber&quot;&amp;gt;
                                                &amp;lt;/div&amp;gt;
                                                &amp;lt;button type=&quot;submit&quot; class=&quot;btn btn-default&quot; ng-disabled=&quot;editProfileForm.$invalid&quot;&amp;gt;Spara&amp;lt;/button&amp;gt;
                                                &amp;lt;button type=&quot;button&quot; class=&quot;btn btn-default&quot; ng-click=&quot;CancelEditUserProfile()&quot;&amp;gt;Avbryt&amp;lt;/button&amp;gt;
                                            &amp;lt;/form&amp;gt;
                                        &amp;lt;/div&amp;gt;
                                    &amp;lt;/div&amp;gt;
                                &amp;lt;/div&amp;gt;
                            &amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    &amp;lt;/tbody&amp;gt;
                &amp;lt;/table&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code should be self-explaining and for the angular parts I urge you to look at the angular tutorial I linked earlier in this blog.&lt;/p&gt;
&lt;p&gt;One little trick on this is that I start the angular repeat with ng-repeat-start instead of just ng-repeat and then I add another tr where I add ng-repeat-end. This will add an additional hidden row after each profile row that I fill up and show when a user clicks edit this makes it look like the row are expanding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create the js-file with logic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To be able to connect the front with some logic I need to create a javascript file where I declare the angular app, controller and logic.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;var profilesApp = angular.module(&#39;ProfilesApp&#39;, []);

profilesApp.controller(&#39;profilesCtrl&#39;, function ($scope, $http) {

    $scope.userProfiles = null;
    $scope.currentObject = null;
    $scope.editObject = false;
    $scope.totalUserProfiles = 0;

    $scope.search = function () {
        var query = &quot;&quot;;
        
        if ($scope.searchString) {
            query = $scope.searchString;
        }
        
        $scope.userProfiles = null;
        $http.get(&#39;/api/ProfileApi/SearchUserProfiles/?searchString=&#39; + query)
			.then(function (result) {
			    $scope.totalUserProfiles = result.data.Count;
			    $scope.userProfiles = result.data;
			});
    };

    $scope.EditUserProfile = function (userProfile) {
        if ($scope.editObject === true) {
            $scope.currentObject = null;
            $scope.editObject = false;
        } else {
            $http.get(&#39;/api/ProfileApi/GetUserProfile?userId=&#39; + userProfile.UserId)
                .success(function (data) {
                    $scope.currentObject = data;
                    $scope.editObject = true;
                });
        }
    };

    $scope.CancelEditUserProfile = function () {
        $scope.currentObject = null;
        $scope.editObject = false;
    };

    $scope.submitEditUserProfile = function () {
        if ($scope.currentObject != null &amp;amp;&amp;amp; $scope.currentObject.UserId != null) {

            $http.post(&#39;/api/ProfileApi/UpsertUserProfile&#39;, $scope.currentObject)
                .success(function (data) {
                    $scope.currentObject = null;
                    $scope.editObject = false;
                    $scope.search($scope.searchString);
                })
                .error(function () {
                    alert(&quot;Error on update profile&quot;);
                });
        }
    };
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this file, I have four functions, search, edit, cancelEdit and submitEdit and in all of them I am using the data binding from the view through the scope variable.&lt;/p&gt;
&lt;p&gt;To be able to communicate with the database I am doing ajax request to a web api and then updating the scope variable with the returned data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Creating the Web Api&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I am using web api 2 to create my REST api functions and that helps me a lot, since so much are built into the code from Microsoft.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.Web.Http;
using ExtendingEditUi.Business.Repositories;
using ExtendingEditUi.Models.Entities;

namespace ExtendingEditUi.Controllers.Api
{
    [Authorize(Roles = &quot;Administrators, WebAdmins, WebEditors&quot;)]
    public class ProfileApiController : ApiController
    {
        private readonly IUserProfileRepository userProfileRepository;

        public ProfileApiController(IUserProfileRepository userProfileRepository)
        {
            this.userProfileRepository = userProfileRepository;
        }
        
        [AcceptVerbs(&quot;GET&quot;)]
        public IHttpActionResult SearchUserProfiles(string searchString)
        {
            return this.Ok(this.userProfileRepository.SearchUsers(searchString));
        }

        [AcceptVerbs(&quot;GET&quot;)]
        public IHttpActionResult GetUserProfile(string userId)
        {
            return this.Ok(this.userProfileRepository.GetUserProfile(userId));
        }

        [AcceptVerbs(&quot;POST&quot;)]
        public IHttpActionResult UpsertUserProfile(UserProfile userProfile)
        {
            try
            {
                this.userProfileRepository.UpsertUserProfile(userProfile);

            }
            catch (Exception ex)
            {
                return this.InternalServerError(ex);
            }

            return this.Ok(&quot;Update done&quot;);
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see I am using dependency injection inside the controller and to make this work with web api there are a couple of things you need to do and I have write a blog post about that and you can find it here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/87c5a3f478d6447a8e048620fa6a7abd.aspx&quot;&gt;http://world.episerver.com/blogs/Henrik-Fransas/Dates/2016/10/installing-webapi-and-enable-dependency-injection-for-it/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Other than that, it is a simple controller that almost just send through the request to the repository but there is one very important thing and that is to also here add the Authorize attribute because otherwise you will expose it to all the web, and you do not want to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Creating the repository&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To be able to talk to the database there are many ways you can do that. I am not a big fan of big ORM solutions like Entity Framework och nHibernate since I think they often write ugly SQL code and also when not seeing the SQL code it tends to often be non-performance effective solutions. But this is only my personal view, if you want to use for example EF, please do so, the only important thing is that you NEVER write SQL code by concatenating string since that will make your site open for SQL Injections and that you absolutely do not want.&lt;/p&gt;
&lt;p&gt;For this solution, I am using a micro ORM called Dapper (&lt;a href=&quot;https://github.com/StackExchange/dapper-dot-net&quot;&gt;https://github.com/StackExchange/dapper-dot-net&lt;/a&gt;) that gives me back typed object but I still have full control over the SQL and it&amp;rsquo;s also encourage me to use parametrized queries and that will protect me against SQL Injections. I tried to hack myself with this solution with the help of an application called Havij and the solution passed. If you like to know more about how to use that, Troy Hunt has made a great video on SQL Injection with Havij (&lt;a href=&quot;https://www.youtube.com/watch?v=Fp47G4MQFvA&quot;&gt;https://www.youtube.com/watch?v=Fp47G4MQFvA&lt;/a&gt;).&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
using ExtendingEditUi.Models.Entities;

namespace ExtendingEditUi.Business.Repositories
{
    public class UserProfileRepository : IUserProfileRepository
    {
        private readonly IConfigRepository _configRepository;
        private readonly string _constringKey;

        public UserProfileRepository(IConfigRepository configRepository)
        {
            _configRepository = configRepository;
            _constringKey = &quot;EPiServerDB&quot;;
        }

        public IEnumerable&amp;lt;UserProfile&amp;gt; SearchUsers(string searchString)
        {
            const string query = @&quot;Select u.UserId, u.UserName, p.PropertyValueStrings as Email, up.FirstName, up.LastName, up.PhoneNumber
                                    From Users u 
	                                    Inner join Profiles p on (u.UserId = p.UserId AND p.PropertyNames like &#39;Email:%&#39;)
	                                    Left outer join UserProfile up on u.UserId = up.UserId
                                    Where UserName like @searchString OR (ISNULL(up.FirstName, &#39;&#39;) + &#39; &#39; + ISNULL(up.LastName, &#39;&#39;)) like @searchString&quot;;

            using (var conn = new SqlConnection(_configRepository.GetConnectionString(_constringKey)))
            {
                conn.Open();
                return conn.Query&amp;lt;UserProfile&amp;gt;(query, new { SearchString = string.Format(&quot;%{0}%&quot;, searchString)});
            }
        }

        public UserProfile GetUserProfile(string userId)
        {
            const string query = @&quot;Select u.UserId, u.UserName, p.PropertyValueStrings as Email, up.FirstName, up.LastName, up.PhoneNumber
                                    From Users u 
	                                    Inner join Profiles p on (u.UserId = p.UserId AND p.PropertyNames like &#39;Email:%&#39;)
	                                    Left outer join UserProfile up on u.UserId = up.UserId
                                    Where u.UserId = @UserId&quot;;

            using (var conn = new SqlConnection(_configRepository.GetConnectionString(_constringKey)))
            {
                conn.Open();
                return conn.Query&amp;lt;UserProfile&amp;gt;(query, new { UserId = userId }).SingleOrDefault();
            }
        }

        public UserProfile UpsertUserProfile(UserProfile userProfile)
        {
            const string query = @&quot;Update UserProfile
                                    Set FirstName = @FirstName, LastName = @LastName, PhoneNumber = @PhoneNumber
                                    Where UserId = @UserId

                                    If @@ROWCOUNT = 0
                                    BEGIN
	
	                                    Insert Into UserProfile
	                                    (UserId, FirstName, LastName, PhoneNumber)
	                                    Values
	                                    (@UserId, @FirstName, @LastName, @PhoneNumber)
                                    END

                                    Select u.UserId, u.UserName, p.PropertyValueStrings as Email, up.FirstName, up.LastName, up.PhoneNumber
                                    From Users u 
	                                    Inner join Profiles p on (u.UserId = p.UserId AND p.PropertyNames like &#39;Email:%&#39;)
	                                    Left outer join UserProfile up on u.UserId = up.UserId
                                    Where u.UserId = @UserId&quot;;

            using (var conn = new SqlConnection(_configRepository.GetConnectionString(_constringKey)))
            {
                conn.Open();
                return conn.Query&amp;lt;UserProfile&amp;gt;(query, new {userProfile.UserId, userProfile.FirstName, userProfile.LastName, userProfile.PhoneNumber }).SingleOrDefault();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are not so much special with this repository, I started to make it testable and tried to get rid of the using (.. new SqlConnection) but did not get the whole way, so that is one thing that will be updated later. Other than that, the only thing that might seem strange is the Upsert function.&lt;/p&gt;
&lt;p&gt;It is a pattern to move the logic of knowing if it should be an update or insert further down the code and it works like this. First it tries to do an update and if it gets zero rows effected back there are no row like that in the database so then it does an insert instead. This will give an extra execution for an insert but I think it&amp;rsquo;s worth it because it saves me a lot of if-statements in the c# code.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;After all this are done, you will have a new view that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/005b920c8dc54217823240e7d550b87f.aspx&quot; width=&quot;1124&quot; alt=&quot;Image extendEditor.JPG&quot; height=&quot;503&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is a simple example on how you can create this views and even if the example feels unusable the technique is very usable and I use it a lot and my clients loves it.&lt;/p&gt;
&lt;p&gt;My goal with this post is for you to not being afraid of adding admin/edit of external data into your Episerver solution and for you to see that you can do it without learning DOJO and how to hook into the ordinary editor.&lt;/p&gt;
&lt;p&gt;I have written a couple of tests but I will write more and I will try to use this codebase to do some good example on how to do unit testing on a Episerver solution but I am in no way there yet, there are a long way to go.&lt;/p&gt;
&lt;p&gt;I publish all the source code on GitHub and I would love if anyone would do pull request to add their own extensions or to fix the code I have written or anything else. You can find the source code here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hesta96/ExtendingEditUi&quot;&gt;https://github.com/hesta96/ExtendingEditUi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I hope this post will inspire you and please add comments and ask stuff or tell me stuff you think might not be good or might be very good, all comments are great comments!&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</id><updated>2016-11-19T20:59:31.2500000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Installing WebApi and enable dependency injection for it</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/10/installing-webapi-and-enable-dependency-injection-for-it/" /><id>&lt;p&gt;&lt;strong&gt;Update:&amp;nbsp;&lt;/strong&gt;I have updated the code to do as Valdis are pointing out in the comments.&lt;/p&gt;
&lt;p&gt;It is more and more common to use WebApi inside a Episerver solution, since it is great to use when you for example like to extend the editor interface or do some ajax request on the site.&lt;/p&gt;
&lt;p&gt;Installing WebApi is very straight forward but sadly not to enable Structure Map to handle dependency injection on it and you should of course use dependency injection also on your api controllers like you do in all your MVC controllers (I hope).&lt;/p&gt;
&lt;p&gt;I am just nu preparing a session on how to extend the editorial interface that I will hold on Ascend in both Stockholm and London and in that solution, I am using webapi with dependency injection so I thought I would share on how I do in that solution to make it all work. First to say is that this solution is based on Episerver 10 that are not released yet but it should work/act the same on Episerver 9.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Installing WebApi&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Like with everything else this is done with nuget package. Install the package Microsoft.AspNet.WebApi and it will install all other stuff you will need as well.&lt;br /&gt;When I run the project the first time after installing this I got this error:&lt;br /&gt;&lt;img src=&quot;/link/0cc9891b9f154643b99fee276ccab1fa.aspx&quot; width=&quot;1264&quot; alt=&quot;Image WebApiInstallError.JPG&quot; height=&quot;534&quot; /&gt;&lt;br /&gt;The problem was that for some reason the installer had added the http-handler ExtensionlessUrlHandler-Integrated-4.0 even that it already existed in my web.config. &lt;br /&gt;The only difference between them was that one had verb=&quot;*&quot; and the other one verb=&quot;GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS&quot; and since the last one seem to include all that I know about, I kept the one with * verb and removed the other one. &lt;br /&gt;This might not happen in your solution since it depends on what you have installed before installing this.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Enable dependency injection&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;I base this session on a Alloy site so this is how to do it in Alloy and it might not be the same for you since it depends a lot on how you have set up Structure Map in your project but it can be a standpoint to take inspiration from.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1:&lt;/strong&gt; Then you create a new class that I call StructureMapScope and that class should implement IDependencyScope. For me it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class StructureMapScope : IDependencyScope
    {
        private readonly IContainer container;

        public StructureMapScope(IContainer container)
        {
            if (container == null) throw new ArgumentNullException(&quot;container cannot be null&quot;);
            this.container = container;
        }

        public object GetService(Type serviceType)
        {
            if (serviceType == null) return null;
            if (serviceType.IsAbstract || serviceType.IsInterface) return container.TryGetInstance(serviceType);

            return container.GetInstance(serviceType);
        }

        public IEnumerable&amp;lt;object&amp;gt; GetServices(Type serviceType)
        {
            return container.GetAllInstances(serviceType).Cast&amp;lt;object&amp;gt;();
        }

        public void Dispose()
        {
            this._container.Dispose();
            GC.SuppressFinalize(this);
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;strong&gt;2:&lt;/strong&gt; Then you have another class in Alloy that are called StructureMapDependencyResolver. Change that class so it also implement System.Web.Http.Dependencies.IDependencyResolver, making its definition look like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class StructureMapDependencyResolver : IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3:&lt;/strong&gt; After doing that you need to also implement a couple of new functions and they should look like this:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;#region Http IDependencyResolver

        public IDependencyScope BeginScope()
        {
            var childContainer = _container.GetNestedContainer();
            return new StructureMapScope(childContainer);
        }

        public void Dispose()
        {
            _container.Dispose();
        }

#endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4: After that you need to call this new class and add it to the global configuration, you can do that in the file&amp;nbsp;DependencyResolverInitialization. In my solution that file looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;[InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class DependencyResolverInitialization : IConfigurableModule
    {
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            context.Container.Configure(ConfigureContainer);
            var resolver = new StructureMapDependencyResolver(context.Container);

            DependencyResolver.SetResolver(resolver);
            GlobalConfiguration.Configuration.DependencyResolver = resolver;
        }

        private static void ConfigureContainer(ConfigurationExpression container)
        {
            //Swap out the default ContentRenderer for our custom
            container.For&amp;lt;IContentRenderer&amp;gt;().Use&amp;lt;ErrorHandlingContentRenderer&amp;gt;();
            container.For&amp;lt;ContentAreaRenderer&amp;gt;().Use&amp;lt;AlloyContentAreaRenderer&amp;gt;();

            //Example on how to add scanning on your own code
            container.Scan(c =&amp;gt;
            {
                c.AssemblyContainingType&amp;lt;IUserProfileRepository&amp;gt;();
                c.WithDefaultConventions();
            });
        }

        public void Initialize(InitializationEngine context)
        {
        }

        public void Uninitialize(InitializationEngine context)
        {
        }

        public void Preload(string[] parameters)
        {
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After all this is done you can safely start to use dependency injection also in you api controllers!&lt;br /&gt;&lt;br /&gt;Happy coding!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</id><updated>2016-10-22T13:12:44.6130000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>New MeetUp group in Gothenburg, Sweden</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/5/new-meetup-group-in-gothenburg-sweden/" /><id>&lt;p&gt;We have just created a new MeetUp group for all epi developers in Gothenburg or nearby.&lt;/p&gt;
&lt;p&gt;We are planning to hold a first session before summer so if you are a epi-developer and live in och nearby Gothenburg, go to this adress and register!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.meetup.com/EPiServer-utvecklare-Goteborg/&quot;&gt;http://www.meetup.com/EPiServer-utvecklare-Goteborg/&lt;/a&gt;&lt;/p&gt;</id><updated>2016-05-11T12:45:12.7470000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Episerver MeetUp in Malm&#246;, Sweden 13 of April</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/3/episerver-meetup-in-malmo-sweden-13-of-april/" /><id>&lt;p&gt;For all you developers in Malm&amp;ouml; or nearby, we will arrange a MeetUp there on the 13&amp;acute;th of April where we will show Episerver Forms and also talk about ASP.NET Core and MVC Core and how Episerver are working on it.&lt;/p&gt;
&lt;p&gt;Read more and register here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.meetup.com/EPiServer-utvecklare-Oresund/events/229838650/&quot;&gt;http://www.meetup.com/EPiServer-utvecklare-Oresund/events/229838650/&lt;/a&gt;&lt;/p&gt;
</id><updated>2016-03-28T20:45:57.4070000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Create custom report in MVC with Excel Export</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/3/create-custom-report-in-mvc-with-excel-export/" /><id>&lt;p&gt;&lt;span&gt;Lately I have seen a couple of questions about how to make a excel export from a custom report and also if it is possible to make a custom report in MVC.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I had never done any custom reports in MVC so I decided to try this things out and it showed that is was pretty simple.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;For the excel-export function I am using the external packaged EPPlus.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I decided to make a very simple report on the pages that are exist on a website,&amp;nbsp;feel free to get inspired from it.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;First I created a Controller that looks like this:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace AlloyExample.Controllers
{
   [EPiServer.PlugIn.GuiPlugIn(
        Area = EPiServer.PlugIn.PlugInArea.ReportMenu, 
        Url = &quot;~/existingpagesreport&quot;,
        Category = &quot;Existing Pages&quot;,
        DisplayName = &quot;Pages By PageType&quot;)]
    [Authorize(Roles = &quot;Administrators, WebAdmins&quot;)]
    public class ExistingPagesReportController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I define that this controller also is a GuiPlugin that should be showed in the reportmenu and I give it a url that is /existingpagesreport. I also protect it so you have to be part of the administrator group to be able to use it.&lt;/p&gt;
&lt;p&gt;For this url to work, we need to add a route to it so I add this in global.asax.cs&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;protected override void RegisterRoutes(RouteCollection routes)
        {
            base.RegisterRoutes(routes);
            routes.MapRoute(
                &quot;ExistingPagesReport&quot;,
                &quot;existingpagesreport/{action}&quot;,
                new { controller = &quot;ExistingPagesReport&quot;, action = &quot;Index&quot; });
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that I created a simple view and just tried out that it showed up in the report center.&lt;/p&gt;
&lt;p&gt;To make it a little more interesting I first added a ViewModel with the properties I needed. It looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class ExistingPagesReportViewModel
    {
        public IEnumerable&amp;lt;PageType&amp;gt; PageTypes { get; set; }
        public PageDataCollection Pages { get; set; }
        public string SelectedPageType { get; set; }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I created a helper class to make my request against Episerver and that looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static class ExistingPagesHelper
    {
        public static IEnumerable&amp;lt;PageType&amp;gt; GetAllPageTypes()
        {
            var contentTypeRepository = ServiceLocator.Current.GetInstance&amp;lt;IContentTypeRepository&amp;gt;();
            return contentTypeRepository.List().OfType&amp;lt;PageType&amp;gt;();
        }

        public static void SetPagesForPageTypeName(ExistingPagesReportViewModel model)
        {
            var criterias = new PropertyCriteriaCollection();

            var criteria = new PropertyCriteria();
            criteria.Condition = CompareCondition.Equal;
            criteria.Name = &quot;PageTypeID&quot;;
            criteria.Type = PropertyDataType.PageType;
            criteria.Value = model.SelectedPageType;
            criteria.Required = true;

            criterias.Add(criteria);

            var pages = DataFactory.Instance.FindPagesWithCriteria(ContentReference.RootPage, criterias);

            model.Pages = pages;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that I updated my index action so it created an instance of the view model and assigned all pagetypes to it. So now it looks like this.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public ActionResult Index()
        {
           var model = new ExistingPagesReportViewModel { PageTypes = ExistingPagesHelper.GetAllPageTypes() };

            return View(model);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I created my view and since I wanted it to feel the same way as the built in reports I added Epi&amp;rsquo;s css and javascript to it. The complete view looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@model ExistingPagesReportViewModel

@using EPiServer.DataAbstraction
@using EPiServer.Framework.Web.Resources

@{
    Layout = null;
}

&amp;lt;!DOCTYPE html&amp;gt;

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=Edge&quot; /&amp;gt;
    &amp;lt;!-- Shell --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;ShellCore&quot;))
    &amp;lt;!-- LightTheme --&amp;gt;
    @Html.Raw(ClientResources.RenderResources(&quot;ShellCoreLightTheme&quot;))

    &amp;lt;link href=&quot;/EPiServer/CMS/App_Themes/Default/Styles/system.css&quot; type=&quot;text/css&quot; rel=&quot;stylesheet&quot;&amp;gt;
    &amp;lt;link href=&quot;/EPiServer/CMS/App_Themes/Default/Styles/ToolButton.css&quot; type=&quot;text/css&quot; rel=&quot;stylesheet&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    @Html.Raw(Html.ShellInitializationScript())

    &amp;lt;div class=&quot;epi-contentContainer epi-padding&quot;&amp;gt;
        &amp;lt;div class=&quot;epi-contentArea&quot;&amp;gt;
            &amp;lt;div class=&quot;EP-systemImage&quot; style=&quot;background-image: url(&#39;/App_Themes/Default/Images/ReportCenter/PublishedPages.gif&#39;);&quot;&amp;gt;
                &amp;lt;h1 class=&quot;EP-prefix&quot;&amp;gt;
                    Existing Pages
                &amp;lt;/h1&amp;gt;
                &amp;lt;p class=&quot;EP-systemInfo&quot;&amp;gt;
                    This report displays pages that exists on the site.
                &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div id=&quot;FullRegion_ValidationSummary&quot; class=&quot;EP-validationSummary&quot; style=&quot;color: Black; display: none;&quot;&amp;gt;

            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        @using (Html.BeginForm(&quot;ListPages&quot;, &quot;ExistingPagesReport&quot;, FormMethod.Post))
        {
            &amp;lt;script src=&quot;/Util/javascript/episerverscriptmanager.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
            &amp;lt;script src=&quot;/EPiServer/CMS/javascript/system.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
            &amp;lt;script src=&quot;/EPiServer/CMS/javascript/dialog.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
            &amp;lt;script src=&quot;/EPiServer/CMS/javascript/system.aspx&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;

            &amp;lt;input type=&quot;hidden&quot; id=&quot;doExport&quot; name=&quot;doExport&quot; value=&quot;False&quot;&amp;gt;
            &amp;lt;div class=&quot;epi-formArea&quot;&amp;gt;
                &amp;lt;fieldset&amp;gt;
                    &amp;lt;legend&amp;gt;
                        Report Criteria
                    &amp;lt;/legend&amp;gt;
                    &amp;lt;div class=&quot;epi-size10&quot;&amp;gt;
                        &amp;lt;label for=&quot;pageTypes&quot;&amp;gt;Select PageType&amp;lt;/label&amp;gt;
                        &amp;lt;select name=&quot;pageType&quot; id=&quot;pageType&quot;&amp;gt;
                            @foreach (var type in Model.PageTypes.Where(w =&amp;gt; w.ID != 1).OrderBy(o =&amp;gt; o.Name))
                            {
                                &amp;lt;option value=&quot;@type.ID&quot; @(type.ID.ToString() == Model.SelectedPageType ? &quot;selected=selected&quot; : &quot;&quot;) &amp;gt;@type.Name&amp;lt;/option&amp;gt;
                            }
                        &amp;lt;/select&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/fieldset&amp;gt;

                &amp;lt;div class=&quot;epitoolbuttonrow&quot;&amp;gt;
                    &amp;lt;span class=&quot;epi-cmsButton&quot;&amp;gt;&amp;lt;input class=&quot;epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Report&quot; type=&quot;submit&quot; name=&quot;showReport&quot; id=&quot;showReport&quot; value=&quot;Show Report&quot; onmouseover=&quot;EPi.ToolButton.MouseDownHandler(this)&quot; onmouseout=&quot;EPi.ToolButton.ResetMouseDownHandler(this)&quot; /&amp;gt;&amp;lt;/span&amp;gt;
                    &amp;lt;span class=&quot;epi-cmsButton&quot;&amp;gt;&amp;lt;input class=&quot;epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Report&quot; type=&quot;submit&quot; name=&quot;exportReport&quot; id=&quot;exportReport&quot; value=&quot;Export Report&quot; onmouseover=&quot;EPi.ToolButton.MouseDownHandler(this)&quot; onmouseout=&quot;EPi.ToolButton.ResetMouseDownHandler(this)&quot; /&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/div&amp;gt;

            &amp;lt;/div&amp;gt;
        }

        @if (Model.Pages != null &amp;amp;&amp;amp; Model.Pages.Count &amp;gt; 0)
        {
            &amp;lt;div class=&quot;epi-floatLeft epi-marginVertical-small&quot;&amp;gt;Number of Hits: @Model.Pages.Count&amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;epi-contentArea epi-clear&quot;&amp;gt;
                &amp;lt;div&amp;gt;
                    &amp;lt;table class=&quot;epi-default epi-default-legacy&quot; cellspacing=&quot;0&quot; id=&quot;FullRegion_MainRegion_ReportView&quot; style=&quot;border-style: None; width: 100%; border-collapse: collapse;&quot;&amp;gt;
                        &amp;lt;tr&amp;gt;
                            &amp;lt;th scope=&quot;col&quot;&amp;gt;Page Id&amp;lt;/th&amp;gt;
                            &amp;lt;th scope=&quot;col&quot;&amp;gt;Page Name&amp;lt;/th&amp;gt;
                            &amp;lt;th scope=&quot;col&quot;&amp;gt;Page Url&amp;lt;/th&amp;gt;
                            &amp;lt;th scope=&quot;col&quot;&amp;gt;Published Date&amp;lt;/th&amp;gt;
                        &amp;lt;/tr&amp;gt;
                        @foreach (var page in Model.Pages)
                        {
                            &amp;lt;tr&amp;gt;
                                &amp;lt;td style=&quot;width: 27%;&quot;&amp;gt;@page.ContentLink.ID&amp;lt;/td&amp;gt;
                                &amp;lt;td&amp;gt;@page.PageName&amp;lt;/td&amp;gt;
                                &amp;lt;td&amp;gt;@Url.ContentUrl(page.ContentLink)&amp;lt;/td&amp;gt;
                                &amp;lt;td&amp;gt;@(page.StartPublish.HasValue ? page.StartPublish.Value.ToString(&quot;yyyy-MM-dd HH:mm&quot;) : &quot;Not published&quot;)&amp;lt;/td&amp;gt;
                            &amp;lt;/tr&amp;gt;
                        }
                    &amp;lt;/table&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

        }

    &amp;lt;/div&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
        document.getElementById(&quot;exportReport&quot;).onclick = function () {
            document.getElementById(&quot;doExport&quot;).value = &quot;True&quot;;
        };
        document.getElementById(&quot;showReport&quot;).onclick = function () {
            document.getElementById(&quot;doExport&quot;).value = &quot;False&quot;;
        };
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see I have two submit buttons but only one form actions and because of that I have connected javascript function to the click event on the export and search submit button that updates a hidden value telling the controller to do an export of the data or not.&lt;/p&gt;
&lt;p&gt;The action in the controller that handles this looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[HttpPost]
        public ActionResult ListPages(FormCollection form)
        {
            var model = new ExistingPagesReportViewModel
            {
                PageTypes = ExistingPagesHelper.GetAllPageTypes(),
                SelectedPageType = form[&quot;pageType&quot;]
            };

            ExistingPagesHelper.SetPagesForPageTypeName(model);


            var doExport = false;

            if (bool.TryParse(form[&quot;doExport&quot;], out doExport) &amp;amp;&amp;amp; doExport &amp;amp;&amp;amp; model.Pages != null &amp;amp;&amp;amp; model.Pages.Count &amp;gt; 0)
            {
                Export(model.Pages, System.Web.HttpContext.Current.Response);
            }

            return View(&quot;Index&quot;, model);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see it returns the view if the export values is not true. To do the export I use EPPlus and for this simple export I just created a function in the same class that looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public void Export(PageDataCollection pagesToExport, HttpResponse response)
        {
            using (var package = new ExcelPackage())
            {
                ExcelWorksheet ws = package.Workbook.Worksheets.Add(&quot;pages&quot;);

                ws.Cells[1, 1].Value = &quot;PageId&quot;;
                ws.Cells[1, 2].Value = &quot;PageName&quot;;
                ws.Cells[1, 3].Value = &quot;PageUrl&quot;;
                ws.Cells[1, 4].Value = &quot;Published Date&quot;;

                ws.Row(1).Style.Font.Bold = true;
                ws.Row(1).Style.Locked = true;

                int row = 2;

                foreach (var page in pagesToExport)
                {
                    ws.Cells[row, 1].Value = page.ContentLink.ID;
                    ws.Cells[row, 2].Value = page.PageName;
                    ws.Cells[row, 3].Value = Url.ContentUrl(page.ContentLink);
                    ws.Cells[row, 4].Value = page.StartPublish.HasValue ? page.StartPublish.Value.ToString(&quot;yyyy-MM-dd HH:mm&quot;) : &quot;Not published&quot;;

                    ++row;
                }

                response.ContentType = &quot;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet&quot;;
                response.AddHeader(&quot;content-disposition&quot;, string.Format(&quot;attachment; filename=pages{0}.xlsx&quot;, DateTime.Now.ToString(&quot;yyyyMMdd&quot;)));
                response.BinaryWrite(package.GetAsByteArray());
                response.Flush();
                response.End();
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a simple example on how to make a custom report in MVC with an Excel export function in it. Hope that it will help you get started and make your own great reports!&lt;/p&gt;
&lt;p&gt;Happy coding!!&lt;/p&gt;</id><updated>2016-03-19T14:10:25.4530000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Using AppSettings instead of ConfigSections for Episerver Find</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2016/3/using-appsettings-instead-of-configsections-for-episerver-find/" /><id>&lt;p&gt;In the documentation for Episerver Find it says that you should write like this in your app.config or web.config file:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;configuration&amp;gt;
     &amp;lt;configSections&amp;gt;
            &amp;lt;section
            name=&quot;episerver.find&quot;
            type=&quot;EPiServer.Find.Configuration, EPiServer.Find&quot; requirePermission=&quot;false&quot;/&amp;gt;
        &amp;lt;/configSections&amp;gt;
        &amp;lt;episerver.find
            serviceUrl=&quot;http://...&quot;
        defaultIndex=&quot;myindex&quot;/&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There also exist another way to set this up that are a little hidden, and that are to go through the appsettings in the config file. So instead of writing like the above you can write like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;appSettings&amp;gt;
    &amp;lt;add key=&quot;episerver:FindServiceUrl&quot; value=&quot;http://...&quot; /&amp;gt;
    &amp;lt;add key=&quot;episerver:FindDefaultIndex&quot; value=&quot;myindex&quot; /&amp;gt;
  &amp;lt;/appSettings&amp;gt;
  &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on how you have set up you deployment pipeline this could be a nice way since it is easier to implement config transformation for this I think.&lt;/p&gt;</id><updated>2016-03-18T11:24:39.1100000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>PropertyAppSettings, a great but kind of hidden build in function in Episerver</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/11/a-great-but-kind-of-hidden-build-in-function-in-episerver/" /><id>&lt;p&gt;This function has been built in for a long time, but the documentation of it is very hidden, so I hope I with this post will inspire more people to use it.&lt;/p&gt;
&lt;p&gt;This is very usefull if you like to have a selectbox with hard values in, meaning that the content of the select box will not change other than when you deploy the next time.&lt;/p&gt;
&lt;p&gt;The property is called&amp;nbsp;PropertyAppSettings and it works like this.&lt;/p&gt;
&lt;p&gt;First create a new appsettings in web.config that looks like this&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;add key=&quot;BackgroundColor&quot; value=&quot;White;#fff|Black;#000|Blue;#0276FD&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then in your content type definition you just write&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[Display(Name = &quot;Background Color&quot;,
   Description = &quot;Select the background color of the page&quot;,
   Order = 1)]
[BackingType(typeof(PropertyAppSettings))]
public virtual string BackgroundColor { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important thing is that your property must be named exactly the same as your key in the appsettings. After that you will have a property that will look something like this in the editor&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ef83a0748c33422cae396d2e949ef849.aspx&quot; alt=&quot;Image example.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hopes this help someone.&lt;/p&gt;</id><updated>2015-11-20T12:03:38.2500000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Create a prop-snippet to support property with virtual</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/5/adding-episerver-find-to-alloy---part-4/" /><id>&lt;p&gt;When I am developing a site and creating a pagetype or block type I use the shortcut for creating a property inside Visual Studio a lot. The problem with that one is that is does not include the virtual part so I often forget that and without setting a property to virtual EPiServer will not use it.&lt;/p&gt;
&lt;p&gt;These shortcuts are called snippets and are realy easy to create you own of. If you have Visual studio 2015 and using csharp you can find a lot of the snippets here:&lt;/p&gt;
&lt;p&gt;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC#\Snippets\1033\Visual C#&lt;/p&gt;
&lt;p&gt;To create your own epi-version of the prop shortcut just copy the one that is called prop.snippet and rename it to epiprop. Then open it and make shore it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;CodeSnippets  xmlns=&quot;http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet&quot;&amp;gt;
	&amp;lt;CodeSnippet Format=&quot;1.0.0&quot;&amp;gt;
		&amp;lt;Header&amp;gt;
			&amp;lt;Title&amp;gt;epiprop&amp;lt;/Title&amp;gt;
			&amp;lt;Shortcut&amp;gt;epiprop&amp;lt;/Shortcut&amp;gt;
			&amp;lt;Description&amp;gt;Code snippet for an automatically implemented property
Language Version: C# 3.0 or higher&amp;lt;/Description&amp;gt;
			&amp;lt;Author&amp;gt;Microsoft Corporation&amp;lt;/Author&amp;gt;
			&amp;lt;SnippetTypes&amp;gt;
				&amp;lt;SnippetType&amp;gt;Expansion&amp;lt;/SnippetType&amp;gt;
			&amp;lt;/SnippetTypes&amp;gt;
		&amp;lt;/Header&amp;gt;
		&amp;lt;Snippet&amp;gt;
			&amp;lt;Declarations&amp;gt;
				&amp;lt;Literal&amp;gt;
					&amp;lt;ID&amp;gt;order&amp;lt;/ID&amp;gt;
					&amp;lt;ToolTip&amp;gt;Sort Order&amp;lt;/ToolTip&amp;gt;
					&amp;lt;Default&amp;gt;10&amp;lt;/Default&amp;gt;
				&amp;lt;/Literal&amp;gt;
				&amp;lt;Literal&amp;gt;
					&amp;lt;ID&amp;gt;name&amp;lt;/ID&amp;gt;
					&amp;lt;ToolTip&amp;gt;Property Name&amp;lt;/ToolTip&amp;gt;
					&amp;lt;Default&amp;gt;Name&amp;lt;/Default&amp;gt;
				&amp;lt;/Literal&amp;gt;
				&amp;lt;Literal&amp;gt;
					&amp;lt;ID&amp;gt;type&amp;lt;/ID&amp;gt;
					&amp;lt;ToolTip&amp;gt;Property type&amp;lt;/ToolTip&amp;gt;
					&amp;lt;Default&amp;gt;int&amp;lt;/Default&amp;gt;
				&amp;lt;/Literal&amp;gt;
				&amp;lt;Literal&amp;gt;
					&amp;lt;ID&amp;gt;property&amp;lt;/ID&amp;gt;
					&amp;lt;ToolTip&amp;gt;Property name&amp;lt;/ToolTip&amp;gt;
					&amp;lt;Default&amp;gt;MyProperty&amp;lt;/Default&amp;gt;
				&amp;lt;/Literal&amp;gt;
			&amp;lt;/Declarations&amp;gt;
			&amp;lt;Code Language=&quot;csharp&quot;&amp;gt;&amp;lt;![CDATA[[Display(
			Name = &quot;$name$&quot;
			GroupName = SystemTabNames.Content,
			Order = $order$)]
			public virtual $type$ $property$ { get; set; }$end$]]&amp;gt;
			&amp;lt;/Code&amp;gt;
		&amp;lt;/Snippet&amp;gt;
	&amp;lt;/CodeSnippet&amp;gt;
&amp;lt;/CodeSnippets&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will give you a shortcut that if you write (after restarting Visual Studio, this is important) epiprop and press tab will look like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;        [Display(
            Name = &quot;Name&quot;
            GroupName = SystemTabNames.Content,
            Order = 10)]
        public virtual int MyProperty { get; set; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this will help all others that aslo forget virtual sometimes&lt;/p&gt;</id><updated>2015-11-13T05:20:21.9800000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to create a admin sql user through code</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/10/how-to-create-a-admin-user-through-code/" /><id>&lt;p&gt;&lt;span&gt;Sometimes you need to have a sql user in your development environment and this is easy to create by code on startup of the application. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In this example I added an if statement so that this is only running if the application are running in debug mode and this is because I do not want this to be created in test och production, only in dev environment.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Important, if you copy this, change the username and password to fit your needs, this is just an example with a generated password!!!&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System.Configuration.Provider;
using System.Web.Security;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Logging.Compatibility;

namespace Alloy46.Business
{
    [InitializableModule]
    public class CreateAdminUserAndRoles : IInitializableModule
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(CreateAdminUserAndRoles));

        public void Initialize(InitializationEngine context)
        {
#if DEBUG
            var mu = Membership.GetUser(&quot;EpiSQLAdmin&quot;);

            if (mu != null) return;

            try
            {
                Membership.CreateUser(&quot;EpiSQLAdmin&quot;, &quot;6hEthU&quot;, &quot;EpiSQLAdmin@site.com&quot;);

                try
                {
                    this.EnsureRoleExists(&quot;WebEditors&quot;);
                    this.EnsureRoleExists(&quot;WebAdmins&quot;);

                    Roles.AddUserToRoles(&quot;EpiSQLAdmin&quot;, new[] { &quot;WebAdmins&quot;, &quot;WebEditors&quot; });
                }
                catch (ProviderException pe)
                {
                    Log.Error(pe);
                }
            }
            catch (MembershipCreateUserException mcue)
            {
                Log.Error(mcue);
            }
#endif
        }

        public void Uninitialize(InitializationEngine context)
        {
        }

        private void EnsureRoleExists(string roleName)
        {
            if (Roles.RoleExists(roleName)) return;

            try
            {
                Roles.CreateRole(roleName);
            }
            catch (ProviderException pe)
            {
                Log.Error(pe);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</id><updated>2015-10-27T13:18:11.4970000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>The known and unknown of Include, IncludeOn, Exclude and ExcludeOn</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/10/the-known-and-unknown-of-include-includeon-exclude-and-excludeon/" /><id>&lt;p&gt;&lt;span&gt;These four settings have always been and are little headache for me and today I took some time trying to learn more about them.&lt;br /&gt;&lt;/span&gt;Basically they work like this:&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;strong&gt;Include&lt;/strong&gt;&lt;br /&gt; A type array of typed content to specify which content types are available under a content instance of the type with the attribute.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exclude&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;A type array of typed content to specify which content types are not available under a content instance of the type with the attribute.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IncludeOn&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;States that the content with this attribute is available under the typed content in the type array.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ExcludeOn&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;States that the content with this attribute is not available under any of the typed content in the type array.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;This is pretty straight forward and the most importing part is to realize that the Include is not just including the defined types, it is also excluding all other things.&lt;br /&gt;&lt;/span&gt;But the funny/hard thing to learn is what happens when you combine them together, which property is more important than the other.&lt;/p&gt;
&lt;p&gt;To figure that out I took a normal Alloy site and changed the settings for StartPage and ProductPage.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[AvailableContentTypes(
        Availability.Specific,
        Include = new[] { typeof(SearchPage) }, 
        ExcludeOn = new[] { typeof(ProductPage) })] 
    public class StartPage : SitePageData
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; [AvailableContentTypes( 
        Availability = Availability.Specific,
        Include = new[] { typeof(StartPage), typeof(SearchPage) },
        IncludeOn = new[] { typeof(StartPage) })]
    public class ProductPage : StandardPage, IHasRelatedContent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gave me this look inside admin:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/8e3d1b317798427793ae7ef960420a35.aspx&quot; alt=&quot;Image startpage1.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/be8f55566d654f08bbeec3edef1412eb.aspx&quot; alt=&quot;Image productpage1.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This shows me that IncludeOn and ExcludeOn are prioritized over Include and Exclude, since even though I say that I should include StartPage on ProductPage, it is not included and even though I do not define ProductPage to be included below StartPage it is included on it.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Then I wounded what would happen if I say that the only thing that should be able to be created below ProductPage should be StartPage and keep saying to StartPage that it should be ExcludedOn Productpage. So I changed the code for ProductPage so it looks like this:&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [AvailableContentTypes( 
        Availability = Availability.Specific,
        Include = new[] { typeof(StartPage) },
        IncludeOn = new[] { typeof(StartPage) })]
    public class ProductPage : StandardPage, IHasRelatedContent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;I thought that I would end up not being able to create anything at all below ProductPage but to my surprise this is what the result was:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img src=&quot;/link/0cc856b2cee14a7090eb2343e95aafa6.aspx&quot; alt=&quot;Image product2.png&quot; /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Somehow I ended up with being able to create everything but StartPage and that was not I was hoping for and for me it feels like a bug, what does you think?&lt;/span&gt;&lt;/p&gt;</id><updated>2015-10-26T20:29:20.9500000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Setup EPiServer Search in a future safe and stable way</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/8/setup-episerver-search-in-a-future-safe-and-stable-way/" /><id>&lt;p&gt;In almost every project I have done where I have the solution in a load balanced way I have had problem with EPiServer Search. Now I have figured out a way to set it up so the problems are nearly gone. This is also the way you must set it up if you would like to move your solution to Microsoft Azure.&lt;/p&gt;
&lt;h3&gt;Develop&amp;nbsp;environment&lt;/h3&gt;
&lt;p&gt;I start out this example by creating a new Alloy project with the extension inside Visual Studio. That gives me a MVC project with EPiServer Search that are setup and will work if I press F5. I have now a solution that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5837d1421ada44a0878554c84f9b9f24.aspx&quot; alt=&quot;Image basic.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Inside web.config EPiServer Search are configured like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;  &amp;lt;episerver.search active=&quot;true&quot;&amp;gt;
    &amp;lt;namedIndexingServices defaultService=&quot;serviceName&quot;&amp;gt;
      &amp;lt;services&amp;gt;
        &amp;lt;add name=&quot;serviceName&quot; baseUri=&quot;http://localhost:27565/IndexingService/IndexingService.svc&quot; accessKey=&quot;local&quot; /&amp;gt;
      &amp;lt;/services&amp;gt;
    &amp;lt;/namedIndexingServices&amp;gt;
    &amp;lt;searchResultFilter defaultInclude=&quot;true&quot;&amp;gt;
      &amp;lt;providers /&amp;gt;
    &amp;lt;/searchResultFilter&amp;gt;
  &amp;lt;/episerver.search&amp;gt;
 &amp;lt;episerver.search.indexingservice&amp;gt;
    &amp;lt;clients&amp;gt;
      &amp;lt;add name=&quot;local&quot; description=&quot;local&quot; allowLocal=&quot;true&quot; readonly=&quot;false&quot; /&amp;gt;
    &amp;lt;/clients&amp;gt;
    &amp;lt;namedIndexes defaultIndex=&quot;default&quot;&amp;gt;
      &amp;lt;indexes&amp;gt;
        &amp;lt;add name=&quot;default&quot; directoryPath=&quot;[appDataPath]\Index&quot; readonly=&quot;false&quot; /&amp;gt;
      &amp;lt;/indexes&amp;gt;
    &amp;lt;/namedIndexes&amp;gt;
  &amp;lt;/episerver.search.indexingservice&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the basic way of configure EPiServer Search and it will work for small projects that are not in a load balanced &lt;span&gt;environment&lt;/span&gt;&amp;nbsp;or on Microsoft Azure. The problem with having this configuration in production in a load balanced &lt;span&gt;environment&lt;/span&gt;&amp;nbsp;is that if you put the index files on a common drive and let all the websites index then the index files will be corrupt. You can solve this by setting only one of the sites to do the index and that usually works, but I have had times when it fails because of for example problems with routing and so on.&lt;/p&gt;
&lt;p&gt;The solution is to create another project inside your solution and when you create it be careful to not get to much example code in it. It should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/151a6113afae4422a49324d34608c69d.aspx&quot; alt=&quot;Image Setup1.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/63b75f4335f244d79d0825a2cc06ac52.aspx&quot; alt=&quot;Image setup2.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the images, I create a empty site with no folders and core reference to anything. I do not create a test project to it and does not host it in the cloud (even if that would be possible)&lt;/p&gt;
&lt;p&gt;After doing that I add the EPiServer Search nuget package to the new site and remove that package from the regular web site. I also add a App_data folder to the new site, so I have somewhere to save the indexing files. The solution then looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/b2227348bc5d48a480871f6fff7e058e.aspx&quot; alt=&quot;Image Setup3.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now to get it to work we need to do a couple of things. We start by locating these lines in the regular web site and remove them (the nuget package are great at adding stuff but not removing them).&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;&amp;lt;section name=&quot;episerver.search.indexingservice&quot; type=&quot;EPiServer.Search.IndexingService.Configuration.IndexingServiceSection, EPiServer.Search.IndexingService&quot; /&amp;gt;

      &amp;lt;dependentAssembly&amp;gt;
        &amp;lt;assemblyIdentity name=&quot;EPiServer.Search.IndexingService&quot; publicKeyToken=&quot;8fe83dea738b45b7&quot; culture=&quot;neutral&quot; /&amp;gt;
        &amp;lt;bindingRedirect oldVersion=&quot;0.0.0.0-7.7.1.0&quot; newVersion=&quot;7.7.1.0&quot; /&amp;gt;
      &amp;lt;/dependentAssembly&amp;gt;

&amp;lt;episerver.search.indexingservice&amp;gt;
    &amp;lt;clients&amp;gt;
      &amp;lt;add name=&quot;local&quot; description=&quot;local&quot; allowLocal=&quot;true&quot; readonly=&quot;false&quot; /&amp;gt;
    &amp;lt;/clients&amp;gt;
    &amp;lt;namedIndexes defaultIndex=&quot;default&quot;&amp;gt;
      &amp;lt;indexes&amp;gt;
        &amp;lt;add name=&quot;default&quot; directoryPath=&quot;[appDataPath]\Index&quot; readonly=&quot;false&quot; /&amp;gt;
      &amp;lt;/indexes&amp;gt;
    &amp;lt;/namedIndexes&amp;gt;
  &amp;lt;/episerver.search.indexingservice&amp;gt;
  &amp;lt;location path=&quot;IndexingService/IndexingService.svc&quot;&amp;gt;
    &amp;lt;system.web&amp;gt;
      &amp;lt;httpRuntime maxQueryStringLength=&quot;65536&quot; /&amp;gt;
    &amp;lt;/system.web&amp;gt;
    &amp;lt;system.webServer&amp;gt;
      &amp;lt;security&amp;gt;
        &amp;lt;requestFiltering&amp;gt;
          &amp;lt;requestLimits maxQueryString=&quot;65536&quot; /&amp;gt;
        &amp;lt;/requestFiltering&amp;gt;
      &amp;lt;/security&amp;gt;
    &amp;lt;/system.webServer&amp;gt;
  &amp;lt;/location&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open up Web.Config inside the new project that contains EPiServer Search and change this line:&lt;br /&gt;&amp;lt;add name=&quot;default&quot; directoryPath=&quot;[appDataPath]\Index&quot; readonly=&quot;false&quot; /&amp;gt; to&lt;br /&gt;&amp;lt;add name=&quot;default&quot; directoryPath=&quot;App_Data\Index&quot; readonly=&quot;false&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Then find this part&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;    &amp;lt;clients&amp;gt;
      &amp;lt;add name=&quot;local&quot; description=&quot;local&quot; allowLocal=&quot;true&quot; readonly=&quot;false&quot; /&amp;gt;
    &amp;lt;/clients&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And change to this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;    &amp;lt;clients&amp;gt;
      &amp;lt;add name=&quot;C7184E3ED7134895B3E95AB3AB4F4AE5&quot; description=&quot;local&quot; allowLocal=&quot;false&quot; ipAddress=&quot;0.0.0.0/0&quot; ip6Address=&quot;::/0&quot; readonly=&quot;false&quot; /&amp;gt;
    &amp;lt;/clients&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the name is a pretty much unique string and I changed allowLocal to false and added settings for ipv4 och ipv6 that basicly says allow all. You can read more here on why to add this settings:&amp;nbsp;&lt;a href=&quot;/link/e4e968bc48f54ee2a92dc2e4e3ac017f.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-CMS/8/Search/Installing-and-deploying-Search-Service/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now you need to go back to the web.config for the regular web project and find the line that points out the endpoint to the indexing asmx, should look something like this:&lt;/p&gt;
&lt;p&gt;&amp;lt;add name=&quot;serviceName&quot; baseUri=&quot;http://localhost:27565/IndexingService/IndexingService.svc&quot; accessKey=&quot;local&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;First change the accessKey to match the pretty much unique name you selected before and then change the baseUri so it match the uri you have to your search project. For me it become like this:&lt;/p&gt;
&lt;p&gt;&amp;lt;add name=&quot;serviceName&quot; baseUri=&quot;http://localhost:28217/IndexingService/IndexingService.svc&quot; accessKey=&quot;C7184E3ED7134895B3E95AB3AB4F4AE5&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Now you should be pretty much up and running for the developer part. Start debug or make shore that you start up both web projects and then go into this url:&lt;/p&gt;
&lt;p&gt;http://localhost:27565/EPiServer/CMS/Admin/IndexContent.aspx&lt;/p&gt;
&lt;p&gt;Make shore you check Delete old data and then press Start Indexing.&lt;/p&gt;
&lt;p&gt;This will reindex all your site the new index.&lt;/p&gt;
&lt;p&gt;Important: If you do this in Visual Studio 2015 and you get an error like this when browsing to&amp;nbsp;http://localhost:28217/IndexingService/IndexingService.svc:&lt;br /&gt;Could not find a part of the path &#39;C:\.....\EPiServerSite.EPiServerSearch\bin\roslyn\csc.exe&#39;. just do like they say here:&lt;br /&gt;&lt;a href=&quot;https://support.appharbor.com/discussions/problems/78633-cant-build-aspnet-mvc-project-generated-from-vstudio-2015-enterprise#comment_37577678&quot;&gt;https://support.appharbor.com/discussions/problems/78633-cant-build-aspnet-mvc-project-generated-from-vstudio-2015-enterprise#comment_37577678&lt;/a&gt;&lt;br /&gt;It is a bug in VS2015&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Live&amp;nbsp;environment&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When you deploy this to production you need to prepare by creating a new website on one of your servers and then either go by port or by a hostname to reach it from all the other servers.&lt;/p&gt;
&lt;p&gt;In my example I create one with a host name since I only have port 80 and 443 open in the firewall so I can not reach the iis from outside the server on any other port. So for example create a site like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/c7e4f3ffe99e4343a59e2fa5ed3b77cb.aspx&quot; alt=&quot;Image site.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then I will add a transformation for my web.config in my regular site like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;

&amp;lt;!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 --&amp;gt;

&amp;lt;configuration xmlns:xdt=&quot;http://schemas.microsoft.com/XML-Document-Transform&quot;&amp;gt;
  &amp;lt;episerver.search active=&quot;true&quot;&amp;gt;
    &amp;lt;namedIndexingServices defaultService=&quot;serviceName&quot;&amp;gt;
      &amp;lt;services&amp;gt;
        &amp;lt;add name=&quot;serviceName&quot; baseUri=&quot;http://EPiServerSite.EPiServerSearch/IndexingService/IndexingService.svc&quot; accessKey=&quot;C7184E3ED7134895B3E95AB3AB4F4AE5&quot; xdt:Transform=&quot;Replace&quot; /&amp;gt;
      &amp;lt;/services&amp;gt;
    &amp;lt;/namedIndexingServices&amp;gt;
  &amp;lt;/episerver.search&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will not work out of the box since the servers does not know how to translate the &quot;url&quot; EPiServerSite.EPiServerSearch to an ip-adress so we need to tell them that. To do this, open up a notepad as a administrator on the servers and navigate to this file:&lt;/p&gt;
&lt;p&gt;C:\Windows\System32\drivers\etc\hosts&lt;br /&gt;It has not file suffix so you need to change in notepad to show all files to see the file hosts. In it on the server that contains the search site add a line like this:&lt;/p&gt;
&lt;p&gt;127.0.0.1&lt;span&gt; &lt;/span&gt;EPiServerSite.EPiServerSearch&amp;nbsp;&lt;/p&gt;
&lt;p&gt;And on the other servers add a &lt;span&gt;similar&lt;/span&gt;&amp;nbsp;line but change 127.0.0.1 (that means localhost) to the ipaddress of the server containing the search site.&lt;/p&gt;
&lt;p&gt;This works great for me and I have saved myself a lot of problems by doing like this.&lt;/p&gt;</id><updated>2015-08-31T22:29:10.0570000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>A breaking change in EPiServer 8 that are not on the list</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/5/a-breaking-change-in-episerver-8-that-are-not-on-the-list/" /><id>&lt;p&gt;EPiServer has defined all the breaking changes for EPiServer 8 on this page:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/3381e12654c04ace971fe8a3d90ae081.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Upgrading/EPiServer-CMS/8/Breaking-changes/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are one change that effected me and that was not on the list and it is&amp;nbsp;for the function&amp;nbsp;GetVirtualPath of&amp;nbsp;UrlResolver.&lt;/p&gt;
&lt;p&gt;In EPiServer 7.5&amp;nbsp;GetVirtualPath returns a string directly but in EPiServer 8 it returns&amp;nbsp;System.Web.Routing.VirtualPathData&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Actually&lt;/span&gt;&amp;nbsp;GetVirualPath were marked as &lt;span&gt;obsolete&lt;/span&gt;&amp;nbsp;in 7.5 so you could think that it would be removed in EPiServer 8 but it is not and another big differens is that if you have a contentreference that has the url &quot;section/subsection/pagename&quot; GetVirtualPath([ContentRef]).VirtualPath will return &quot;section/subsection/pagename&quot;. In 7.5 GetVirtualPath for the same content returned &quot;/section/subsection/pagename&quot;.&lt;/p&gt;
&lt;p&gt;The way to solve this is to do as EPiServer told as in 7.5 and that is to change from GetVirtualPath() to GetUrl() and all will work (and it returns a &quot;/&quot; url for the content)&lt;/p&gt;
&lt;p&gt;Just thought I should share this with you, since it caused me some &lt;span&gt;headache&lt;/span&gt;&amp;nbsp;and took some time to find (the thing with &quot;section&quot; instead of &quot;/section&quot; url)&lt;/p&gt;</id><updated>2015-05-29T10:31:26.9800000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Automatic schema updates with backup for SQL Azure</title><link href="https://world.optimizely.com/blogs/Henrik-Fransas/Dates/2015/5/automatic-schema-updates-with-backup-for-sql-azure/" /><id>&lt;p&gt;From version 8.7 of EPiServer it is now possible to allow EPiServer to make &lt;span&gt;schema changes&amp;nbsp;&lt;/span&gt;to the database &lt;span&gt;automatically&lt;/span&gt;&amp;nbsp;when there are any in the upcoming version of EPiServer. You can read more about it here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/71614863c0ef4a70bad72c55ba5518d1.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-CMS/8/Deployment/automatic-schema-updates/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is great news because this has always been a problem when deploying to production &lt;span&gt;environment&lt;/span&gt;&amp;nbsp;where you might not have access to run sql-&lt;span&gt;script&lt;/span&gt;&amp;nbsp;on the database and it has also been hard to know when there are any schema changes and not. Now EPiServer can handle all that for us &lt;span&gt;automatically&lt;/span&gt;&amp;nbsp;and that is great, but it comes with a price. The things to consider I think is this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Now the user MUST be dbo of the database, otherwise this will fail. Up to now it was possible to limit &lt;span&gt;access&lt;/span&gt;&amp;nbsp;to the database to be datawriter and datareader with execution rights on all objects of the dbo schema. This will &lt;span&gt;probably&lt;/span&gt;&amp;nbsp;not be a problem for most sites, since most are already has a user that is dbo connected to the database.&lt;/li&gt;
&lt;li&gt;If it all runs &lt;span&gt;automatically&lt;/span&gt;&amp;nbsp;we do no know when it is extra important to make a backup of the database before deploying (I recommend that you do that for every deployment you do, otherwise it is very hard to roll back to the version before do deployment since EPiServer could have done a lot of data changes to the database on the first run of of the site).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily&amp;nbsp;for us EPiServer has thought of the second problem and made it possible for us to implement our own logic that should happen before the update happens. There are some great examples in the documentation where they show how to &lt;span&gt;automatically&lt;/span&gt;&amp;nbsp;create a backup file of the database before updating it. The problem for this when you have your database in Azure is that Azure SQL Database does not support backup the way you do it in a regular SQL Server.&lt;/p&gt;
&lt;p&gt;The way Azure SQL Databases works is that &lt;span&gt;Azure SQL Database automatically creates backups of every active database. Every hour a backup is taken and additionally, transaction log backups are taken every 5 minutes. This gives you opportunity&amp;nbsp;to do just in time restore from the azure portal but it does not give you the possibility to trigger a full backup when you want. You can read more about it here:&lt;br /&gt;https://msdn.microsoft.com/en-us/library/azure/jj650016.aspx&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;There are ways to work around the problem with not being able to do a normal backup but they are a little more tricky. The way I solve it is to do a copy of query and by that I will get a copy of the database so I will get my backup but not as a backup file. This is ok but there are some money issue into it because the cost for another database is much higher than for storage area, but luckily this is Azure so we only pay for the things we use when we use it, so this automatic step comes with a manual step, and that is to create a &quot;backup file&quot; of the copy of the database by creating a&amp;nbsp;bacpac-file (&lt;a href=&quot;https://msdn.microsoft.com/en-us/hh213241.aspx&quot;&gt;https://msdn.microsoft.com/en-us/hh213241.aspx&lt;/a&gt;).&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So lets show how to do this in code.&lt;/p&gt;
&lt;p&gt;Start by upgrade your developer &lt;span&gt;environment&lt;/span&gt;&amp;nbsp;to the latest version of EPiServer. Run it locally and you will first see this error:&lt;/p&gt;
&lt;p&gt;The database &#39;EPiServerDB&#39; has not been updated to the version &#39;7019.0&#39;, current database version is ....&lt;/p&gt;
&lt;p&gt;This is expected &lt;span&gt;behavior&lt;/span&gt;, so do not worry.&lt;/p&gt;
&lt;p&gt;Start by changing your Episerver.Framework element tag to look like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;episerver.framework updateDatabaseSchema=&quot;true&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then somewhere in your solution (&lt;span&gt;preferably&lt;/span&gt; in the folder where you have any other&amp;nbsp;Initialization stuff) create a new class that&amp;nbsp;implements&amp;nbsp;IDatabaseSchemaValidator. It should on first creation look something like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.Configuration;
using EPiServer.Data.SchemaUpdates;

namespace AutomaticSchemaUpdateWithAzureDBBackup.Business.Initialization
{
    public class BackupDatabaseValidator : IDatabaseSchemaValidator
    {
        public bool IsDatabaseUpdateAllowed(ConnectionStringSettings connectionStringSettings)
        {
            throw new NotImplementedException();
        }

        public void BeforeUpdating(ConnectionStringSettings connectionStringSettings)
        {
            throw new NotImplementedException();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Important!&lt;br /&gt;You also need to tell StrutureMap to use your class, do that by adding something like this to your dependency setup:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;container.For&amp;lt;IDatabaseSchemaValidator&amp;gt;().Use&amp;lt;BackupDatabaseValidator&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see there are two functions that needs to be implemented. IsDatabaseUpdateAllowed that will tell EPiServer if it is allowed to update the schema or not, and BeforeUpdating that can do what ever you want and that is happening before the update of the schema &lt;span&gt;occur&lt;/span&gt;, so this is here we will put our logic to backup the database.&lt;/p&gt;
&lt;p&gt;Start by allowing the schema to be &lt;span&gt;automatically&lt;/span&gt;&amp;nbsp;&lt;span&gt;updated&lt;/span&gt; by changing IsDatabaseUpdateAllowed to always return true.&lt;/p&gt;
&lt;p&gt;Then in BeforeUpdating we start by checking if the database is in Azure, and that we do by looking at the address to the database like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var sqlConStrBuilder = new SqlConnectionStringBuilder(connectionStringSettings.ConnectionString);
if (sqlConStrBuilder.DataSource.ToLowerInvariant().Contains(&quot;.database.windows.net,&quot;))
{
    //Do action here
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a pretty simple check and might not be bulletproof but for now all connectionstrings I have used to Azure SQL Database have that string in the server-path.&lt;/p&gt;
&lt;p&gt;Right now you should be able to get your development &lt;span&gt;environment&lt;/span&gt;&amp;nbsp;to work, so if you compile and run the site, the error message should be gone.&lt;/p&gt;
&lt;p&gt;To the problem with Azure and backup. To be able to do a copy of the database you need to be dbo in the database you are copying from and you also need to be able to create databases and hopefully the user you are using do not have that access rights (otherwise you are pretty much running your site with the sa account and that are a big no no!!!) so we need to add another connectionstring to the site. The important thing here is that you do not write any sensitive information in your web.config because you will not need it. Instead you add this connectionstring to the config tab of your site in Azure and by doing that, the information are protected and will not be visible to anyone (Azure are replacing the connectionstrings you define in web.config with the one from the config-tab in runtime).&lt;/p&gt;
&lt;p&gt;After doing that we update the class so it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.Configuration;
using System.Data.SqlClient;
using EPiServer.Data.SchemaUpdates;

namespace AutomaticSchemaUpdateWithAzureDBBackup.Business.Initialization
{
    public class BackupDatabaseValidator : IDatabaseSchemaValidator
    {
        public bool IsDatabaseUpdateAllowed(ConnectionStringSettings connectionStringSettings)
        {
            return true;
        }

        public void BeforeUpdating(ConnectionStringSettings connectionStringSettings)
        {
            var sqlConStrBuilder = new SqlConnectionStringBuilder(connectionStringSettings.ConnectionString);
            if (!sqlConStrBuilder.DataSource.ToLowerInvariant().Contains(&quot;.database.windows.net,&quot;)) return;

            var azureConnectionWithCreateDbAccessRights = ConfigurationManager.ConnectionStrings[&quot;azureConnectionWithCreateDbAccessRights&quot;];
            var episerverConnectionString = ConfigurationManager.ConnectionStrings[&quot;EPiServerDB&quot;];
            if (azureConnectionWithCreateDbAccessRights != null)
            {
                var builder = new SqlConnectionStringBuilder(episerverConnectionString.ConnectionString);
                using (var connection = new SqlConnection(azureConnectionWithCreateDbAccessRights.ConnectionString))
                {
                    connection.Open();
                    var queryString = string.Format(&quot;CREATE DATABASE [{0}_{1}] AS COPY OF [{0}]&quot;, builder.InitialCatalog, DateTime.UtcNow.ToString(&quot;yyyyMMdd_HHmmss&quot;));
                    var command = new SqlCommand(queryString, connection)
                    {
                        CommandTimeout = 600
                    };
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you look at this, I set the timeout pretty high because it can take a while to get the response back. The command are said to be async and are supposed to return fairly quickly but in my tests with a alloy site it has taking between 1 and 2 minutes for it to &lt;span&gt;respond&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Then all you have to do is wait...... So, doing this on a staged eviroment&amp;nbsp;is good, since the upgrade will take longer time to complete because it need to also copy a database.&lt;/p&gt;
&lt;p&gt;When all this are done and you see that it is all working, create a bacpac file of your copy and save it and then delete the database so you do not need to pay for it any more.&lt;/p&gt;</id><updated>2015-05-27T22:50:40.7630000Z</updated><summary type="html">Blog post</summary></entry></feed>