Frederik Vig – ASP.NET developer

Follow me

Archive for May 2010

EPiServer Developer Resources

Last updated 05.07.2010

Table of Contents

  1. Report Center
  2. Mobile
  3. Bugs and where to find help
    1. FAQ
  4. Subscription
  5. Categories
  6. Import / Export
  7. Content Channels
  8. URL rewriting / friendly URL
  9. TinyMCE / Editor
  10. XForms
  11. Globalization / localization
  12. File system / VPP
  13. Security, Membership and roles
  14. Events
  15. Oracle
  16. SEO
  17. Workflows
  18. Errors, Logging and debugging
    1. Logging
    2. Errors and Error handling
    3. Debugging
  19. Configuration
  20. Getting started guides and tutorials
  21. Checklists
  22. SDK and API documentation
  23. Guides
  24. EPiServer Framework
    1. Online Center
      1. Gadgets
    2. Dynamic Data Store
      1. Page Objects
      2. Dynamic Data Store
    3. Initialization System
  25. Modules
    1. Modules and deployment center
    2. Open Source Modules
      1. Templates
      2. Gadgets
      3. Page Providers
      4. Virtual Page Providers
      5. Google Analytics and SEO
      6. Dynamic Content
      7. Images, Image galleries and slideshows
      8. Google Map
      9. Properties
      10. Language, Globalization and Localization
      11. Reports
      12. Debugging and diagnostics
      13. Frameworks
      14. Performance and caching
      15. RSS and feeds
      16. Edit mode enhancements
      17. Blog
      18. Various modules
      19. Enhancements for editors
      20. Rich Text Editor enhancements
    3. EPiServer Composer
    4. EPiServer Community
      1. Tech-notes
      2. More resources
    5. EPiServer Commerce
    6. EPiServer Relate+
    7. EPiServer Mail
    8. EPiServer CMO
    9. EPiServer Connect for CRM
    10. EPiServer Connect for SharePoint
  26. Extending EPiServer
    1. Plugins
    2. Custom Property
    3. Dynamic Content
    4. Scheduled Jobs
  27. EPiServer Enterprise
    1. Load Balancing
    2. Server Architecture
    3. EPiServer Enterprise
    4. Mirroring
    5. Page Providers
  28. Developing with EPiServer
    1. Web Controls
    2. Page Types and PageData
    3. Properties / Dynamic Properties
    4. Code architecture
    5. Searching and filtering
  29. Performance and caching
    1. Caching
    2. Performance
  30. EPiServer Quick Publishing

Report Center

EPiServer’s Report Center comes with 6 predefined reports.

  • Not Published Pages
  • Published Pages
  • Changed Pages
  • Expired Pages
  • Simple Addresses
  • Link Status

For a short guide to using the existing reports see the guide: EPiServer CMS R2: Report Center.

To get started developing your own reports see: Create Your Own Reports in the Report Center and PageName vs UrlSegment Report.

Mobile

Bugs and where to find help

Often the first thing I do when I’m stuck on a problem is to go to EPiServer’s bug list and do a quick search to see if by problem might be reported in as a bug. Next up after that is Google, if I cannot find an answer there I try to post a clear description of my problem on the forum or create a support ticket.

FAQ

Not sure if it’s still in use but EPiServer World’s FAQ still contains some valuable information.

Subscription

EPiServer Subscription is used for sending email updates to users when new content is published.

These articles should at least give you a little information about EPiServer’s subscription functionality. For some sample code see the Public Templates under Pages and SubscriptionPage.aspx.

Categories

Like with Subscription there is not much documentation and blog posts about EPiServer’s built in category functionality. I’ve started using categories more for tagging pages, which I then use to filter search pages and list pages.

Here are a few resources on categories.

Import / Export

EPiServer has built in functionality for importing and exporting pages, files, categories, page types, and more. This creates an .episerverdata file that you can open up with a program like WinRar and see its content, which consists of a bunch of xml files.

Content Channels

Content Channels are used for pushing external data into EPiServer. This is for instance used by the SharePoint connector for pushing data into EPiServer from SharePoint.

For a nice introduction see:  Pushing Data into EPiServer CMS 5 via Content Channels, there is also the video: Content Channel and Custom Page Store in EPiServer CMS 5.

URL rewriting / friendly URL

Both for users and search engines it helps to use friendly urls. Since EPiServer’s URL rewriter is provider based, it is easy to replace it with your own implementation.

Below are a couple of resources for changing the built in functionality of EPiServer’s URL rewriter. I recommend using Reflector to learn more about the inner workings of the URL rewriter (in EPiServer.dll and the EPiServer.Web namespace).

TinyMCE / Editor

With the rich text editor we want to add custom styles to the content and add new functionality and plugins, below are a couple of links that should help you with this.

The default editor for EPiServer CMS 6 is TinyMCE, if you upgrade from EPiServer CMS 5 or use EPiServer CMS 5, EPiServer’s built in editor is used. You can change which editor you wish to use in your web.config file, see: XHTML Editor – PropertyXHTMLString and PropertyLongString for more information.

XForms

EPiServer XForm is used for creating user forms. EPiServer uses an editor for letting the editors of the site build the forms. As developers we can easily extend and attach our self to various events that get triggered when the forms is sent (as an email, stored in the database or both).

A trick to note when using the XForm editor is that ctrl + shift + c and ctrl + shift + v copies/pastes the markup generated by the XForm editor, allowing you to easily change it without needing to attach yourself to various events and change the markup there.

For some sample code on rendering the XForm on your site see the Public Templates and XForm.ascx and the XForm page type.

Globalization / localization

EPiServer has some powerful globalization and localization support, built on ASP.NET’s globalization and localization functionality. I recommend starting out with the Globalization tech-note. EPiServer uses xml files to store the language information inside the lang folder. When installing a new EPiServer site there are quite a few default language files that get installed with it, I recommend deleting all the files you do not use.

Also be sure to check out Manage Languages for keeping language files up to date and TranslateX for sending EPiServer CMS pages to/from a translation service.

File system / VPP

EPiServer uses Microsoft’s Virtual Path Provider for their file system. Each file has a version history and supports meta data properties for storing information like author, copyright and other information, see my post:  EPiServer File Manager and File Summary for information on how to add more meta data properties.

There has also been developed a module for storing the files in the database instead of on a file share or on the web server.

Security, Membership and roles

EPiServer uses ASP.NET’s membership and role provider model for their membership and roles. By default these providers ship with EPiServer:

  • OracleMembershipProvider
  • WindowsMembershipProvider
  • SqlServerMembershipProvider
  • ActiveDirectoryMembershipProvider
  • MultiplexingMembershipProvider

MultiplexingMembershipProvider forwards requests to the Windows- and SqlServerMembershipProvider, allowing for creating and storing users and roles in EPiServer’s database and using the Windows users and roles (this is the mostly used provider).

Events

Oracle

EPiServer supports Oracle databases and even comes with sample configuration settings in their config files.

SEO

Here are a few blog posts and modules with tips on making EPiServer even more SEO friendly.

Workflows

EPiServer uses Windows Workflow Foundation for their Workflows and even ship with 4 workflows:

  • Sequential Approval
  • Parallel Approval
  • Request for feedback
  • Ready for translation

See Enabling the EPiServer CMS Workflows for information on how to activate them.

Errors, Logging and debugging

Logging

Errors and Error handling

Debugging

Configuration

EPiServer stores a lot of its configuration settings in various .config files. Below are a few resources that’ll help you navigate through most of what you can configure.

Overview of the default config files that come with EPiServer (containing both ASP.NET and EPiServer settings).

  • web.config – The main configuration file for the application. Contains configuration for the ASP.NET API and some parts of the EPiServer CMS API.
  • episerver.config – The main configuration file for the EPiServer CMS API. Contains the basic settings for the EPiServer CMS site (or sites – in an enterprise installation).
  • episerverFramework.config – Contains mapping information describing which host addresses leads to a particular EPiServer CMS site.
  • connectionStrings.config – Contains a list of database connection strings

There are two further configuration files located in the application’s root folder. These two configuration files are separate and not related to the files listed above or each other.

  • episerverLog.config – Contains the log4net settings for the application, please see the log4net homepage for full information on configuration options.
  • fileSummary.config – An XForm defining the meta data properties attached to files that are uploaded to EPiServer CMS.

Getting started guides and tutorials

Checklists

SDK and API documentation

Guides

EPiServer Framework

EPiServer Framework consists of:

  • EPiServer OnlineCenter
  • Dynamic Data Store
  • Initialization System

For a good introduction see: Introducing EPiServer Framework.

Online Center

Gadgets

Dynamic Data Store

The Dynamic Data Store is a new feature that came with EPiServer CMS 6 and is essentially a place to store data that might not be best stored in a page property. XForms for instance are stored in the Dynamic Data Store.

Page Objects

Page Objects are .Net objects and collections that are associated with an EPiServer CMS page.

Dynamic Data Store

There are two things you need to read to understand most of the Dynamic Data Store. First the tech-note: Dynamic Data Store, and second the examples referred to in the tech-note: Dynamic Data Store Examples.

More resources

Initialization System

Modules

This section contains how to create module packages for the deployment center, open source modules, and other EPiServer modules.

Modules and deployment center

The deployment center allows us to easily install new packages on existing sites. A package is a zip file with the necessary files that the modules needs along with configuration settings that need to be added to the various configuration files.

Open Source Modules

Most of the open source modules out there are hosted on either EPiCode or CodePlex. Here is a list of the various open source modules that I know about.

Templates

Gadgets

Page Providers

Virtual Page Providers

Google Analytics and SEO

Dynamic Content

Images, Image galleries and slideshows

Google Map

Properties

Language, Globalization and Localization

Reports

Debugging and diagnostics

Frameworks

Performance and caching

RSS and feeds

Edit mode enhancements

Blog

Various modules

Enhancements for editors

Rich Text Editor enhancements

EPiServer Composer

EPiServer’s Composer module is used to add extra functionality to EPiServer CMS pages, allowing editors to drag and drop functionality on predefined placeholders.

EPiServer Community

EPiServer Community is a framework for building online communities and consists of modules for user management, video galleries, chat, blogs etc.

The EPiServer Community SDK has a great howto section that covers a lot of the topics you as a developer will come across when working with EPiServer Community.

Joel Abrahamsson has also written a great series on creating new modules for EPiServer Community:

Tech-notes

More resources

EPiServer Commerce

Before trying to install EPiServer Commerce read the release notes and the section “Known Limitations”.

EPiServer Relate+

EPiServer Relate+ consists of a set of templates that builds on EPiServer Community and EPiServer CMS, and gets you started with a sample site.

EPiServer Mail

EPiServer Mail is used for sending emails and newsletters and is used by EPiServer Community and EPiServer Relate+.

EPiServer CMO

EPiServer CMO is used for monitoring and optimizing pages on your website by learning about your users’ behavior and helping your editors easily create A/B tests.

EPiServer Connect for CRM

EPiServer Connect for SharePoint

Extending EPiServer

One of EPiServer’s biggest strengths in my humble opinion is how easy EPiServer is to extend. Take a look at this picture by Deane Barker for a great overview of what is possible.

Plugins

Custom Property

Dynamic Content

Scheduled Jobs

EPiServer Enterprise

Load Balancing

Server Architecture

EPiServer Enterprise

Mirroring

Page Providers

Developing with EPiServer

Web Controls

Page Types and PageData

Properties / Dynamic Properties

Code architecture

Searching and filtering

Performance and caching

EPiServer has some great performance and caching out of the box (usually 90-95% of pages are served from the cache!). Below are some resources for improving it even more. For client side performance I recommend checking out YSlow as well.

Caching

Performance

EPiServer Quick Publishing

EPiServer Custom Property with Custom Settings

I was brushing up on custom properties and came across Allan Thræn’s post Custom Property: Category Drop Down. The code is simple and works great, but one thing I didn’t like is that you need to give the property the same name as the root category that you use to populate the drop down.

In EPiServer CMS 6 we now have the ability to add settings to the properties. Linus Ekström wrote a great post on how to use the new settings functionality: Settings for properties. After reading both Allan’s and Linus’ post I decided to give the EPiServer administrator the ability to choose which category should be the root category in the settings tab of the property.

Custom Property

using System;
using EPiServer.Core.PropertySettings;
using EPiServer.PlugIn;
 
namespace EPiServer.Templates.Properties
{
    [Serializable]
    [PageDefinitionTypePlugIn]
    [PropertySettings(typeof(CategoryDropDownSettings), true)]
    public class CategoryDropDown : EPiServer.Core.PropertyString
    {
 
        public override EPiServer.Core.IPropertyControl CreatePropertyControl()
        {
            return new CategoryDropDownControl();
        }
    }
}

The code is almost identical to Allan’s except for the PropertySettings attribute.

using System.Web.UI.WebControls;
using EPiServer.DataAbstraction;
 
namespace EPiServer.Templates.Properties
{
    public class CategoryDropDownControl : EPiServer.Web.PropertyControls.PropertyTextBoxControlBase
    {
 
        public override bool SupportsOnPageEdit
        {
            get
            {
                return false;
            }
        }
 
        protected DropDownList ddl;
 
        public override void CreateEditControls()
        {
            ddl = new DropDownList();
            CategoryDropDownSettings settings = (CategoryDropDownSettings)base.PropertyData.GetSetting(typeof(CategoryDropDownSettings));
 
            Category main_c = Category.Find(settings.RootCategory);
            ddl.Items.Add(new ListItem("", ""));
            foreach (Category c in main_c.Categories)
            {
                ListItem li = new ListItem(c.LocalizedDescription, c.Name);
                li.Selected = (((string)CategoryDropDown.Value) == c.Name);
                ddl.Items.Add(li);
            }
 
            this.ApplyControlAttributes(ddl);
            Controls.Add(ddl);
        }
 
        public override void ApplyEditChanges()
        {
            SetValue(ddl.SelectedValue);
        }
 
        public CategoryDropDown CategoryDropDown
        {
            get
            {
                return PropertyData as CategoryDropDown;
            }
        }
    }
}

Only difference to Allan’s code here is in the CreateEditControls method.

...
CategoryDropDownSettings settings = (CategoryDropDownSettings)base.PropertyData.GetSetting(typeof(CategoryDropDownSettings));
 
Category main_c = Category.Find(settings.RootCategory);
...

Custom Settings

using EPiServer.Core.PropertySettings;
 
namespace EPiServer.Templates.Properties
{
    [PropertySettingsUI(AdminControl = typeof(CategoryDropDownSettingsUI))]
    public class CategoryDropDownSettings : PropertySettingsBase
    {
        public int RootCategory { get; set; }
 
        public override IPropertySettings GetDefaultValues()
        {
            return new CategoryDropDownSettings { RootCategory = 0 };
        }
    }
}

I’m setting the default rootCategory to be 0 (root).

using System.Linq;
using System.Web.UI.WebControls;
using EPiServer.Core;
using EPiServer.Core.PropertySettings;
using EPiServer.Web.WebControls;
 
namespace EPiServer.Templates.Properties
{
    public class CategoryDropDownSettingsUI : PropertySettingsControlBase
    {
        private InputCategoryTree _categoryTree;
 
        protected override void CreateChildControls()
        {
 
            base.CreateChildControls();
 
            _categoryTree = new InputCategoryTree {ID = "CategoryTree"};
 
            Label label = new Label { Text = "Root Category", AssociatedControlID = "CategoryTree" };
 
            Controls.Add(label);
 
            Controls.Add(_categoryTree);
 
        }
 
        public override void LoadSettingsUI(IPropertySettings propertySettings)
        {
 
            EnsureChildControls();
 
            _categoryTree.SelectedCategories = new CategoryList(new[] {((CategoryDropDownSettings)propertySettings).RootCategory});
        }
 
        public override void UpdateSettings(IPropertySettings propertySettings)
        {
 
            EnsureChildControls();
 
            // We only want the first selected category
            ((CategoryDropDownSettings)propertySettings).RootCategory = int.Parse(_categoryTree.SelectedCategories.ElementAt(0).ToString());
        }
    }
}

When we add a new property we now have the option, under custom settings, to choose the root category for our drop down.

EPiServer Category Drop Down Settings

Which gives our editors these options.

EPiServer Category Drop Down in view mode

Creating a contact form with ASP.NET MVC

We’re going to create a contact form in ASP.NET MVC 2.0, that uses Ajax to send the form data and that uses client side validation to improve the user experience for our users.

Start by creating a new ASP.NET MVC 2.0 web application project in Visual Studio.

New project dialog in Visual Studio

This will create a new project with the default ASP.NET MVC 2.0 sample code.

Inside the Models folder create a new class, and give it the name: Contact.cs. The class is very simple with just some properties for Name, Email and Comment.

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
 
namespace ContactForm.Models
{
    public class Contact
    {
        [Required(ErrorMessage = "You need to fill in a name")]
        [DisplayName("Name")]
        public string Name { get; set; }
 
        [Required(ErrorMessage = "You need to fill in an email address")]
        [DataType(DataType.EmailAddress, ErrorMessage = "Your email address contains some errors")]
        [DisplayName("Email address")]
        public string Email { get; set; }
 
        [Required(ErrorMessage = "You need to fill in a comment")]
        [DisplayName("Your comment")]
        public string Comment { get; set; }
    }
}

Notice the Required, DisplayName and DataType attributes. This is called DataAnnotations, and is a new feature in ASP.NET MVC 2.0, which helps us add validation logic to our models.

Next step is creating the /home/contact URL and the contact view. Open up HomeController.cs, and add the following method.

public ActionResult Contact()
{
    return View();
}

Place your cursor inside View() and press Ctrl+M, Ctrl+V, or just right-click and choose Add View. Check the “Create a strongly-typed view” and type in: ContactForm.Models.Contact.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ContactForm.Models.Contact>" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Contact
</asp:Content>
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
    <h2>Contact</h2>
 
</asp:Content>

We can now create the form markup.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ContactForm.Models.Contact>" %>
 
<asp:Content ContentPlaceHolderID="TitleContent" runat="server">
	Contact us
</asp:Content>
 
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
    <% Html.EnableClientValidation(); %> 
    <% using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "Post", OnComplete = "Contact.onComplete"}))
        { %>
        <%: Html.ValidationSummary(true, "A few fields are still empty") %>
        <fieldset>
            <legend>Contact us</legend>
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Name) %>
            </div>
            <div class="editor-field"><%: Html.TextBoxFor(m => m.Name) %>
                <%: Html.ValidationMessageFor(m => m.Name) %>
            </div>
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Email) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Email) %>
                <%: Html.ValidationMessageFor(m => m.Email) %>
            </div>
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Comment) %>
            </div>
            <div class="editor-field">
                <%: Html.TextAreaFor(m => m.Comment, 10, 25, null) %>
                <%: Html.ValidationMessageFor(m => m.Comment) %>
            </div>
            <p>
                <input type="submit" value="Submit" />
            </p>
        </fieldset>
        <p id="result"><%: TempData["Message"] %></p>
    <% } %>
</asp:Content>

<%: … %> is just a shortcut for HTML encoding the data, a new feature in ASP.NET 4.0. <% Html.EnableClientValidation(); %> enables client side validation for us, and must be included right before the start of the form. Ajax.BeginForm is a helper method that generates the form tag for us and attaches a little JavaScript code for sending the form data with Ajax, if the client supports it, otherwise the form data will be sent like normal.

Lets create the JavaScript method that gets called when the form is finished. Create a new JavaScript file inside the Scripts folder and give it the name: Site.js. Add the Contact object with the property function onComplete.

var Contact = {
    onComplete: function (content) {
        var result = eval(content.get_response().get_object());
 
        var textNode = document.createTextNode(result.message);
 
        document.getElementById("result").appendChild(textNode);
    }
};

I take the JSON result from the server and add the message to the result paragraph.

If you press F5, the site should open up in your default browser. If you add Home/Contact behind the URL, you should be taken to the Contact Us page.

Contact us form

For the client side validation to work we need to reference a few JavaScript files. Open up Site.master (located in the shared folder), and add the following code right before the closing body tag.

...
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>
<script src="../../Scripts/Site.js" type="text/javascript"></script>
</body>
</html>

You should now receive some friendly error messages when you click the submit button without filling out the form properly.

Contact us form validation

Next step is adding the action method to HomeController.cs that will handle the form data.

[HttpPost]
public ActionResult Contact(Contact model)
{
        string message = "There are a few errors";
 
	if (ModelState.IsValid)
	{
                message = "Thanks! We'll get back to you soon.";
	}
 
	if (Request.IsAjaxRequest())
	{
		return new JsonResult { ContentEncoding = Encoding.UTF8, Data = new { success = true, message = message } };
	}
 
        TempData["Message"] = message;
 
	return View();
}

Notice that I check if the request is an Ajax request, if it is I return a JSON object with the result, otherwise I return the whole view with the message.

You would also add some other logic here for saving the message in some way.

The finished form

That’s it!

© Copyright Frederik Vig. Based on Fluid Blue theme