vrijdag 17 januari 2014

"Default Settings" for configuration transformations in Visual Studio using SlowCheetah

Hopefully as a result of my User voice but mostly just helpful in writing configuration transformations. It is especially helpful in the scenario where you are given predefined web.config files and where you have multiple build configurations for instance with CMS implementations like Sitecore.

I like to define all my custom configuration changes in transformation files, and leave the main web.config untouched. This makes upgrades a lot easier.

I'm unable to find anything related to "Default Settings" with SlowCheetah, but I noticed that when I created a transformation for a project I'm working on there was suddenly an extra "File.Default Settings.config". Upon further investigation this seems to behave the way I requested.

1. You define a *.config file


2. You 'Add transformations'



 - In "Default Settings" you define transformations that need to be applied to all build configurations


 - For each *.[Build configuration].config file you define transformations specific for that configuration


3. Configuration is transformed in the following order:

 - *.config
 - *.Default Settings.config applied
 - *.[Build Configuration].config applied

This is the resulting configuration file for a "Release" build of the examples shown in the screenshots:



So now you have an extra layer for defining transformations. Does anyone know with which version this functionality came?

woensdag 11 april 2012

Creating a strongly typed Repeater Control in ASP.NET using generics

The Repeater is one of my favorite and most used controls when developing websites in ASP.NET Webforms. It is the best tool at hand for generating dynamic lists based on collections of data. I'm also very fond of using databinding syntax in .aspx files avoiding code-behind files because in my opinion it increases readability and manageability.

One of the main drawbacks when using the Repeater in conjunction with databinding syntax is the need for casting the DataItem property to it's actual type:


I would like to share my take on a strongly typed Repeater which removes the need for casting objects, increases productivity and readability and unlocks intellisense!


The solution

The solution requires you to create two base classes which includes the core functionality and then for every typed Repeater create a simple inherited class which defines the type. As you might have noticed in the above screenshot I introduced a new property called TypedItem. You are free to create a name which suits your needs e.g. Container.I for improved readability.

Setting up the base classes:
1. Create a generic Repeater base class

This is the main workhorse of the solution. It was neccessary to override the InitializeItem method of the Repeater class because the default Repeater works with the private variables for the templates. This implementation uses the properties for each template which is required because we will be overriding the ItemTemplate and AlternatingItemTemplate for our typed Repeaters.

Also the CreateItem method is overriden to actually create a generic RepeaterItem<T> which will be created in step 2.

using System.Web.UI;
    using System.Web.UI.WebControls;

    public class GenericRepeaterBase<T> : Repeater where T : class
    {
        protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType)
        {
            return CreateRepeaterItem(itemIndex, itemType);
        }

        private static GenericRepeaterItem<T> CreateRepeaterItem(int itemIndex, ListItemType itemType)
        {
            return new GenericRepeaterItem<T>(itemIndex, itemType);
        }

        protected override void InitializeItem(RepeaterItem item)
        {
            ITemplate template = (ITemplate)null;
            switch (item.ItemType)
            {
                case ListItemType.Header:
                    template = HeaderTemplate;
                    break;
                case ListItemType.Footer:
                    template = FooterTemplate;
                    break;
                case ListItemType.Item:
                    template = ItemTemplate;
                    break;
                case ListItemType.AlternatingItem:
                    template = AlternatingItemTemplate;
                    if (template != null)
                        break;
                    else
                        goto case 2;
                case ListItemType.Separator:
                    template = SeparatorTemplate;
                    break;
            }
            if (template == null)
                return;
            template.InstantiateIn((Control)item);
        }
    }

2. Create a generic RepeaterItem base class

This class contains the property that you use while working with the Repeater. You can rename the TypedItem property as you wish.

using System.Web.UI.WebControls;

    public class GenericRepeaterItem<T> : RepeaterItem where T : class
    {
        public GenericRepeaterItem(int itemIndex, ListItemType itemType)
            : base(itemIndex, itemType)
        {

        }

        public T TypedItem
        {
            get { return (T)this.DataItem; }
        }
    }

Usage:
3. Create a typed Repeater

Now for every Repeater that you would like to use strongly typed you must create an easy implementation of the GenericRepeaterBase<T> class. The example below creates the PersonRepeater mentioned before.

The PersistenceMode attribute needs to be redefined because it is not automatically inherited. The TemplateContainer attribute and the generic type definition on the class make the neccessary definitions for the Repeater to work.

public class PersonRepeater : GenericRepeaterBase<GenericRepeaterItem<Person>>
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(GenericRepeaterItem<Person>))]
        public override ITemplate ItemTemplate { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(GenericRepeaterItem<Person>))]
        public override ITemplate AlternatingItemTemplate { get; set; }
    }

4. Use it!

public class Person
    {
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }

<%@ Register TagPrefix="asp" Namespace="TypedRepeater" Assembly="TypedRepeater" %>
    <asp:PersonRepeater runat="server">
        <ItemTemplate>
            Name: <% #Container.TypedItem.Name %><br />
            Date of birth: <%#Container.TypedItem.DateOfBirth.ToShortDateString() %>
        </ItemTemplate>
    </asp:PersonRepeater>

I hope this code brings you lots of joy! ;)

donderdag 8 december 2011

E-Commerce Services - Storing the configuration settings outside home node

Sitecore has a great E-Commerce module (They just released their latest version that runs under Sitecore 6.5 called E-Commerce Services). When running multiple sites you probably want each site to have it's own configuration items. The default setup isn't ideal for this situation as it forces you to store the configuration item under the /home node of your website. If you're not going to setup any presentation details for these items it makes no sense to store them under the /home node.

How does it work?
Well, first of all E-Commerce Services is configured using "site" settings and "business" settings. Site settings are general settings for your site like: links to item locations, design, defaults, formats etc. Whilst business settings define things like VAT regions, Currencies, Order statusses etc. Business settings can be placed anywhere you like because they are linked under site settings. The site settings itself however are resolved through a pipeline called "getConfiguration"

The getConfiguration pipeline
The configuration pipeline is configured in the Sitecore.Ecommerce.config file and looks like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
      <getConfiguration>
        <processor type="Sitecore.Ecommerce.Pipelines.GetConfiguration.GetFromContextSite, Sitecore.Ecommerce.Kernel" />
        <processor type="Sitecore.Ecommerce.Pipelines.GetConfiguration.GetFromWebSite, Sitecore.Ecommerce.Kernel" />
        <processor type="Sitecore.Ecommerce.Pipelines.GetConfiguration.GetFromLinkManager, Sitecore.Ecommerce.Kernel" />
        <processor type="Sitecore.Ecommerce.Pipelines.GetConfiguration.GetFromResolver, Sitecore.Ecommerce.Kernel" />
      </getConfiguration>
  </sitecore>
</configuration>


The problem lies in the fact that the GetFromContextSite processor gets the Site settings item based on the StartPath property of the site. This points to the /sitecore/content/Sitename/home item of the website and not the /sitecore/content/Sitename item. The module allows for the <site /> definition to contain a EcommerceSiteSettings attribute to be specified which defaults to "/Site Settings" in the processor. But as said this is relative to the StartPath property and not the ContentStartPath property.

The solution
The solution to the problem is very simple. I created a custom processor that retrieves the Site Settings relative to the ContentStartPath property of the website. Just insert this processor at the first position in the pipeline and you're good to go!

Good luck!


namespace Sitecore.SharedSource.Pipelines.GetConfiguration  
 {  
   class GetFromContextSiteOutsideRoot : GetConfigurationProcessor  
   {  
     private static readonly string SettingsRootTemplateId = Settings.GetSetting("Ecommerce.Settings.SettingsRootTemplateId");  
     private const string EcommerceSiteSettingsAttribute = "EcommerceSiteSettings";  
     private const string EcommerceSiteSettingsDefaultValue = "/Home/Site settings";  
     public GetFromContextSiteOutsideRoot()  
     {  
     }  
     public override void Process(ConfigurationPipelineArgs args)  
     {  
       object obj1 = IoCContainerExtensions.Resolve(Sitecore.Ecommerce.Context.Entity, args.ConfigurationItemType, new ResolverOverride[0]);  
       if (!(obj1 is IEntity) || Sitecore.Context.Site == null || Sitecore.Context.Database == null)  
         return;  
       string part2 = Sitecore.Context.Site.Properties[GetFromContextSiteOutsideRoot.EcommerceSiteSettingsAttribute];  
       if (string.IsNullOrEmpty(part2))  
         part2 = GetFromContextSiteOutsideRoot.EcommerceSiteSettingsDefaultValue;  
       Item obj2 = Sitecore.Context.Database.GetItem(FileUtil.MakePath(Sitecore.Context.Site.ContentStartPath, part2));  
       if (obj2 == null || obj2.Template.ID.ToString() != GetFromContextSiteOutsideRoot.SettingsRootTemplateId && Enumerable.FirstOrDefault<TemplateItem>(Enumerable.Where<TemplateItem>((IEnumerable<TemplateItem>)obj2.Template.BaseTemplates, (Func<TemplateItem, bool>)(x => x.ID.ToString() == GetFromContextSiteOutsideRoot.SettingsRootTemplateId))) == null)  
         return;  
       IEntity entity = (IEntity)obj1;  
       Item source = obj2.Axes.SelectSingleItem(string.Format(".//*[@@name='{0}']", (object)entity.Alias));  
       this.RegisterInstance(args, (IEntity)obj1, source);  
       args.AbortPipeline();  
     }  
   }  
 }  

woensdag 1 juni 2011

RazorForSitecore module - Enable Razor templating in the Sitecore CMS

I'm a huge fan of the ASP.NET MVC framework since it was launched. A few months ago I was forced back to Webforms when I started using the Sitecore content management system. While trying to combine elements from MVC into Sitecore I found a nice opportunity that allows for Razor syntax to be used to define re-usable renderings. Webforms and Razor together might not be "love at first sight" but after a bit of tweaking it turns out to work quite nicely.

I molded together a module called "RazorForSitecore". This module is backed by the open-source Razor Engine* and integrates nicely into the Sitecore presentation engine (how?). It can even be combined with other modules that enable strongly-typed classes to be created from templates (e.g. CompiledDomainModel). This has some benefits like:
  • Compile time type checking (views are compiled into memory assembly)
  • Intellisense support
(* PLEASE NOTE: This module uses a slightly modified version of the Razor Engine so that template cache invalidation is made possible!)

The module is available at the Sitecore Shared Source (RazorForSitecore) library. To use the module you have to do the following (detailed description):
  1. Update IIS application pool .NET version to 4.0!
  2. Install the package from the Shared Source Library (link).
  3. Update your main web.config file (see below)
  4. Create your first Razor View
  5. Add your view to the presentation details of an item
To use RazorForSitecore in conjunction with a module like CompiledDomainModel you have to follow these additional steps:
  • Install your strong-typing module
  • Create a processor in the "initialize" pipeline that specifies which ITypedModelProvider the module should use
You can download the module from the Shared Source libary: http://trac.sitecore.net/RazorForSitecore



How does this module work?
The module allows you to create Razor renderings mostly as an alternative to XSLT renderings. XSLT renderings are used for simple presentation elements (from a backend perspective), unlike sublayouts which allow for complex behavior or logic to be implemented. These Razor views cannot contain complex behavior as they are totally unaware of the Webforms lifecycle (they are parsed in a PreRender event of a sublayout).

When you create a 'Razor view' the module automatically creates an item and a corresponding .cshtml template file. However, it also creates an .ascx sublayout to 'contain' and 'integrate' the Razor template into Sitecore. Don't worry, you don't have to do anything with the file it just sits there and inherits from a base class that contains the logic. The module also sets the parameters 'modelType' and 'razorPath' on the item. Don't change or overwrite these or you will die! :)

Creating view helpers
It is possible to extend the view with helper functions through extension methods applied on the SitecoreHelper class. The SitecoreHelper class is exposed as property "Sc" in the view baseclass. This is an example of an extension method that uses the FieldRenderer control and keeps supporting the Page Editor:

public static class RenderingHelpers
  
   {
  
     public static string FieldRenderer(this SitecoreHelper helper, Item item, string fieldName)
  
     {
  
       Sitecore.Web.UI.WebControls.FieldRenderer r = new Sitecore.Web.UI.WebControls.FieldRenderer();
  
       r.Item = item;
  
       r.FieldName = fieldName;
  
       return r.Render();
  
     }
  
   }  


Here is a detailed description of the steps involved.
1, 2
This really shouldn't be a problem

3. Update your main web.config file
Add the following section element under configSections:

<section name="razorEngine" type="RazorEngine.Configuration.RazorEngineConfigurationSection, RazorEngine" requirePermission="false" />  

Add the following razorEngine element under configuration (right above connectionStrings (as sibling) will do):

<razorEngine>  
   <namespaces>  
    <add namespace="Sitecore.Sharedsource.RazorForSitecore" />  
   </namespaces>  
   <templateServices default="Sitecore.Sharedsource.RazorForSitecore.TemplateService, RazorForSitecore">  
    <add name="Sitecore.Sharedsource.RazorForSitecore.TemplateService, RazorForSitecore" language="CSharp" />  
   </templateServices>  
  </razorEngine>  

Find the compilation element (at the bottom of the file) and add/change the targetFramework attribute to reflect "4.0" (changing the target framework under Project properties -> Application will also work):

<compilation defaultLanguage=”c#” debug=”false” targetFramework=”4.0” />  

Add the following buildProviders element under compilation:

<buildProviders>
  
   <add extension=".cshtml" type="System.Web.WebPages.Razor.RazorBuildProvider, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  
 </buildProviders>
  

Add the following add elements under assemblies (under compilation). Make sure you remove the existing add element for System.Data.Linq which is probably pointing to version 3.5.0.0:

<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  
 <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  
 <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

In some cases you need to find the providerOption element and change the value to "v4.0". Don't worry if you can't find it, .NET will throw a nice exception if you forget to change it in that case.

<providerOption name="CompilerVersion" value="v4.0" />  

4. Create your first Razor View


Specify the view name.

The default model type is Item. The module sets the .Model property of the view to the current item in Sitecore.
You should only change the model type if you are using a strong-typing module that converts a Sitecore item into
another type.

Items belonging to the views are stored in a fixed location. You can create subfolders if you like.

The files (one .ascx 'container' and the .cshtml Razor template) are stored in /layouts/razor by default. You can change this
in the /App_Config/Include/RazorForSitecore.config file.

Refresh the content tree to see your view.


5. Add your view to the presentation details of an item


Additionally: to use CompiledDomainModel as your model provider.
  • Install the module as described on the module homepage (link).
  • Create a processor for the initialize pipeline that registers the model provider, e.g.
    namespace Sitecore.Sharedsource.RazorForSitecore
      
       {
      
         class RazorProcessor
      
         {
      
           public void Process(PipelineArgs args)
      
           {
      
             RazorForSitecore.ModelManager.SetModelProvider(new GenericTypedModelProvider(YourDomainModelNamespaceHere.ItemWrapper.CreateTypedWrapper));
      
           }
      
         }
      
       }  
    
  • Register the processor in ~/App_Config/Include/RazorForSitecore.config
From this point you can specify your newly created model types instead of the Sitecore Item class.

Enjoy the module and let me know if you find bugs or have suggestions for improvements!