ILearnable .Net

May 22, 2011

Introducing EPiLang

Filed under: Uncategorized — andreakn @ 18:28

A while back I wrote about how to get strongly typed globalization handling for EPiServer CMS.

In the time since I have been playing around with ways to empower administrators of EPiServer sites to change the globalization texts themselves without having to involve developers directly. I don’t have know how many times the customer has come to me and requested that this string should be that and required me to circumvent our standard deployment routine so that “we can get the new string out immediately”

What I have come up with is basically a framework that allows each text used to be persistently overridden in runtime by an administrator. Combined with the T4-goodness from my previous post I believe I have made something that could turn out to be useful for some other sites. I Have decided to call it EPiLang (yes, I am inspired by the awesome EPiImage framework)

Under normal conditions you can access globalized strings in three different ways in EPiServer:

  1. <%= LanguageManager.Instance.Translate(“/xpath/to/string”) %>
  2. <EPiServer:Translate runat=”server”  Text=”/xpath/to/string” />
  3. <%$ Resources: EPiServer, xpath.to.string%>
Each of these methods require you to spell the path to the text correctly and also requires a fair bit of looking up the xml file(s) going “where did I put that string….” .
With EPiLang you can access strings this way
  • <%= LanguageService.Translate(“/xpath/to/string”) %>
  • <EPiLang:Translate runat=”server”  Text=”/xpath/to/string” />
  • <%$ Resources: EPiServer, xpath.to.string%>
  • <%= EPiLang.xpath.to.string.Text %>
The first three are as you can see remarkably like the originals, but the last one is the one you’ll find yourself using over and over again since it is strongly typed and gives you compiler support and intellisense.
No matter which of these you use, after adding the webcontrol <EPiLang:Editor runat=”server” /> to the very end of your master page, any administrator surfing the site will get a new item on his/her EPiServer context menu:
When they click that item a list of all overrideable texts used for that page rendering will be shown in the lower right hand corner of the page:
If the administrator clicks on one of the texts then an editor is shown in a lightbox:
Now the administrator may input a new string into the textbox and click save
on refreshing the page the globalized text will now be changed:
What happened here was that every time EPiLang is asked to fetch a string, it registers it on the HttpContext.Current.Request.Items array, so that when the editor is rendered it can produce a list of all the relevant strings.  The mechanism used for localizing this string here (this is taken from the AlloyTech sample provided by EPiServer) is like this:
EPiServer has defined its own resourcefactory which enables this syntax, however EPiLang substitutes this factory with  its own during installation and so is able to override which string is actually displayed.
There is a way to enable administrators to just click on the text inline instead of finding it in the list on the lower right hand side, we can exchange the code above with a strongly typed editable string:
which in turn light up the string when an admin enables editing of globalized strings through the context menu
The editable text is in fact a <span> tag with some attributes set which enables the editing
Overridden strings are not saved back to the xml file, as the next deploy would probably undo what the admin just did. Instead they are saved into EPiServers Dynamic Data store, along with the original string. That way if the string in the xml file changes for any reason then that value is used (until it is overridden at a later stage). Using this setup ensures that the latest write wins.
If you want to play around with EPiLang you are more than welcome to do so. It is CMS6 only and you will find it on nuget under the name Forse.EPiLang
When you install it it modifies your web.config slightly, it:
  • Adds Forse.EPiLang to pages/namespaces
  • Adds Forse.EPiLang.Controls to pages/controls
  • Adds a httphandler to handle the overriding commands (from the lightbox)
  • Exchanges the resourceProviderFactoryType with a new one.
on uninstall all these changes are reverted.
You will also get a readme file under the App_Readme folder. It contains the instructions on what to do to get up and running.
If this is useful for anyone I’ll put it up on EPiCode. Any feedback is much appreciated
Advertisements

May 2, 2011

EPiServer CMS 5 / PageTypeBuilder 1.1 / UnmappablePropertyTypeException

Filed under: Uncategorized — andreakn @ 09:18

I never work on EPiServer projects without PageTypeBuilder anymore, it’s such a great tool, however version 1.1 (the one to use for EPiServer CMS 5) has a really annoying “bug/feature” in it:

If you add a new custom property type and make use of it all in one go then PTB will *sometimes* throw an UnmappablePropertyTypeException. The reason is that EPiServer does not yet know of a given property, (the reflection magic which makes EPi startup times such a nightmare has not yet been executed on the assembly containing the prop and thus registered the prop to the EPi DB)

(this is not a problem on later PTB versions, but they don’t support EPiServer CMS 5)

the reason this only crops up *sometimes* is that the order in which EPi CMS 5 does its thing is important. If it finds the DLL with the prop first then you are ok, if it finds the PTB page type definitions first then you get the exception. This wouldn’t be such a huge problem if EPi continued wth dll registration after PTB throws up, so that the property in question would get into the DB and thus be ready for the next time PTB starts up, but then I wouldn’t be writing this blogpost if it did.

Even if you are really careful with always adding the custom property first, starting the site, adding the use of the property to a PTB page type, starting the site again, you are vulnerable when deploying to a new system (test / stage / prod) or when rolling back to a previous version of the EPiServer DB.

PageTypeBuilder comes with a funnily named config setting you can enable: “disablePageTypeUpdation”, but setting this to true does not stop the exception from cropping up, as the thing you are disabling is the “updation” of already known page types, it still goes through and adds new pagetypes, and more importantly: it validates the entire PTB setup, which fails as there is an unknown property type.

So I made my own version of PTB 1.1 which actually lets you disable it completely so that it doesn’t throw a spanner in the works when registering new custom properties, the new property is called disablePageTypeBuilder, and its implementation is thusly:

\Configuration\PageTypeBuilderConfiguration.cs

...
        [ConfigurationProperty("disablePageTypeBuilder", IsRequired = false)]
        public virtual bool DisablePageTypeBuilder
        {
            get
            {
                return (bool)this["disablePageTypeBuilder"];
            }
        }
...

\Initializer.cs :

...
  public static void Start()
        {
            lock (_lockObject)
            {
                if (_started)
                    return;

                PageTypeBuilderConfiguration configuration = PageTypeBuilderConfiguration.GetConfiguration();
                if (!configuration.DisablePageTypeBuilder)
                {
                    PageTypeSynchronizer synchronizer = new PageTypeSynchronizer(new PageTypeDefinitionLocator(),
                                                                                 configuration);

                    synchronizer.SynchronizePageTypes();

                    DataFactory.Instance.LoadedPage += DataFactory_LoadedPage;
                    DataFactory.Instance.LoadedChildren += DataFactory_LoadedChildren;
                    DataFactory.Instance.LoadedDefaultPageData += DataFactory_LoadedPage;
                }

                _started = true;
            }
        }
...

\Synchronization\Validation\UnmappablePropertyTypeException.cs

    public class UnmappablePropertyTypeException : PageTypeBuilderException
    {
        private const string NewMappingsHint =
            @"
You may try to disable PageTypeBuilder using the pagetypebuilder config section
( <section name=""pageTypeBuilder"" type=""PageTypeBuilder.Configuration.PageTypeBuilderConfiguration, PageTypeBuilder""/> )
with attribute <pageTypeBuilder disablePageTypeBuilder=""True"" />
and run the site once without PTB to register any new custom properties first,
then reactivate PTB to make use of new properties";

        public UnmappablePropertyTypeException()
        {
        }

        public UnmappablePropertyTypeException(string message) : base(message + NewMappingsHint)
        {

        }

        public UnmappablePropertyTypeException(string message, Exception innerException)
            : base(message + NewMappingsHint, innerException)
        {
        }
    }

This means that when you get the exception you can just turn off PTB, restart, then turn on PTB and restart and you’re set

You disable PageTypeBuilder like this in web.config:

...
    <section name="pageTypeBuilder" type="PageTypeBuilder.Configuration.PageTypeBuilderConfiguration, PageTypeBuilder"/>
...
    <pageTypeBuilder disablePageTypeBuilder="true" />
...

If you’re too lazy to compile it yourself you can download this file: PTB_With_Shutoff_switch.zip, and change from .doc to .zip, (thanks a lot wordpress….) unzip it and use it instead of the regular PTB1.1

Blog at WordPress.com.