Showing posts with label EPiServer 5. Show all posts
Showing posts with label EPiServer 5. Show all posts

Wednesday, 16 September 2009

Custom styles in Rich Text Editor (part II)

It wasn't so long ago when I posted 'How to define custom styles in EPiServer's Rich Text Editor'. A few days ago I was following my own instructions to set up my custom styles and for absolutely peculiar reason Rich Text Editor didn't want to catch my settings! It took me a few hours to figure out what was going on.

Basically you can define custom styles in EPiServer by setting uiEditorCssPaths property in web.config:


which is the same as using System Settings in Admin mode:


But of course there are cases where it may not take any effect :)

If you encounter such case then check dynamic properties. You may have dynamic property called UIEditorCssPaths. If this property is set then other settings are ignored.


Tuesday, 1 September 2009

How to display Dynamic Content without Property control

Recently I was investigating EPiServer's Dynamic Content a bit. General impression is very positive, Dynamic Content is a great feature. From developer's point of view it's fairly easy to develop custom Dynamic Content, everything works like charm .... maybe beside one thing :)

Unfortunately, to display property's value with dynamic content replaced with our controls it's required to use EPiServer's control:

   1:  <EPiServer:Property PropertyName="MainBody" runat="server" />

Different approaches won't work.

   1:  <%= CurrentPage["MainBody"] %>
   2:  <%= CurrentPage.Property["MainBody"] %>
   3:  litMainBody.Text = CurrentPage.Property["MainBody"].ToWebString()



It is a considerable drawback as there are cases in which it's required to do something with property's value before displaying it:
  • For example you may want to alter all links by adding some extra parameters
  • When using Model-View-Presenter (flavor of MVC) you want to pass only pure data to the view.
It was worth spending some time to find a workaround. In the end, solution turned out to be not so convoluted, here is a control which takes a string and displays it replacing any dynamic content with corresponding controls.

   1:  public partial class DynamicContent : UserControlBase
   2:  {
   3:      public string StringToDisplay
   4:      {
   5:          set
   6:          {
   7:              var propertyControl = new PropertyLongStringControl
   8:                                        {
   9:                                            Page = Page,
  10:                                            PropertyData = new PropertyXhtmlString(value)
  11:                                        };
  12:              Controls.Add(propertyControl);
  13:          }
  14:      }
  15:  }

And the usage is really straightforward:

   1:  <unit:DynamicContent runat="server" id="dcMainBody" />
   2:  
   3:  dcMainBody.StringToDisplay = CurrentPage.Property["MainBody"].ToWebString();

Before calling dcMainBody.StringToDisplay you can of course change property's value in any arbitrary way.

Hope it will save someone a bit of time :)

Tuesday, 30 June 2009

EPiServer Composer - How to load content functions defined on a different page

This in another example of interesting EPiServer Composer (version 3.2.5) use case - on homepage there is a content area called "Bottom Area" which contains number of content functions. I want to display this whole content area on other arbitrary pages.

Here is a piece of code which can do this for us:

   1:  var structure = PageDataManager.LoadPageStruct(homepage.PageLink);
   2:  var contentArea = structure.GetContentAreaById(AreaName.BOTTOM_AREA_ID);
   3:  for (var i = 0; i < contentArea.ContentFunctions.Count; i++)
   4:  {
   5:      var data = contentArea.ContentFunctions[i];
   6:      var contentFunction = PageDataManager.InitContentFunctionControl(ExtensionGeneric.ViewMode.ExtensionNormalMode,
   7:                                                                       Page, data);
   8:      plhContainer.Controls.Add(contentFunction);
   9:      
  10:      var element = contentFunction as IListItemPosition;
  11:      if (element == null) continue;
  12:  
  13:      element.IsLastElement = IsLastElement(i, contentArea);
  14:      element.IsFirstElement = IsFirstElement(i);
  15:      
  16:  }

Explanation:
  1. In first two lines I'm using PageDataManager to get instance of ExtensionPageData class for homepage and then I can easily access required content area. Please note that to load data from a different page you just need to provide reference to a different page.
  2. Heaving an instance of ContentAreaData class (contentArea variable) I have access to the list of all content functions (contentArea.ContentFunctions)
  3. In lines 5-8 I'm instantiating each content function and then it can be added to the PlaceHolder which is a container for all content functions.
  4. In lines 10-14 I'm detecting first and last element as those elements may require some special CSS classes. You can read more about IListItemPosition interface and how to figure out which content function is first/last in my previous post.
To get everything working you still need those two simple methods:

   1:  private static bool IsFirstElement(int i)
   2:  {
   3:      return i == 0;
   4:  }
   5:  
   6:  private static bool IsLastElement(int i, ContentAreaData contentArea)
   7:  {
   8:      return i == contentArea.ContentFunctions.Count - 1;
   9:  }

I think it's fairly simple but if you have any questions don't hesitate to leave a comment.

Sunday, 28 June 2009

EPiServer Composer - How to figure out which content function is first (or last) within a content area

In my previous post (EPiServer-based site in 4 weeks?) I have presented number of quite high level thoughts regarding our last EPiServer project. In this and a few forthcoming post I would like to focus more on technical details and present several more interesting EPiServer Composer (version 3.2.5) use cases.

How to figure out which content function is first (or last) within a content area.

Imagine following piece of user interface:


It's an area where editors can define four arbitrary components. Components can be a text, image + text or just an image. Here is a typical html for this use case:

   1:  <div class="options">
   2:     <div class="option first"> ... </div>
   3:     <div class="option"> ... </div>
   4:     <div class="option"> ... </div>
   5:     <div class="option last"> .... </div>
   6:  </div>

Div with class options is a wrapper for all components. Each component has a class option. Quite typically in situation like this, first and last elements are a bit different. Depending on site design it's required to add some visual effects and that is why additional classes are added to the first and last element. This is very flexible approach as changes in html are minimal and final visual effect depends fully on CSS.

The problem for developer is that EPiServer Composer allows to simply drag and drop any number of content functions and each function is fairly autonomous. Content function is responsible for displaying html, it has no knowledge about context in which it was used. It doesn't know anything about content area to which it belongs, also it doesn't know anything about other content functions within the same content area. That is why to get information about context you need to operate one level above the functions.

But firstly, each content function need to implement following interface:

   1:  public interface IListItemPosition
   2:  {
   3:      bool IsLastElement { get; set; }
   4:      bool IsFirstElement { get; set; }
   5:  }

And now tricky part - changes on the higher level. A few things need to be added to the page/class which inherits from ExtensionBaseTemplate.

You need to override OnInit method:

   1:  protected override void OnInit(EventArgs e)
   2:  {
   3:      CurrentExtensionPageHandler.LoadContentArea += CurrentExtensionPageHandler_LoadContentArea;
   4:      CurrentExtensionPageHandler.LoadContentFunction += CurrentExtensionPageHandler_LoadContentFunction;
   5:  
   6:      base.OnInit(e);
   7:  }
Above code adds our handlers to the following events:
  • LoadContentArea - this way we will get information about all available content areas and what is more important - count of content functions for each area!
  • LoadContentFunction - Composer fires this event for each content function. Having a total number of content functions for each area we can easily detect first and last elements.

Here are implementations for the handlers:

   1:  #region ContentAreaFunctionCounter
   2:  
   3:  protected ContentAreaFunctionCounter ContentAreaFunctionCounter = new ContentAreaFunctionCounter();
   4:  
   5:  protected void CurrentExtensionPageHandler_LoadContentFunction(object sender, ExtensionPageEventArgs e)
   6:  {
   7:      var element = e.ContentFunction as IListItemPosition;
   8:      if (element == null) return;
   9:      
  10:      element.IsFirstElement = IsFirstElement(e);
  11:      element.IsLastElement = IsLastElement(e);
  12:  }
  13:  
  14:  private static bool IsFirstElement(ExtensionPageEventArgs e)
  15:  {
  16:      return e.ContentArea.Controls.Count == 0;
  17:  }
  18:  
  19:  private bool IsLastElement(ExtensionPageEventArgs e)
  20:  {
  21:      var numberOfElements = ContentAreaFunctionCounter.GetContentAreaFunctionCount(e.ContentArea.ID);
  22:      return e.ContentArea.Controls.Count == (numberOfElements - 1);
  23:  }
  24:  
  25:  protected void CurrentExtensionPageHandler_LoadContentArea(object sender, ExtensionPageEventArgs e)
  26:  {
  27:      var functionsCount = e.ContentAreaData == null ? 0 : e.ContentAreaData.ContentFunctions.Count;
  28:      ContentAreaFunctionCounter.SetContentAreaFunctionCount(e.ContentArea.ID, functionsCount);
  29:  }
  30:  
  31:  #endregion

Missing part is class ContentAreaFunctionCounter which is responsible only for keeping information about available content areas and functions count:

   1:  public class ContentAreaFunctionCounter
   2:  {
   3:      private readonly IDictionary<string, int> contentAreaFunctionCount = new Dictionary<string, int>(); 
   4:      
   5:      public void SetContentAreaFunctionCount(string contentArea, int functionCount)
   6:      {
   7:          contentAreaFunctionCount[contentArea] = functionCount;
   8:      }
   9:  
  10:      public int GetContentAreaFunctionCount(string contentArea)
  11:      {
  12:          if (contentAreaFunctionCount.ContainsKey(contentArea))
  13:          {
  14:              return contentAreaFunctionsCount[contentArea];
  15:          }
  16:  
  17:          return 0;
  18:      }
  19:  }

I would recommend creating a base class encapsulating this functionality. Each content function which needs to be aware of it's position just need to implement the IListItemPosition interface. And that's it! :)

If it's necessary you can easily extend IListItemPosition interface to contain int Position {get; set;} property and this way give all functions idea about it's position (not just first and last one).

Thursday, 18 June 2009

EPiServer-based site in 4 weeks?

I'm happy to share with you story of a EPiServer-based project that we have delivered a few weeks ago. I have been working on quite a few EPiServer projects but this one was special for number of reasons:
  • We had only four weeks to build the site.
  • EPiServer Composer was used to build almost whole functionality.
  • Extensive usage of Model-View-Presenter design pattern.
  • We have put into use idea which is getting more and more popular within Cognifide - Content First.
Let go though those main points in details ...


Four weeks deadline

Well, that was the biggest challenge, from the very first workshop with the client to the launch day we got only four weeks. Client provided initial designs, but actual PSDs were not ready yet. So we had to understand what client needs, build it and also give client a chance to enter actual content before the deadline. Avoiding incorrect assumptions and misunderstandings is always important but with time frame like this we knew that a single bigger misunderstanding will slow us down enough to miss the deadline. Therefore we decided to use following subset of SCRUM practices:
  • daily scrum meetings - everyday in the morning we were meeting and briefly talking about 1) progress which was made the day before, 2) about plans for given day and 3) about blockers. We considered something as blocker if something was stopping us from finishing the task. It could have been unclear requirement, lack of html template or simply tech problem (with EPiServer Composer for instance).
  • retrospective meetings after each iteration - we decided to have one week sprints as it was giving us 3-4 iterations and also 3-4 chances to stop for a while and think about improving our process. After each iteration we were also delivering the latest version to the client.
I think I can safely say that without daily meetings and retrospective delivering the site before the deadline would not be possible.


EPiServer Composer

Before that project we were mostly only playing with EPiServer Composer. Since the EPiServer Dev Summit 2008, when I had the chance to see X3 presentation, I always wanted to give it a try.
If you don't know anything about EPiServer Composer then check this video to get an idea how it can change the way you work with EPiServer:



What is my impression after this project?
  • It's great for small and medium sites, very intuitive, editors were able to understand it, learn it and use it very fast.
  • From developer's perspective impression is also positive. It's really great that we can build a function/component that is completely "standalone" and can be reused many times. Although, don't think that you can build for example single generic flash component and reuse it absolutely everywhere. If usability is your main objective then probably you are going to need more the one version of flash component. But still, I like to think about it this way: we can reuse generic flash component every time when it's needed to get something working quickly and then refine it if necessary.
Composer was sometimes giving us a hard time but now, when we know how to use it I will be happy to see more projects with Composer on board.


Model-View-Presenter

Another fairly new thing for us. We felt that separation of concerns which code behind files offer is not sufficient. In order to get loosely coupled architecture which is later easy to maintain we decided to use MVP design pattern. We don't have any hard, unbiased metrics which can prove it, but my feeling is that MVP gives much better separation, which affects things like:
  • much higher code reusability rate
  • much easier refactoring
  • because presentation logic is separated from controls it's much easier to spot common parts of code that should be extracted
  • presentation logic has no dependency on ASP.NET runtime therefore it can be unit tested (more on that in one of my old posts - TDD with EPiServer)
  • it's easier to catch what given module is doing, code readability is increased
Before that project MVP was an interesting design pattern for me, now I know that it works in practice. Try it once and you won't be able to live without it ;)


Content First

Content First is almost a buzz word within the Cognifide. But seriously, it really makes sense. In our case, it was a requirement to give editors a possibility to work with system before the final release. Editors needed about 2-3 weeks to check how EPiServer works, learn about provided by us functionality, think about content structure and finally for entering "production" content. We had to use Content First approach and work with editors in parallel. What are the implications?
  • Increased complexity - for a few reasons:
    • we were not working with test data therefore we had to think about backward compatibility or about upgrading content to make sure that it works with the latest code. There was no option like "forget about that component which we delivered last week and start using this new one ... by the way .. you need to recreate all pages that use old component".
    • additional environment to deal with - editors need separate server, you need to updated this server frequently. Lots of things can be automated here but there are always some upgrades (new properties, required configuration etc.) that will require your time.
    • editors will always find a way to break something, again, you have to deal with it.
  • Greater chance to deliver what is really needed - sometimes you can find important requirements only when working with real content, things like incorrect layout of some random components caused by too long title etc. Content itself might be an issue and while working on "Lorem ipsum" data you can't really discover this issues until the very last moment before the deadline.
  • Site is well tested - before the deadline site was checked by developers, testers and the client. Client has everything in place, level of confidence that system won't break in a last few moments before the deadline is high.
  • Dev team has more time to deliver whole functionality - because client can work with the system there is no need to deliver "final" release two or three weeks before going live and then praying for small number of critical bugs. Editors and developers can work in parallel which gives both sides additional time.
It was a cool project to work on, very dynamic, very agile and successful. It was also very busy time with a lot of pressure on us. But in the end we have learnt a lot and I think we all have a feeling that it was a decent achievement.

Tuesday, 19 May 2009

How to define custom styles in EPiServer's Rich Text Editor

EPiServer's Rich Text Editor (RTE) can be customized in a number different ways. Unfortunately not all available options are well documented. Defining your own set of styles available in RTE is one of those things.


I always struggle to recall how to change this dropdown even though I have done it number of times. Therefore I have decided to simply write it down. Styles visible above are defined with this code:

   1:  /* EPiServer Editor Class names, required for WYSIWYG editor dropdown */
   2:  h1 { EditMenuName: Header 1; }
   3:  h2 { EditMenuName: Header 2; }
   4:  h3 { EditMenuName: Header 3; }
   5:  h4 { EditMenuName: Header 4; }
   6:  h5 { EditMenuName: Header 5; }

Of course your are free to add as many styles as you like, important is to define styles with EditMenuName keyword.

Having CSS ready you still have to make it visible to the editor. In old times, when Edit and Admin mode files were part of each solution it was a matter of editing this file: \Util\styles\editor.css. But nowdays, Edit and Admin mode files are located outside the solution, in Program Files folder, so they are shared among all applications. (since EPiServer 5 R2) Because usually different applications use different styles, and also I would say that it's a good practice to keep all "parts" of application in one place, we can't use old approach anymore. So what is a new approach?

Answer is simple, create separate CSS file for your application which will be used by RTE. Creating such file is a good thing anyway as content editors expect similar experience when editing content in edit mode and browsing the site in view mode. If on your site text inside H1 tags is displayed in blue then you should get the same visual result in edit mode. This is the main point of creating special CSS file for RTE. Customization of style dropdown can be done in a meantime :)

Let's say that your file is ready, final question is how you can configure EPiServer to load your file in edit mode. It can be done in two ways:
  • in web.config, update EPiServer's stite configuration by changing uiEditorCssPaths property in a following way uiEditorCssPaths="~/css/rte.css"
  • or alternatively you can set this property in Admin mode:

Outcome of both options is exactly the same, in edit mode Rich Text Editors will "get" your CSS file, dropdown will use your styles and configuration will be saved in web.config.

Wednesday, 15 April 2009

Action<T> and Func<TResult> delegates

In this post I would like to write a little bit about two types of delegates which were added recently with ASP.NET 3.5 (System Namespace):

Action<T> - "Encapsulates a method that takes a single parameter and does not return a value". There are additional versions for two, three and four parameters:
It can be used with method like this:

   1:  public static void UpdatePage(PageData page, Action<PageData> action)
   2:  {
   3:      var writableClone = page.CreateWritableClone();
   4:      action(writableClone);
   5:      DataFactory.Instance.Save(writableClone, SaveAction.Publish);
   6:  }

To updated page in EPiServer it's required to create writable clone first, then we can apply our changes and finally instance of PageData has to be saved. This is a procedure which you can probably find in many places in your projects. With delegates you can easily "close" standard steps into a single method and pass changeable part as a parameter, like this:

   1:  PresenterUtils.UpdatePage(CurrentPage,
   2:                            p =>
   3:                                {
   4:                                    p.Property["MetaAuthor"].Value = "Marek";
   5:                                    p.Property["MainBody"].Value = "Hello World!";
   6:                                });

I think it should be pretty obvious that the above piece of code changes MetaAuthor and MainBody properties on CurrentPage. For me, main benefit of this approach is increased readability, hence the code is easier to maintain. Note that there is nothing in this call about creating writable clone, calling save method on DataFactory, those are all important and required steps to get things working properly but in the same time they are irrelevant from businesses logic point of view. You don't need to see those "standard" actions to understand what your class/method is doing.

You can also use delegates which return a value:
The simples version Func<TResult> - "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter."

In this example, method returns default value if property doesn't exist or is not set. If property is set then its value can be formatted in some special way:

   1:  public static string GetProperty<T>(PageData page, string propertyName, string defaultValue, Func<T, string> customAction)
   2:  {
   3:      if (page == null || string.IsNullOrEmpty(propertyName) || page[propertyName] == null)
   4:      {
   5:          return defaultValue;
   6:      }
   7:  
   8:      return customAction((T) page[propertyName]);
   9:  }

And this is how this method can be used:

   1:  public string GetFormattedDate(PageData page, string format)
   2:  {
   3:      return PresenterUtils.GetProperty<DateTime>(page, "EventDate", "", x => x.ToString(format));
   4:  }

In the above example we are interested in EventDate property, if this property is not set then empty string (default value) will be returned, otherwise DateTime will be formatted appropriately.

Of course, in pre-ASP.NET 3.5 era things like this were also possible but it was required to define your own delegates etc. Now you can use those which are available for you in the framework. Personally I really like lambda expressions and this "way" of crating APIs, it's much more readable for me therefore I encourage you to give it a try! :)

Thursday, 26 March 2009

TDD with EPiServer

During the last Demo Day (conference organized by Cognifide every 3-4 months) I had a chance to talk about Test Driven Development in EPiServer projects. In this post I would like to recap main points of my presentation.

Obstacles on the way to TDD

Unfortunately it's very hard to apply TDD to ASP.NET WebForms as it is. WebForms was designed as a platform for Rapid Application Development. Features like wide set of rich controls and state management (view state) can significantly speed up development but in the same time WebForms does not provide decent separation of concerns. I think code-behind files were suppose to give us some separation between the HTML and application logic. And to be fair it's "some" separation, but we still need to deal in one code-behind file with ASP.NET components, which are tightly bound to the underlying HttpContext, presentation logic and usually a few other things. It's clearly too much. There are way too many concerns in one place, too many dependencies on ASP.NET runtime. It makes code in applications like this hard to maintain and also makes writing unit test impossible.

Another thing with which you have to cope with is EPiServer. To get an idea of problems which you may encounter check this unit tests:

   1:  [Test]
   2:  public void MyEPiServerUnitTest()
   3:  {
   4:      var page = new PageData();
   5:      page.Property.Add("MetaAuthor", new PropertyString { Value = "MetaAuthor123" });
   6:      page.Property.Add("Heading", new PropertyString { Value = "Heading123" });
   7:      page.Property.Add("MainBody", new PropertyXhtmlString { Value = "MainBody123" });
   8:  
   9:      Assert.IsTrue(page["MainBody"].Equals("MainBody123"));
  10:  }

Basically I'm creating PageData instance with a few properties and then I'm checking if correct values will be returned. This is probably the simplest unit test ever. So if you run it then you don't expect anything else then a green bar right? Surprisingly, instead of green bar you will get exception saying:

   System.ApplicationException: First time you call Settings.Instance you must have a valid HttpContext.

If you want to know who you should blame for calling Settings.Instance then check PropertyXhtmlString :)

As you may know, HttpContext can be mocked, it's not a problem. But in our case it won't help much, we won't get this exception of course, but we will get different one instead.

As you can see the way to TDD is not easy, EPiServer is also closely bound to HttpContext.

Deal with problems in two steps

Step 1 - Isolate as much functionality as you can away from ASP.NET runtime. How? Introduce design pattern called Model-View-Presenter (MVP). This pattern divides the responsibilities into separate classes:
  • View - those are *.aspx and *.ascx files including code-behind files. This layer should be as thin as possible, code-behind files should delegate as much functionality as possible to the Presenter.
  • Presenter is responsible for interface logic which includes initialization of view and dealing with all events. It's important that presenter shouldn't have any reference to System.Web namespace and should be separated from the concrete view with an interface. Additionally presenter has access to business layer and/or backend services.
  • Model - the data on which application is working on, for EPiServer those might be pages, users, files, data stored in the HttpSession etc.
It's worth mentioning that in general there are two variations of MVP design pattern:

There are minor differences between both versions, you can learn more about both versions on Martin Fowler's site: Passive View and Supervising Controller.

Let me show you how it works in practice. I'm going to implement well known news page from EPiServer's public templates, the page for visitors looks like this:


Areas which are set by news page are marked with red rectangles. Other data comes from MasterPage. Below you can check how the aspx file:

   1:  <asp:Content ID="Content4" ContentPlaceHolderID="MainBodyRegion" runat="server">
   2:      <div id="MainBody">
   3:          <h1><asp:Literal ID="litHeading" runat="server" /></h1>
   4:          <asp:Literal ID="litMainBody" runat="server" />
   5:      </div>    
   6:  </asp:Content>
   7:  
   8:  
   9:  <asp:Content ID="Content5" ContentPlaceHolderID="SecondaryBodyRegion" runat="server">
  10:   <div id="SecondaryBody">
  11:      <dl>
  12:          <dt><EPiServer:Translate ID="author" runat="server" Text="/news/writername" /></dt>
  13:          <dd><asp:Literal ID="litMetaAuthor" runat="server" /></dd>
  14:          <dt><EPiServer:Translate ID="Translate1" runat="server" Text="/news/publishdate" /></dt>
  15:          <dd><asp:Literal ID="litPageStartPublish" runat="server" /></dd>
  16:      </dl>
  17:  </div>
  18:  </asp:Content>

As you can see, so far nothing surprising. But now, instead of jumping to the code-behind file, you should create an interface for the view. I'm going to call my interface INewsView and this is how it looks like:

   1:  public interface INewsView
   2:  {
   3:      String Heading { get; set; }
   4:      String MainBody { get; set; }
   5:      String MetaAuthor { get; set; }
   6:      String PageStartPublish { get; set; }
   7:  }

INewsView interface defines a contract between presenter and view. View needs to get above data from presenter and presenter is obligated to deliver them.

Having the interface ready we can start working on the presenter, the simplest implementation looks like this:

   1:  public class NewsPresenter : AbstractPresenterBase<INewsView>
   2:  {
   3:      public override void Initialize()
   4:      {
   5:          View.MetaAuthor = PresenterUtils.GetPropertyValue(CurrentPage, "MetaAuthor", "");
   6:          View.PageStartPublish = PresenterUtils.GetProperty<DateTime>(CurrentPage, "PageStartPublish", "",
   7:                                                                       x => x.ToShortDateString());
   8:  
   9:          View.Heading = PresenterUtils.GetPropertyValue(CurrentPage, "Heading", "");
  10:          View.MainBody = PresenterUtils.GetPropertyValue(CurrentPage, "MainBody", "");
  11:      }
  12:  }

PresenterUtils class is just my helper which is returning values of properties if they are set, or default values otherwise. There is no magic here but as you may notice, logic dealing with CurrentPage is located in one place which makes it much more reusable.

Now we have NewsPresenter ready we can piece it all together. The only missing part is implementation of INewsView interface:


   1:  public partial class NewsItem : SimpleMVPPageBase<NewsPresenter, INewsView>, INewsView
   2:  {
   3:      protected override INewsView ViewImplementation
   4:      {
   5:          get { return this; }
   6:      }
   7:  
   8:      public String MetaAuthor
   9:      {
  10:          get { return litMetaAuthor.Text; }
  11:          set { litMetaAuthor.Text = value; }
  12:      }
  13:  
  14:      public String PageStartPublish
  15:      {
  16:          get { return litPageStartPublish.Text; }
  17:          set { litPageStartPublish.Text = value; }
  18:      }
  19:  
  20:      public string Heading
  21:      {
  22:          get { return litHeading.Text; }
  23:          set { litHeading.Text = value; }
  24:      }
  25:  
  26:      public string MainBody
  27:      {
  28:          get { return litMainBody.Text; }
  29:          set { litMainBody.Text = value; }
  30:      }
  31:  }

This class is located in code-behind file, it implements INewsView interface and represents a link between NewsPresenter and the view and actual ASP.NET controls.

What you can't see in this class is how the presenter is instantiated, when the view implementation is passed to the presenter, this is wrapped in SimpleMVPPageBase class:

   1:  public abstract class SimpleMVPPageBase<PresenterClass, ViewInterface> : SimplePage
   2:      where PresenterClass : AbstractPresenterBase<ViewInterface>, new()
   3:  {
   4:      protected PresenterClass Presenter;
   5:      protected abstract ViewInterface ViewImplementation { get; }
   6:  
   7:      protected override sealed void OnInit(EventArgs e)
   8:      {
   9:          base.OnInit(e);
  10:          Presenter = new PresenterClass
  11:          {
  12:              View = ViewImplementation,
  13:              CurrentPage = CurrentPage
  14:          };
  15:      }
  16:  
  17:      protected override sealed void OnLoad(EventArgs e)
  18:      {
  19:          base.OnLoad(e);
  20:          if (!IsPostBack)
  21:          {
  22:              Presenter.Initialize();
  23:              DataBind();
  24:          }
  25:      }        
  26:  }

SimpleMVPPageBase class is responsible for two things:
  • It instantiates presenter class, and sets for it CurrentPage and view implementation.
  • Also, within OnLoad, it calls Presenter.Initialize() method which should initialize the view.
With all those pieces in place, you should get a working page. So, in fact we are in the starting position but with this version we have significantly better separation of concerns and our logic is testable:


   1:  public class Checking_news_view_initialization : AbstractPresenterTest<NewsPresenter>
   2:  {
   3:      protected override void Before_each_test()
   4:      {
   5:          Presenter.CurrentPage = EPiServerPage.CreateBlankPage()
   6:              .AddProperty<PropertyString>("MetaAuthor", "author123")
   7:              .AddProperty<PropertyString>("Heading", "heading123")
   8:              .AddProperty<PropertyXhtmlString>("MainBody", "mainBody123")
   9:              .AddProperty<PropertyDate>(EPiProperties.PageStartPublish, DateTime.Today)
  10:              .Instance();
  11:  
  12:          Presenter.View = mocks.StrictMock<INewsView>(null);
  13:      }
  14:  
  15:      [Test]
  16:      public void It_should_set_all_properties()
  17:      {
  18:          using (mocks.Record())
  19:          {
  20:              Expect.Call(Presenter.View.Heading).SetPropertyWithArgument("heading123");
  21:              Expect.Call(Presenter.View.MainBody).SetPropertyWithArgument("mainBody123");
  22:              Expect.Call(Presenter.View.MetaAuthor).SetPropertyWithArgument("author123");
  23:              Expect.Call(Presenter.View.PageStartPublish).SetPropertyWithArgument(DateTime.Today.ToShortDateString());
  24:          }
  25:  
  26:          using (mocks.Playback())
  27:          {
  28:              Presenter.Initialize();
  29:          }
  30:      }
  31:  }


Within Before_each_test() method I'm basically creating an instance of PageData with a few properties. I'm also mocking the view using Rhino Mocks. Thanks to that in It_should_set_all_properties() method I can record my expectations and later verify them.

But wait a second ... as I was writing earlier, EPiServer requires valid HttpContext right?

Workaround for this issue is a step 2 on our way to TDD:

   1:  [TestFixture]
   2:  public abstract class AbstractPresenterTest<PresenterClass> where PresenterClass : new()
   3:  {
   4:      protected PresenterClass Presenter { get; private set; }
   5:      protected MockRepository mocks { get; private set; }
   6:  
   7:  
   8:      protected AbstractPresenterTest()
   9:      {
  10:          Presenter = new PresenterClass();
  11:      }
  12:  
  13:      [TestFixtureSetUp]
  14:      protected void SetUpContextForAWholeFixture()
  15:      {
  16:          var settings = new EPiServer.Configuration.Settings
  17:                             {
  18:                                 StringCompressionThreshold = 0
  19:                             };
  20:  
  21:          Type settingsType = settings.GetType();
  22:          settingsType.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)
  23:              .SetValue(settings, settings);
  24:      }
  25:  
  26:      [SetUp]
  27:      protected void SetUpContextForEachTest()
  28:      {
  29:          mocks = new MockRepository();
  30:          Before_each_test();
  31:      }
  32:  
  33:      protected abstract void Before_each_test();
  34:  }


HttpContext related exception won't be triggered if you create instance of Settings and using reflection assign it to the static and private field _instance on Settings class. This way you can also provide your configuration for tests.

Conclusions
  • the most important one is that Test Driven Development with EPiServer is possible!
  • you need to isolate as much functionality from ASP.NET runtime and MVP design patter is doing exactly that for you
  • with a little bit of hacking of EPiServer you can instantiate PageData and test your logic
  • MVP requires additional effort to create interfaces, presenters etc. Downside is that you won't be able to build web applications so rapidly but it should pay off in longer and more complex projects where having a set of automated tests is a big advantage.
Credits
  • My research and presentation were inspired by Raymond Lewallen who was a guest of Poznan .Net Group and was presenting Behavioral Driven Development approach.
  • I have used image from Microsoft site talking about MVP.

Thursday, 5 March 2009

EPiServer Search with VirtualPathVersioningProvider

EPiServer out of the box provides versioning file system handled by VirtualPathVersioningProvider. In this post I would like to show you step by step how to enable search for this provider.

Step 1 - Make sure that you are using VirtualPathVersioningProvider.

It sounds funny but it's worth double checking if your website is configured to use versioning file system or native file system (VirtualPathNativeProvider).

The best way is to check web.config section in which virtual providers are defined. You should find there lines like this:

   1:  <add showInFileManager="true" 
   2:       virtualName="Documents" 
   3:       virtualPath="~/Documents/" 
   4:       bypassAccessCheck="false" 
   5:       maxVersions="5" 
   6:       name="SiteDocuments"
   7:       type="EPiServer.Web.Hosting.VirtualPathVersioningProvider,EPiServer"
   8:       physicalPath="C:\EPiServer\VPP\ExampleEPiServerSite\Documents">

physicalPath property specifies where actually files will be stored. Interesting is that the paths and the filenames are stored in a database, only content is located in the file system. Internally, VirtualPathVersioningProvider keeps content in a fairly characteristic way:


Step 2 - EPiServer Indexing Service configuration


Indexing Service configuration is saved in a EPiServer.IndexingService.exe.config file which can be found in the same folder where the EPiServer Indexing Service was installed.

To let know indexing service about your folders you need to alter following part of configuration:

   1:  <episerver.indexingService>
   2:    <indexes>
   3:      <add connectionString="Data Source=server_name;Database=db_name;User Id=username;Password=password;Network Library=DBMSSOCN;" 
   4:           databaseClient="" 
   5:           filePath="C:\EPiServer\VPP\ExampleEPiServerSite\Documents" 
   6:           itemRoot="/Documents" />
   7:    </indexes>
   8:  </episerver.indexingService>

Two things are important here:
  • you need to use the same database here (connectionString) which your application is using
  • and the same path (filePath)

Step 3 - Make sure that Indexing Service works

The best way to makes sure that your folder was indexed is to check if folder called index was created. (Check the first image) This folder contains indexing service's data.

If your service is started and index folder is not present then probably good idea is to check indexing service logs, usually you can find there helpful information.

Lack of index folder can also cause error like this:

VirtualPathProvider 'SiteDocuments', Search Error: C:\EPiServer\VPP\ExampleEPiServerSite\Documents\index not a directory

Step 4 - Check if it works Probably the fastest way to check if search works it to use EPiServer's build-in file management tool:


Then you can access search feature:


I hope that this post will save a few people a bit of time. If you have been working with EPiServer search before and have interesting experiences then leave a comment, your input is valuable!

Tuesday, 24 February 2009

EPiServer -- Deleted Pages


I was asked recently a few times how to detect deleted pages. Deleted from editors perspective, which means -- moved to the Recycle Bin. Answer to this question is really simple, PageData class has a property called IsDeleted, here is an example:

   1:  public static bool IsPageDeleted(PageReference pageRef)
   2:  {
   3:      PageData page = DataFactory.Instance.GetPage(pageRef);
   4:      return page.IsDeleted;
   5:  }

In fact, that is all what is needed to check if page was deleted. Below I would like to list additionally a few related tips which might be useful:
  • You can get reference to the Recycle Bin thanks to this static property:

       PageReference.WasteBasket
  • Another way to check if instance of PageReference class points to the Recycle Bin is to call this method:

       DataFactory.Instance.IsWastebasket(new PageReference(12))
  • If you would like to move a page to the Recycle Bin programmatically then you don't really want to delete the page, you should use this method instead:

       DataFactory.Instance.MoveToWastebasket(new PageReference(121));
  • EPiServer has by default scheduled job called "Automatic Emptying of Recycle Bin" which is responsible for:
    With Automatic Emptying of Recycle Bin, you can set how often your Recycle Bin should be emptied. The aim of this function is to stop old information from being left in the Recycle Bin for a long period of time. With automatic emptying, all information that is older than 30 days will be deleted from the Recycle Bin.

    So what can be wrong when pages from your Recycle Bin don't get removed? The most likely option is that this scheduled job is not activated, make sure that checkbox Active is checked ;)
If you need further details or some relevant information is missing then feel free to leave a comment.

Other interesting posts:

Monday, 16 February 2009

EPiServer - Outgoing Links

In this post I will show how to get list of all referenced pages (and files) for any EPiServer page. Although it sounds like a trivial task, in fact, it's not so obvious. First of all it's necessary to realize that there are two major groups of "linking" properties:
  • Properties that derive from PropertyPageReference, internally they store link as a page id. Out of the box there in only one property type in EPiServer which uses this class -- PageReference.
  • And the bunch of properties which use permanent links internally like:
    • PropertyImageUrl - Url to image
    • PropertyDocumentUrl - Url to document
    • PropertyUrl - URL to page/external address
    • PropertyXhtmlString - Xhtml Long String
    • PropertyLinkCollection - Link Collection
It's fairly simple to get referenced page from PropertyPageReference:

   1:  var pageReference = CurrentPage.Property["propert_name"] as PropertyPageReference;
   2:  var page = DataFactory.Instance.GetPage(pageReference.PageLink);

What about other property types? There is a one common thing for them -- they all implement IReferenceMap interface:


We can use following code to get outgoing links:

   1:  var referenceMap = property as IReferenceMap;
   2:  if (referenceMap != null)
   3:  {
   4:      IList<Guid> linkIds = referenceMap.ReferencedPermanentLinkIds;
   5:      foreach (Guid guid in linkIds)
   6:      {
   7:          PermanentLinkMap map = PermanentLinkMapStore.Find(guid);
   8:          
   9:          // mappedUrl example: /Templates/Public/Pages/NewsItem.aspx?id=30
  10:          string mappedUrl = map.MappedUrl.ToString();
  11:  
  12:          // and get friendly URL version using UrlRewriteProvider
  13:          var url = new UrlBuilder(mappedUrl);
  14:          EPiServer.Global.UrlRewriteProvider.ConvertToExternal(url, null, System.Text.Encoding.UTF8);
  15:          string friendlyUrl = UriSupport.AbsoluteUrlBySettings(url.ToString());
  16:      }
  17:  }

What are permanent links?

Internal URL's in EPiServer are stored in the database using a format called Permanent Links. Property types are responsible to transform a URL from a permanent link to a standard template link upon access from user code, and of course the other way around before content is stored to the database.
It's a very useful feature of EPiServer because it enables you to manipulate files and pages without risk that some links will get broken.
You can rename files and templates without affecting the links; you can even move an EPiServer site from a virtual directory to a root site without breaking a single link.

Permanent Links and EPiserver's API

IReferenceMap Interface exposes ReferencedPermanentLinkIds property thanks to which we have access to all links stored internally. That is very convenient especially for properties like PropertyXhtmlString which usually also store lots of other data. It is worth noticing that PropertyLongString doesn't implement this interface, hence it doesn't use permanent links. That is a reason why PropertyXhtmlString is recommended over PropertyLongString.

PermanentLinkMapStore class is a part of EPiServer's API for permanent links. I used this class to get mapped URL based on link's Guid. In next step mapped URL can be converted to friendly URL (code based on Ted Nyberg's post). Permanent link can be broken (referenced page was deleted) in which case PermanentLinkStore.Find() method will return null.

Based on above code I have created an edit mode plugin which lists all outgoing links, it looks like this:

Source code can be downloaded from here.

Other interesting posts:

Thursday, 12 February 2009

EPiServer 5 R2 and Link Collection property

With EPiServer 5 R2 new property type was released -- Link Collection. It looks like a EPiServer's version of very popular Mulitipage property. In this post I would like to show you exactly how it can be used and also what are the pros and cons.

After adding a property of this type to a page you will see in edit mode this:


And with a few links added property looks like this:


This is first significant change comparing to old Multipage property (MP) -- list of all links is visible on the page. With old MP it was necessary to click on the button to get a popup with a list of links. That is a good change!

What is missing here for me is a ability to test links. Text which you can see for the first item on the list is not necessary a page name (it might be a clickable text) so it's impossible to figure out from this view what page is referenced.

Funny thing is that title for this link has a following form:

It is very useful isn't it? ;) I think the simplest solution would be to make link text clickable.

After clicking on 'Add Link' or 'Edit' button you will get old popup:


There are no surprises here, it's an old well-know dialog.

Lets check now how to deal with Link collection in a code. It's quite common to use Repeater to display links:

   1:  <asp:Repeater ID="rptRelatedLinks" runat="server">
   2:      <HeaderTemplate><dl></HeaderTemplate>
   3:      <ItemTemplate><dt><asp:HyperLink runat="server" ID="hplMainLink" /></dt></ItemTemplate>
   4:      <FooterTemplate></dl></FooterTemplate>
   5:  </asp:Repeater>

And here is a code to get links from the CurrentPage:

   1:  PropertyLinkCollection links = (PropertyLinkCollection) CurrentPage.Property["RelatedLinks"];
   2:  rptRelatedLinks.DataSource = links;
   3:  rptRelatedLinks.DataBind();

And a method populating the Repeater:

   1:  void rptRelatedLinks_ItemDataBound(object sender, RepeaterItemEventArgs e)
   2:  {
   3:      if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
   4:      {
   5:          LinkItem linkItem = (LinkItem) e.Item.DataItem;
   6:          HyperLink link = (HyperLink) e.Item.FindControl("hplMainLink");
   7:  
   8:          // mapped link has a form like:
   9:          // <a href="/Templates/Public/Pages/Page.aspx?id=16&epslanguage=en" target="_blank" 
  10:          //       title="this is link title">Information about the meeting</a>
  11:          string mappedLink = linkItem.ToMappedLink();
  12:          
  13:          // permanent link form:
  14:          // <a href="~/link/bb6aa3227f8f467bbe1a42154cb56ba5.aspx" target="_blank" 
  15:          //           title="this is link title">Information about the meeting</a>
  16:          string permanentLink = linkItem.ToPermanentLink();
  17:  
  18:          // because Href property will return permanent link like 
  19:          // ~/link/bb6aa3227f8f467bbe1a42154cb56ba5.aspx
  20:          //
  21:          // it's necessary to use PermanentLinkMapStore.ToMapped(url) to covert it to normal form
  22:          // result is required to determine if conversion was successful
  23:          // it will fail for mails (mailto:test@test.com), documents and external links
  24:          UrlBuilder url = new UrlBuilder(linkItem.Href);
  25:          bool result = PermanentLinkMapStore.ToMapped(url);
  26:  
  27:          link.NavigateUrl = result ? url.ToString() : linkItem.Href;
  28:          link.Text = linkItem.Text;
  29:          link.ToolTip = linkItem.Title;
  30:          link.Target = linkItem.Target;
  31:      }
  32:  }

The basic problem is that Href property returns permanent link, therefore it's necessary to use PermanentLinkMapStore class to convert the links. ToMappedLink() method returns a full "a" tag, which might be convenient in some cases. Take a look on all properties again:

My overall impression is positive, new property Link collection is easy to use but for sure there are things which could be improved like ability to test a link or ability to define page root to look for pages to include. It's a hassle to always start from the very top!

I wonder now if there is still a reason to use old Multipage property, what do you think?

Related posts: