Category Archives: EPiServer CMS 5

Error in UI-path when upgrading from EPiServer 5 to 6

The other day we were upgrading a site for a customer from EPiServer 5 to EPiServer 6. In the upgrade process an exception was thrown telling us:

At C:\Program Files (x86)\EPiServer\CMS\6.0.530.0\Upgrade\System Scripts\Upgrade Site (SqlServer).ps1:361 char:29

+                  $newUiUrl = $uiUrl.Replace <<<< ($uiPath, $uiPath+”/CMS”)

System.Management.Automation.ParentContainsErrorRecordException: Cannot convert argument “1”, with value: “/CMS”, for “Replace” to type “System.Char”: “Cannot convert value “/CMS” to type “System.Char”. Error: “Strängen måste vara exakt ett tecken lång.””

After some research we realized that the uiUrl in episerver.config was set to https://www.somedomain.com/ui/ and the upgrade script didn’t like it and most likely expected the value to be ~/ui/.

The solution was to change it to ~/ui/ during upgrade and then change it back.

Creating classes for PageTypeBuilder in an existing project

Update: This can now be found on GitHub: https://github.com/eriknordin/PTB-Helpers

While everyone else is creating cool gadgets for the new site center I was appointed to update an “old” site with some new functionality. And since PageTypeBuilder was introduced in my EPiServer life, and don’t feel like working without it in any project. If this would’ve been a small project I would probably just created the PTB classes by hand, but after looking into the page type list I was freaked out. Therefore I spent some time creating a “plugin” (it’s not a  real plugin, just “some” code) that creates PageTypes classes from all the page types and properties in the current project.

Step 1
The first step is to download the source code, it’s a zip-file containing a .aspx/.designer/.cs
Download it here

Step 2
Include the files in your project.

Step 3
Open you web browser and surf to the page.

Step 4
There are a few values that yo can change before doing the import/export (it’s up to you to define what it is :)).
You can add a prefix before and after the class name, change the base class that the classes should extend, enter a namespace (required) and finally decide where to put the files.
ptb-classes1

You can also exclude page types that you don’t want to include.
ptb-classes2

And finally you can decide which namespaces that should be included by default.
ptb-classes3

Step 5
When clicking “Create classes” you will see a log of classes created plus if something is excluded.
An example is that every propertyname with a “-” in it will be excluded because you can’t have “-” in a property name in C#.
When the files are created you just have to include them in your project and you should be good to go. You have to remember to include a reference to PageTypeBuilder, and you also need to change so all PageTypes .aspx.cs files inherits from the right class.

One other thing that should be mentioned is that PageTypes names like “[Something] Page Type Name” will end up like [yout catalog]\Something\PageTypeName.cs (if you don’t have any prefix).

Update – 091103 10:23
1. Discovered a bug and updated the source files.
2. Before using this, backup you database, this code is used without any warranty.
3. If you want to extend the functionality, please feel free to do it.

Update – 100128
DefaultValueType was not set which lead to some problems. Fixed.

PageTypeBuilder – makes developing with EPiServer funny

As you may have noticed over the last month, Joel Abrahamsson has created a module to EPiServer called PageTypeBuilder. In a short explanation PageTypeBuilder brings joy into developing with EPiServer! No more need for ranting around in admin mode, no more need of synchronizing page types between dev – test- stage – live! You get stronged typed access to all properties defined, you can inherit page types, you can also very easy create your own package with page types and reuse it in another project. Anyway, Joel explains it all on his own blog in four different blog posts.

So why am I writing this post when Joel explains it all?

First of all, I want to spread the PageTypeBuilder – I can guarantee that you will enjoy working with EPiServer much much more with this module.

Just a small thing like this code makes me happy.


public T1 GetDefaultPageData<T1>(PageReference parent) where T1 : TypedPageData
{
int? id = PageTypeResolver.Instance.GetPageTypeID(typeof(T1));
if (!id.HasValue)
{
throw new Exception("Could not find PageTypeID for class " + typeof(T1).ToString());
}
return (T1)DataFactory.Instance.GetDefaultPageData(parent, id.Value);
}

With this code you can create a new page of any page type you have defined with the PageTypeBuilder without having to store the id or the name of the page type in some settings page/web.config. You just need to use this nice line:


MyPageType myPage = GetDefaultPageData<MyPageType>(pageRefParent);

Code snippets

Another reason for writing this post is to say that I’ve updated the code snippets I’ve blogged about earlier.

I’ve added two snippets:

  • pt – which is the attribute for the class
  • ptppage – page reference property

I’ve also updated all the old snippets to include EditCaption and HelpText, so it’s a bit more user friendly now.

You can download the new zip-package here

Unzip the files into “My Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets”.

Update: Code Snippets to PageTypeBuilder

UPDATE: The code snippets are updated, read more in this blog post.

Since the way of including properties in PageTypeBuilder has been changed, so has the snippets.

This ZIP-package includes nine different snippets:

  • ptpbool – Selected/Not selected
  • ptpdate – Date
  • ptpimage – URL to image
  • ptpnumber- Integer
  • ptpobject – You decide.. 🙂
  • ptpstring – Short string
  • ptpurl – URL
  • ptpxhtmlstring – XHTML with properties
  • ptplongstring – XHTML string with no properties

Click here to download the snippets, unzip the files into “My Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets”

By the way, I found a better tool for making snippets, even though it’s called VB Snippet Editor, it handles C# snippets aswell 🙂 You can find it here: http://billmccarthy.com/Projects/Snippet_Editor/

Code Snippets to PageTypeBuilder

UPDATE: These snippets are out of date, download from this post instead

For you who have developed with the open source CMS N2 CMS, I guess you fell in love in how you are in control over the page type properties. And as a few of you have might notice the newly announced EMVP Joel Abrahamsson has found a really nice way to achieve this with EPiServer.

To create a property there are a few lines of code, and sometimes you might be to lazy to write these lines.. And as you may know there is a great thing in Visual Studio called Snippets that can help you out. A snippet is a XML-based file, and if you are lazy as I am, you probably want some help when you create your own snippets. I can recommend using a program called Snippy.

Here is a step by step how to create your own snippet. In this example I will create a snippet for a “Short string property” to the PageTypeBuilder earlier mentioned. (if you just want to download the snippets, check at the bottom of this post)

  1. Download and start Snippy
  2. Start by entering some information about the snippet.
    snippet_1
    The shortcut field is what you have to write in Visual studion to use the snippet.
  3. In the Code-area, change language to csharp and paste the code that you would like to make a snippet of. In my case I will use the code to create a definition for a Short string.
    [PageTypeProperty(SortOrder = 100, UniqueValuePerLanguage = true, Searchable=true, Type = typeof(PropertyString))]
    public string Headline
    {
    get
    {
    return GetPropertyValue<StartPageType, string>(page => page.Headline);
    }
    
    set
    {
    SetPropertyValue<StartPageType, string>(page => page.Headline, value);
    }
    }
    

    snippet_2

  4. If we save the snippet now, we would actually have a functional snippet, but there a few things that we would like to change..
    In this snippet I would like to change SortOrder, UniqueValuePerLangugae, Searchable, Headline(PropertyName) and the StartPageType(which pagetype this property should be saved/read to/from)
    In “Literal & Objects” we can define so these things mentioned above could be changed by just tabbing around when we are using our snippet.
  5. Let’s start with SortOrder. Press the “Add…”-button.
    • ID: the “variable”-name that we will refer to in the code-area
    • Tooltip: a little help-text when you are using the snippet
    • Default value: when using the snippet and you ignore this part you get the default value
    • Function: There are a few predefined functions you can use, we will use one later to retrive the class name of the page type. You can read more about the functions here: http://msdn.microsoft.com/en-us/library/ms242312(VS.80).aspx
    • Declaration type: Yo can read about the difference here: http://msdn.microsoft.com/en-us/library/ms165396(VS.80).aspx. We will use Literals.
    • Editable: Everything except the PageType should be editable by the user

    This is how the SortOrder should look like.
    snippet_4

    And when we refer to it in the code area we use the ID surronded with $-signs, like $SortOrder$.
    snippet_5

  6. Continue with the others, the only special one is the PageType. For this one we need to use a function called ClassName() and the Editable-box should not be checked.
    snippet_6
  7. This is how it should look like when you are done:
    snippet_7

    snippet_8
    As yo can see the PropertyName is used several times, but you will only need to enter it once.

  8. Now save your snippet. I think the right directory will choosen, if not it should be saved in “My Documents\Visual Studio [2005/2008]\Code Snippets\Visual C#\My Code Snippets”
  9. Open up visual studio, and in a class start typing the shorcut name given, and it will be shown in the intellisene.
    snippet_9
  10. Press Tab, Tab and you can now use your snippet.
    snippet_10

I’ve only created three snippets so far. You can download them here! (Unzip in “My Documents\Visual Studio [2005/2008]\Code Snippets\Visual C#\My Code Snippets”)

Feel free to contribute with your own snippets to the PageTypeBuilder, I will probably update this blog post as a create new ones.

My Documents\Visual Studio [2005/2008]\Code Snippets\Visual C#\My Code Snippets

Scheduled tasks – tips

Here are a few tips about scheduled tasks that may spare you some debugging and angry moments.

First of all, if you are interested in how you create a simple scheduled job, please check out Ted Nybergs post.

Several developers / Load balancing environment

If the same site is up’n’running on several computers/servers but with a shared database, the scheduled job might run at the “wrong” machine. There is a setting in web.config which controls if the scheduler should be run by this instance or not. It’s an attribute to <siteSettings> called “enableScheduler”, eg. <siteSettings enableScheduler=”false” …. /> if you don’t want the scheduler to run in this instance/machine.

Another way, not to tell if the job should be executed or not, but if you want to know which computer that executed the job, is to return the computer name with the return message. This can save you a lot of frustration when are about to pull you hair of cause the changes you make in to code doesn’t affect to job. 🙂

return "[Computer: " + System.Environment.MachineName + "] " + returnMessage;

Object reference not set to an instance…
Sometimes when you work with scheduled jobs, you can get this error, even though the only thing your job does is to return a “hellow world”. I’ve experienced this a few times, but never put much effort in to find out what causes this.
However, you can easily go around this by deleteing your job and create a new one with a slightly different class name.

Impersonate
In some cases you might need to run to job as a certain user.
Specially when you want to use FindPagesWithCriteria in EPiServer CMS 5 R2. It ignores the AccessControlList and only fetches pages that the current user has access rights to read. In R2 SP1 there is a new method called FindAllPagesWithCriteria which is supposed to find all pages despite access rights.
Well, this is how you login:

[ScheduledPlugIn(Description = "Test job with impersonated user", DisplayName = "Test job")]
public class TestJob
{

public static string Execute()
{
string returnMessage;

if (EnterImpersonatedState("erik.nordin", "password"))
{
returnMessage = "Job executed with user " + PrincipalInfo.CurrentPrincipal.Identity.Name;
LeaveImpersonatedState();
}
else
{
returnMessage = "Failed to login";
}
return "[Computer: " + System.Environment.MachineName + "] " + returnMessage;
}

private static IPrincipal prevPrincipal;
private static bool EnterImpersonatedState(string userName, string password)
{
IPrincipal impersonatedUser = null;

// If you don't want to validate the user, you can use skip the if-statement:
if (Membership.ValidateUser(userName, password))
{
impersonatedUser = PrincipalInfo.CreatePrincipal(userName);
}

if (impersonatedUser == null)
{
return false;
}

prevPrincipal = PrincipalInfo.CurrentPrincipal;
PrincipalInfo.CurrentPrincipal = impersonatedUser;

return true;
}

private static void LeaveImpersonatedState()
{
PrincipalInfo.CurrentPrincipal = prevPrincipal;
}
}

EPiServer:PageList with ItemDataBound

In my last post I explained how you can extend your episerver:pagelist with your own templates (i.e. SelectedItemTemplate, AlternatingItemTemplate). In this post I will show you how to extend your PageList so you can use ItemDataBound. This is very useful if you want to use asp:controls in your templates and then populate them from code behind.

In this post I will start with the code from my last post and extend it further.

1. Creating PageListEventArgs
First of all we need to create our own EventArgs by extending EventArgs. In this case we need to access to the PageData that is bound to the Template, and we also need access to the Template which should be rendered. So this is the class we need:

public class PageListEventArgs : EventArgs
{
public PageData DataItem { get; set; }
public Control ControlItem { get; set; }
}

If you look at the RepeaterItemEventArgs, which is used by the asp:Repeater, you can get some ideas what else that can be useful in this EventArgs.

2. Creating PageListEventHandler
This is where we create our Eventhandler that will use the PageListEventArgs we just made. It’s just two lines of code:

[Serializable]
public delegate void PageListEventHandler (object sender, PageListEventArgs e);

3. Add PageListEvenHandler to the PageList
Ok, now that we have made an EventHandler we need to use it somehow. So, in the PageList we add the PageListEventHandler:

public event PageListEventHandler ItemDataBound;

By doing this, we can now add events to our PageList. We now need to make sure that the event is triggered when it should. So let’s jump into CreateChildControls, and find where the Item/SelectedItemTemplate is added. Right below the

ItemTemplate.InstantiateIn(control2);

we add

if (ItemDataBound != null)
{
PageListEventArgs args = new PageListEventArgs();
args.ControlItem = control2;
args.DataItem = pages[i];
ItemDataBound.Invoke(this, args);
}

When this is done, we have the option to add the ItemDataBound-event to our PageList!

This is how you do it:

<Antecknat:PageList ID="PageList1" PageLinkProperty="PageLink" OnItemDataBound="PageList1_ItemDataBound" runat="server">
<ItemTemplate>
<asp:Literal ID="Literal1" runat="server"></asp:Literal><br />
</ItemTemplate>
</Antecknat:PageList>

or from code behind:

PageList1.ItemDataBound += new Antecknat.Web.WebControls.PageListEventHandler(PageList1_ItemDataBound);

And to modify the controls in the current template:

protected void PageList1_ItemDataBound(object sender, <span>Antecknat.Web.WebControls</span>.PageListEventArgs e)
{

Literal l = e.Item.FindControl("Literal1") as Literal;
if(l!=null)
l.Text = e.DataItem.PageName;
}

EPiServer:PageList with SelectedItemTemplate

This is a quick guide how to extend EPiServer:PageList with a SelectedItemTemplate. This is a good start if you want to try to extend it with a SeparatorTemplate or something more spectacular.

1. Create a class
I.e. PageList.cs, a recommendation is to follow EPiServers namespace, so in this case I would create my class in Antecknat.Web.WebControls.
This class should inherit EPiServer.Web.WebControls.PageList

namespace Antecknat.Web.WebControls
{
public class PageList : EPiServer.Web.WebControls.PageList
{

2. Create the SelectedItemTemplate
This is done by the following code:

private ITemplate _selectedItemTemplate;
[TemplateContainer(typeof(PageTemplateContainer)), Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate SelectedItemTemplate
{
get
{
return this._selectedItemTemplate;
}
set
{
this._selectedItemTemplate = value;
}
}

3. Modify CreateChildControls()
With Reflector (the best program ever made?) we can reuse the way controls are added to the regular PageList. We need to change the current method so that the SelectedItemTemplate is used when is supposed to.
In this case I want the current page, the parent page, the parents parents page and so on to count as selected. So we’ll make a PageReferenceCollection containing these pages.

// Find pages that should be marked as Selected
PageReferenceCollection prcActivePages = new PageReferenceCollection();
PageReference prCurrentLevel = CurrentPage.PageLink;
while (prCurrentLevel != null && prCurrentLevel != PageReference.EmptyReference)
{
prcActivePages.Add(prCurrentLevel);
prCurrentLevel = EPiServer.DataFactory.Instance.GetPage(prCurrentLevel).ParentLink;
}

And where the regular ItemTemplate is added, we change the code from:

ItemTemplate.InstantiateIn(control2);

to:

if (prcActivePages.Contains(pages[i].PageLink)) // Add to SelectedItemTemplate if in Selected collection
SelectedItemTemplate.InstantiateIn(control2);
else // Add to normal ItemTemplate
ItemTemplate.InstantiateIn(control2);

4. Change web.config
This is not a mandatory step, but if you always want to be able to use your fresh PageList from any aspx or ascx in the project, add teh following line in <system.web><pages><controls>

<add tagPrefix="Antecknat" namespace="Antecknat.Web.WebControls" assembly="Antecknat"/>

5. Use your PageList
Now you can use your PageList in your .aspx and .ascx files.
Just type

<Antecknat:PageList ID="PageList1" runat="server>
<HeaderTemplate><ul></HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><li><%# Container.CurrentPage.PageName %></li>
<SelectedItemTemplate><li class="selected"><%# Container.CurrentPage.PageName %></li>
</Antecknat:PageList>

And then add a PageDataCollection from code behind and databind it.

Download sourcecode
PageList.zip

Implementing the FCKEditor

One of our customers really needed an editor that would work for FireFox when they are Mac-users, so I tried to implement the FCKEditor which EPiServer has an howto about: http://world.episerver.com/Download/Code-Samples/Editor-Tools/Editor-for-non-IE-browsers/.

I followed the steps in the article, but the editor only loaded about half of the times. Wierd, so I started debugging. No luck at all. Tried to deploy the project to our test-server and it worked all of the time.

Some problems/wanted features that I bumped into was:

  1. Got a Javascript error when trying to insert an EPiServer page
  2. The document tab was selected when i wanted to insert a EPiServer page
  3. When the property had no toolbar-options choosen(in admin) it still showed the rich text editor

Well, I really needed this to work ASAP so i made some modification to the EPiServer-FCKEditor project.

Solutions
1. In the WrapServerObjectBrowser.aspx I changed the content for the SelectPage-panel to this:

<script type="text/javascript"><!--
function OpenGetPageDialog(url, selectedpage, value, text, lang)
{
EPi.CreatePageBrowserDialog(url, selectedpage,'False','False', text, value, lang, enddialog, null);
}
// --></script>
<input id="PageAddText" disabled="disabled" type="text" Runat="server" />
<input type="button" value="Select page" onclick="OpenGetPageDialog('<%= EPiServer.UriSupport.ResolveUrlFromUIBySettings("edit/pagebrowser.aspx") %>','', '<%= PageUrlValue.ClientID %>', '<%= PageAddText.ClientID %>', 'en');" />
<script type="text/javascript"><!--
OpenGetPageDialog('<%= EPiServer.UriSupport.ResolveUrlFromUIBySettings("edit/pagebrowser.aspx") %>','', '<%= PageUrlValue.ClientID %>', '<%= PageAddText.ClientID %>', 'en');
// --></script>

2. In the WrapServerObjectBrowser.aspx.cs OnInit-method I added these lines

if (!IsPostBack)
{
if (Request.QueryString["type"]!= null)
actionTab.SetSelectedTab(Request.QueryString["type"]);
}

3. In PropertyLongStringControlAdapter.cs, declared a TextBox, checked if the property had any settings choosen and then choosed if to use the textbox or the FCKEditor. When the proeprty was saved I had to do the check again to know wich webcontrol to fetch the value from.

protected WrapFCKEditor editor;
protected TextBox textbox;

public override void ApplyEditChanges()
{
if (((PropertyLongString)this.PropertyData).EditorToolOptions == 0)
this.PropertyData.Value = textbox.Text;
else
this.PropertyData.Value = editor.Value;

}

public override void CreateEditControls()
{
if (((PropertyLongString)this.PropertyData).EditorToolOptions == 0)
{
textbox = new TextBox();
textbox.TextMode = TextBoxMode.MultiLine;
textbox.Rows = 10;
textbox.Columns = 40;
textbox.Text = this.PropertyData.Value as string;
this.Control.Controls.Add(textbox);
}else{
// display the editor...
}
}

Using XForms to store information

At the moment I’m adding some functionallity to vinsprit.se, so that a visitor can add products, that aren’t in the regular assortment, to a cart and by a simple click send this cart by fax to the closest Systembolag. A pretty nice feature. The customer also wanted to store the orderinfo in episerver if something goes wrong, so I thought that you must be able to do that with XForms. And yes, it was very simple.

First of all I added a “XForms form”-property to the pagetype. Then I made an empty Form. The rest is handled from code behind. The following example works both with 4.6 and CMS 5. Hopefully in CMS 5 R2 aswell.. 🙂

// Get guid for the form and creates instance
XForm form = XForm.CreateInstance(new Guid((String)CurrentPage["XFormPropertyName"]));
// This is where data is stored
XFormData form_data = form.CreateFormData();
//We want this info related to the order page
form_data.PageId = CurrentPage.PageLink.ID;
// We want it saved in the database
form_data.ChannelOptions = ChannelOptions.Database;

// Here we add the values.
form_data.SetValue("Key 1", "some value");
form_data.SetValue("Key 2", "some other value");
// and saves it
form_data.Send();

Pretty nice if you want to avoid creating your own sql-table or some other way to store data.