3 Comments

In a quite recent blog post by Per Bjurström of EPiServer he wrote about a new database version for EPiServer CMS where they actually include the database schema changes in the nuget update package.

This is an awesome step in the right direction where everything that is needed to upgrade an EPiServer site is included in the nuget package, but… How do we integrate this with our continuous integration process?

Turns out it’s not so difficult at all. Looking at this blog post by Paul Stovell, the man behind Octopus Deploy, we can get some inspiration as to how this could be achieved.

Lets start by adding a console application project to our solution and add the DbUp nuget package. This will be the project that contains all of our database schema changes, in most cases probably only the SQL scripts from EPiServer, but we could of course have different databases that also need to update their schema from time to time.

We then update our EPiServer nuget packages. If we try to run our site now we’ll get the old familiar yellow screen of death saying: “The database has not been updated to the version 7007.0, current database version is 7006.0.”

dberror

This is easily remedied by running the “update-epidatabase” command from the Nuget Package Manager Console, however this will only fix our development database. The database will still be out of sync when we deploy this release to any other environment. We need to extract the SQL script from the nuget package by using the “export-epiupdates” command:

PM> Export-EPiUpdates An Export package is created C:\EPiServer\AlloyDemo\wwwroot\EPiUpdatePackage Exporting epiupdates into EPiUpdatePackage\EPiServer.CMS.Core.7.8.2\epiupdates

This will create a folder in our project root where we can find the sql scripts being run: EPiUpdatePackage\EPiServer.CMS.Core.7.8.2\epiupdates\sql

episql

 

We’ll grab the 7.8.0.sql file from the folder and include it in our console application and make sure to set the build action to “embedded resource” to make the sql script a part of the generated .exe file.

episql2

Next we’ll add our episerver database connection string to our App.config.

connectionstring

It doesn’t really matter what you call the connectionstring here and you could of course have several if you have multiple databases that need to be updated.

Then we write som code to trigger the update by DbUp in the main method of our console app. This code is taken directly from Paul Stovells blog post and works just perfectly.

DbUp will automatically wrap all the calls in a transaction that will only be committed if all of the calls are successful. This is really nice as it means that we won’t have to worry about leaving our database in a half-upgraded state of some kind.

static int Main(string[] args) { //Grab a reference to our connectionstring var connectionString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString; //DeployChanges is a fluent builder for creating databases. //There are lots of options other than executing scripts embedded in the assembly, //including from a specified file location or manually created scripts. var upgrader = DeployChanges.To .SqlDatabase(connectionString) .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()) .LogToConsole() .Build(); var result = upgrader.PerformUpgrade(); //If the result is unsuccessful we'll change the fore color to red and display the error if (!result.Successful) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(result.Error); Console.ResetColor(); return -1; } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Success!"); Console.ResetColor(); return 0; }

DbUp is also clever enough to add a simple database table storing every script that has already been executed so we do not need to worry about unnecessarily running scripts multiple times.

We could run this application right now, as is, and it would upgrade our database for us, but that’s not our goal right now. We want Octopus Deploy (or whichever deployment service you use) to be able to run this app and upgrade our databases automatically. That’s why we also add a simple powershell script that Octopus deploy will run as part of the deployment process. We put this in a Deploy.ps1 file and include it in our console project, making sure to put Copy to output directory to Copy if newer. This file will be automatically picked up by Octopus and run when the project has been deployed.

deployps

The Deploy.ps1 file looks like this (again courtesy of Paul Stovell):

& .\OctoSample.Database.exe | Write-Host

As I mentioned in a previous blog post I currently use TeamCity as my build server of choice and let TeamCity run Octopack to package my projects into nuget packages that Octopus deploy then grabs and deploys. To be able to let TeamCity pack our console application we need to install the OctoPack nuget package. Then when we check in our changeset TeamCity will pick it up and run OctoPack automatically, creating a nuget package for every project with OctoPack installed.

The last step to get the whole process to work is configuring Octopus Deploy. Simply add a step to the deployment process called “Update database” or something similar where you fetch the nuget package “EpiDbUp”, deploy it and execute the resulting .exe file to update the database.

octopusdeploy

The complete process now looks something like this:

  • Include the .sql file we want to run in our EPiDbUp-project (making sure to embedd it into the .exe).
  • Check it in to source control. Which triggers a build on our TeamCity Build server which upon completion triggers the creation of a release in Octopus Deploy.
  • Choose which environment we wish to deploy the current release to.
  • The deployment process runs in two steps, first updating the database through our console project, then deploying our web application.

And that’s it! The next time a database schema change is included in an EPiServer update we’ll simply add the script to our database project and all our environments will be automatically updated on next deploy.

0 Comments

Lately I’ve been working with streamlining a clients deployment process for several different EPi-sites. The CI server of choice is TeamCity and we decided to go with Octopus deploy when it came to automating deployment. The result is a pretty awesome continuous delivery process where we can push code from any check in directly to production should we like to.

Just one of the many features of Octopus is that it can easily run all your config transforms for you. And not only web.config, any config! This means we can easily make the pesky episerver.config and episerverframework.config transforms that we unavoidably need when we deploy any EPiServer site.

We ran into just one minor speed bump during the deployment process. It’s spelled license.config. Normally you just throw up the license.config for your different environments and leave it there, but it turns out that Octopus creates a new fresh directory for every deploy which means that if the license.config is not part of our deployment process it won’t get deployed to the new directory and we’ll face something similar to this beauty:

licenseerror

Ah, the joy!

Turns out it’s really easy to fix this though. License.config is just another configuration file really, lets just transform it! However every developer has it’s own during development, so how do we do that?

We first need to add license.config to source control. Just add an empty one like this in a /licenses/ folder.

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <!--This license file is just here for the transforms it's nothing to worry about, just put your own license.config in the site root as usual (but do not check it in to source control)--> </Signature>

Add the appropriate config transform files e.g. license.staging.config, license.release.config etc. and make sure to run an insert on the appropriate sections:

<SignedInfo xdt:Transform="Insert" /> <SignatureValue xdt:Transform="Insert" /> <KeyInfo xdt:Transform="Insert" />

And then update your episerver.framework.config transforms to make sure it points to the new location of the license.config files.

<?xml version="1.0" encoding="utf-8" ?> <episerver.framework xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <licensing xdt:Transform="Insert" licenseFilePath="licenses/license.config" /> </episerver.framework>

Note that this is for EPiServer 7. For EPi 6 you would need to transform the episerver.config (Check out this blog post for more info) instead, for EPi 5 you would just sit around and look sad that you haven’t upgraded yet.

That’s nice! We now have a continuous delivery process. Just one more thing… When we deploy through Octopus it does not automatically remove our config transformation files which leaves alot of junk web.environment.config files lying around. Unfortunately there’s no built in feature in Octopus that handles this (although there was an issue raised), but this is easily handled through a simple powershell script.

We add a postdeploy.ps file to our solution (make sure to set the Build Action to “Content” on the file). This file will be automatically picked up by Octopus and the script will run after deployment has completed.

Here’s the script:

get-childitem .\ -include *debug.config, *release.config, *staging.config -recurse | foreach ($_) {remove-item $_.fullname -force }

And that’s it. Continous delivery made easy with TeamCity and Octopus for EPiServer websites.

1 Comments

I had a strange experience when upgrading a clients site from EPiServer 7 to EPiServer 7.5. Suddenly the site performed noticeably worse with loading times being measured in seconds. At one point it was so bad the site was hardly useable.

Naturally we initially blamed the new code we had deployed and tried to investigate where we had gone wrong performance-wise, but after some further investigation it became clear that the performance drain was not coming from any new code but rather from multiple calls to the EPiServer Dynamic Data Store (DDS). This was very strange as we had made no changes to the code handling the DDS calls and would therefore assume that the performance would be similar as before the upgrade. The performance was so appallingly bad that we had to temporarily disable some of the functionality of the site. Hurriedly we rewrote parts of the application to instead use Entity Framework and most of the performance issues went away.

While this might have been an edge case (thousands upon thousands  of objects in the DDS with many nested objects and relations) and certainly the DDS implementation could have been rewritten to perform better it still raises an interesting question.

Just how badly does the DDS perform in comparison to EF 6.1?

I decided to create a few simple test scenarios and try to make an even comparison.

First I created a few POCO objects to store in the respective ORM:

    [EPiServerDataStore(AutomaticallyCreateStore = true, AutomaticallyRemapStore = true)]
    public class DdsLike : IDynamicData
    {
        public string User { get; set; }
        public int PageId { get; set; }

        

        public Identity Id
        {
            get;
            set;
        }
    }

    [EPiServerDataStore(AutomaticallyCreateStore = true, AutomaticallyRemapStore = true)]
    public class DdsComment : IDynamicData
    {
        public string User { get; set; }
        public int PageId { get; set; }
        public string Text { get; set; }
        public string Heading { get; set; }

        public Data.Identity Id
        {
            get;
            set;
        }
    }

   public class EfComment
    {
        public int Id { get; set; }
        public string User { get; set; }
        public int PageId { get; set; }
        public string Text { get; set; }
        public string Heading { get; set; }
    }

    public class EfLike
    {
        public int Id { get; set; }
        public string User { get; set; }
        public int PageId { get; set; }
    }

Likes and comments the bread and butter of the web!

I then created 20 000 objects (10 000 of each entity) in both Entity Framework (using EF code first) and the DDS:

        public void InitializeData()
        {
            var itemCount = 10000;

            using (var alloyContext = new AlloyContext())
            {
                for (int i = 0; i < itemCount; i++)
                {
                    alloyContext.Likes.Add(new EfLike() { 
                        PageId = i, 
                        User = "bla"
                    });
                    alloyContext.Comments.Add(new EfComment() { PageId = i, User = "bla", 
                        Heading = "The raven", Text = "Once upon a midnight dreary, while I pondered weak and weary",
                    });

                }

                alloyContext.SaveChanges();
            }

            var likeStore = DynamicDataStoreFactory.Instance.GetStore(typeof(DdsLike));
            var commentsStore = DynamicDataStoreFactory.Instance.GetStore(typeof(DdsComment));

            var ddsLikes = new List<DdsLike>();
            var ddsComments = new List<DdsComment>();

            for (int i = 0; i < itemCount; i++)
            {
                likeStore.Save(new DdsLike()
                {
                    PageId = i,
                    User = "bla",
                    
                });
                commentsStore.Save(new DdsComment() { PageId = i, User = "bla", 
                    Heading = "The raven", Text = "Once upon a midnight dreary, while I pondered weak and weary" });
            }

        }

I then started measuring different calls. The procedure was to setup a scenario and measure it 5000 times with EF and DDS respectively, measuring the average time of all the calls made. Here’s the breakdown of the result:

Procedure DDS EF
Load 20 000 items, return count 19 ms 12 ms
Load 20 000 items, sort on pageid > 5000, orderby pageId, return count 88 ms 10 ms
Load 20 000 items, sort on pageid > 5000 && < 15000, orderby pageId, return count 104 ms 11 ms
Load 20 000 items, sort on pageid > 5000 && < 15000, orderby pageId, tolist, count 840 ms 11 ms

Load 20 000 items, get by id 5555, orderby pageid, tolist, count

40 ms 8 ms
Load 20 000 items, order by pageid, take 20, tolist, count 313 ms 33ms
Load 20 000 items, order by pageid, skip 80, take 20, tolist, count 420 ms 42 ms
Load 20 000 items, order by pageid, skip 5000, take 20, tolist, count 390 ms 190 ms

Returning a count of 20 000 items is of course no problem for either mapper, but it’s interesting to note how quickly the performance of the DDS deteriorates as soon as we add some ordering and filtering. Especially noteworthy is when we load our 20 000 items, sort on pageId > 5000 and < 15 000, order by pageId then make a ToList() call, effectively forcing the execution of the query against the database. 840 ms vs 11 ms!

To make things a bit more interesting I reran the test but added some related objects in both databases. I added two nested classes to the Comments class for the DDS and a simple reference to two other classes in EF.

public class DdsSomeNestedClass
    {
        public int MyProperty { get; set; }
        public string MyString { get; set; }
        public Guid Id { get; set; }
        public int PageId { get; set; }
        public string SomethingElse { get; set; }
        public Guid GuidId { get; set; }
    }

    public class DdsSomeOtherClass
    {
        public string MyProperty { get; set; }
        public int SomeId { get; set; }
        public int PageId { get; set; }
        public string Something { get; set; }
        public DateTime Date { get; set; }
        public DateTime ChangedDate { get; set; }
        public Guid GuidMaster { get; set; }
    }

I ran all the tests again and performance went down quite dramatically for the DDS while EF kept going at about the same pace:

Procedure DDS EF
Load 20 000 items, return count 11 ms 12 ms
Load 20 000 items, sort on pageid > 5000, orderby pageId, return count 78 ms 8 ms
Load 20 000 items, sort on pageid > 5000 && < 15000, orderby pageId, return count 78 ms 13 ms
Load 20 000 items, sort on pageid > 5000 && < 15000, orderby pageId, tolist, count 1346 ms 212 ms

Load 20 000 items, get by id 5555, orderby pageid, tolist count

42 ms 6 ms
Load 20 000 items, order by pageid, take 20, tolist, count 407 ms 33ms
Load 20 000 items, order by pageid, skip 80, take 20, tolist, count 420 ms 36 ms
Load 20 000 items, order by pageid, skip 5000, take 20, tolist, count 500 ms 40 ms

There’s absolutely nothing scientific about the tests, but it’s still interesting to note how poorly the DDS performs in comparison on certain calls. Especially when filtering and sorting is involved.

To conclude we can say that using EF (or some other ORM that does not rely on a big table solution) is always preferable. If you’re going to use the DDS make sure to cache your calls and try to keep them as simple as possible for best performance.

0 Comments

I normally use jQuery validation any chance I get when it comes to validation, but sometimes you find yourself in situations where this isn’t the best option. jQuery validation normally doesn’t play very nice with ASP.NET web forms and it’s not unusual to have to make all kinds of adaptions for custom validation scenarios (in ASP.NET MVC it works much better).

I was recently asked to make several updates to an existing ASP.NET web forms application that included several forms of varying complexity. They all relied heavily on ASP.NET validation controls. One feature that was requested was to have validation as the user types and not on blur, another was to have icons signaling whether an input field had been successfully validated or not. Both notoriously difficult to implement using the traditional validation controls of ASP.NET web forms.

This brings us to a choice. Do we remove the validation controls and rewrite the validation using jQuery validation and custom server side validation? Do we keep the validation controls and try to customize them to accommodate the new client requests?

In this particular case I really didn’t want to have to rewrite all the validation and customizing the validation controls didn’t sound like a whole lot of fun either, so I tried to find some kind of a middle way. I came up with some sort of mysterious ASP.NET web controls and client side jQuery validation hybrid that look like this:

//Create some sort of entry object. validation is as good a name as any
var validation = {
    init: function () {
        var elems = $('body').find('[data-validation]');
        elems.each(function (index, elem) {
            $(elem).on('blur keyup change', function () {
                validation.validateElement($(this));
            });
        });
    },
    validateElement: function (elem) {
        var validationRules = $(elem).attr('data-validation').split(' ');

        var valid = false;

        for (var i = 0; i < validationRules.length; i++) {
            var func = validation.rules[validationRules[i]];

            valid = func($(elem).val());
            if (!valid)
                break;
        }

        var after = $(elem).next();
        if (after.hasClass('validationResult')) {
            after.remove();
        }

        if (valid) {
            //Do whatever you want to happen when an element is valid
            $(elem).after('<span class="validationResult success"><img class="validation-img" src="/Templates/Static/Images/check.png"/></span>');
        } else {
            //Do whatever you want to happen when an element is invalid
            $(elem).after('<span class="validationResult fail"><img class="validation-img" src="/Templates/Static/Images/cross.png"/></span>');
        }
    },
    rules: {
        required: function (val) {
            return val != undefined && val.trim() != '';
        },
        personalNumber: function (val) {
            //we allow null or empty values
            if (val == '' || val == undefined)
                return true;

            var persRegex = /^\d{6}-\d{4}$/;
            return persRegex.test(val);
        },
        email: function (val) {
            //we allow null or empty values
            if (val == '' || val == undefined)
                return true;

            var emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
            return emailRegex.test(val);
        },
        phone: function (val) {
            //we allow null or empty values
            if (val == '' || val == undefined)
                return true;

            var phoneRegex = /^(\+|[0])\d[0-9- ]{5,}$/;
            return phoneRegex.test(val);
        },
        number: function (val) {
            if (val == '' || val == undefined)
                return true;

            var numberRegex = /^\d{1,}$/;
            return numberRegex.test(val);
        }
    }
}

Nothing complicated. We simply hook up our validation to any elements with the data-validation attribute. Then we create a generic validation method that looks at the values of the attribute and calls the appropriate validation method.

Simple and extendable. Without the actual rules it’s just shy of 30 lines of javascript.

This way I could leave my ASP.NET validation controls as they were and hook this up to the existing textboxes something like this:

<asp:TextBox id="tb1" data-validation="required number" runat="server" />
And that’s it. Although bear in mind that I have attached no logic to the submit button at this point (in my case it wasn’t necessary).

2 Comments

Creating custom page properties in EPiServer has undergone some changes in the latest version of the CMS. No more ASP.NET server side controls. Instead you are required to write a client side representation of your property in Dojo (or more accurately in  Dijit). A choice criticized by many, but once you’ve gotten the hang of the Dojo way of doing things it actually becomes a quite reasonable choice. Dojo is a widely used and very well documented toolkit that excels in widget-like functionality and offers a very wide arrange of different base classes to inherit functionality and properties from. The documentation is excellent (unfortunately the EPiServer documentation is extremely lacking) and as long as you have a basic understanding of JavaScript creating simple plugins is trivial.

The first thing you need to understand is the anatomy of a Dijit plugin, so lets have a look.

define([
    // These are the dependencies for our created widget.
    'dojo/dom',
   '_templatedMixin'
], function(dom, _templatedMixin){
    // Once all modules in the dependency list have loaded, this
    // function is called. It's important to understand that this function need to be 
    // passed all the modules in your dependency list, for example we have the 'dojo/dom' dependency
    // which is why we pass our function an argument of dom.
    // This is so that we can access those dependencies in our actual widget.
   
    declare('namespace.ourawesomeproperty', _templatedMixin, {
           // The next step is to call the declare function
           // The first argument is the name and optional namespace of our newly created widget
           // The second argument is the widget we inherit from.
          // If we want to inherit several widgets we can put them in an array []
          // The third argument is this javascript object (the one that contains this comment).
         // This is where we declare any custom properties or functions for our widget
        myprop: 'Dojo is awesome!'
    });
});
It looks more daunting than it is. Dijit widgets are defined in the AMD (Asynchronous Module Definition) pattern where you first define all the dependencies of your module, then declare your actual JavaScript object (with the declare function).

 

But once we have a Dijit module defined and ready to go we need to somehow hook it up to EPiServer and tell EPi which property we want to use our shiny module for. This isn’t very difficult either. We simply decorate any property on a ContentType with the ClientEditor  attribute, like this:

[ClientEditor(ClientEditingClass = "ournamespace.ourawesomewidget")]
public virtual string SomeProperty { get; set; }

or if you wish to use your editor for several properties you can create an EditorDescriptor and define which dijit module should be used for properties of a certain type, something like this:

[EditorDescriptorRegistration(TargetType = typeof(string))]
public class StringEditorDescriptor : EditorDescriptor {

    public StringEditorDescriptor () {
          this.ClientEditingClass = "ournamespace.ourawsomewidget";
    }
}

And suddenly all strings will be handled by the “ournamespace.ourawesomewidget” module. Pretty neat huh?

But hey, wait a minute, where do we actually put our created Dijit module for EPiServer to find it? This is an excellent question and here we unfortunately need to mix in some site configuration. In the module.config  you can define where the Dojo loader should look for files with a specific namespace. In the alloy templates site we find this:

    <dojo>
      <!-- Add a mapping from alloy to ~/ClientResources/Scripts to the dojo loader configuration -->
        <paths>
            <add name="alloy" path="Scripts" />
        </paths>
    </dojo>

Ok, so there’s a <dojo> tag where we can add <paths>, apparently this automatically maps to the ~/ClientResources/ root folder. Also worth noting is that the name of our added path (in this case alloy) must correspond exactly to the namespace of our modules for Dojo to understand where to look for them, this means that if your namespace alloy.somenamespace.widgetname with  a modules config as the one above the path to your widget must be ~/ClientResources/Scripts/somenamespace/widgetname.js.

I hope all that made sense…

Anyway, enough with the background already, lets actually build something. Imagine our client wants to add some sort of property editor for creating polls to their site. Each poll has a title and a couple of possible answers.

Allright, lets start with modeling the data real quick. I’m gonna use ASP.NET Web Api as the back end for our polls.

    public class PollsController : ApiController
    {
        public List<SimplePoll> data = new List<SimplePoll>() {
            new SimplePoll {
                    Header = "Who's your favorite chess player?",
                    PollId = 1,
                    Answers = new List<SimpleAnswer> {
                        new SimpleAnswer() {
                            Answer = "Bobby Fischer",
                            AnswerId = 1
                        },
                        new SimpleAnswer() {
                            Answer = "Magnus Carlsen",
                            AnswerId = 2
                        },
                        new SimpleAnswer() {
                            Answer = "José Raul Capablanca",
                            AnswerId = 3
                        },
                        new SimpleAnswer() {
                            Answer = "Garry Kasparov",
                            AnswerId = 4
                        },
                        new SimpleAnswer() {
                            Answer = "Emanuel Lasker",
                            AnswerId = 5
                        },
                    }
                }, 
                new SimplePoll {
                    Header = "What's your favorite chess opening?",
                    PollId= 2,
                    Answers = new List<SimpleAnswer> {
                        new SimpleAnswer() {
                            Answer = "Sicilian dragon",
                            AnswerId = 1
                        },
                        new SimpleAnswer() {
                            Answer = "Scottish",
                            AnswerId = 2
                        },
                        new SimpleAnswer() {
                            Answer = "Queens Gambit",
                            AnswerId = 3
                        },
                        new SimpleAnswer() {
                            Answer = "English",
                            AnswerId = 4
                        },
                        new SimpleAnswer() {
                            Answer = "Kings gambit",
                            AnswerId = 5
                        },
                    }
                }
        };

        // GET api/<controller>
        public IEnumerable<SimplePoll> Get()
        {
            return data;
        }

        public SimplePoll Get(int id)
        {
            return data.FirstOrDefault(p => p.PollId == id);
        }

        public void Delete(int id)
        {
            data.Remove(data.FirstOrDefault(p => p.PollId == id));
        }
    }

    public class SimplePoll
    {
        public int PollId { get; set; }
        public string Header { get; set; }
        public List<SimpleAnswer> Answers { get; set; }
    }

    public class SimpleAnswer
    {
        public int AnswerId { get; set; }
        public string Answer { get; set; }
    }

Just a real simple API controller with an in memory representation of our data. We’re going to need an answers controller as well...

public class AnswersController : ApiController
    {
        public void Delete(int id)
        {
            //Nah, don't wanna delete it
        }
    }

Ok, well that would of course work a little bit different if we actually had some sort of database or something to persist our polls and answers to, but we don’t, so lets just have a look at the Dijit now…

We start by configuring up a new namespace in the module.config (we could of course use the already existing alloy namespace, but where’s the fun in that?). Note how we can have two namespaces pointing to the same path without a problem.

 <dojo>
        <!-- Add a mapping from alloy to ~/ClientResources/Scripts to the dojo loader configuration -->
        <paths>
            <add name="alloy" path="Scripts" />
            <add name="whatever" path="Scripts" />
        </paths>
    </dojo>

I create a Dijit module PollCreation.js:

define([
    "dojo/_base/connect",
    "dojo/_base/declare",
    "dojo/store/Memory",

    "dijit/_CssStateMixin",
    "dijit/_Widget",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/form/FilteringSelect",
    "dijit/TitlePane",

    "epi/dependency",
    "epi/epi",
    "epi/shell/widget/_ValueRequiredMixin",
    "epi/shell/widget/dialog/Dialog",
    "epi/cms/widget/_HasChildDialogMixin",

    "whatever/editors/PollDialog",
    "whatever/editors/DeleteableTextBox",
    "dojo/text!./templates/poll.html"
],
function (
    connect,
    declare,
    Memory,

    _CssStateMixin,
    _Widget,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    FilteringSelect,
    TitlePane,

    dependency,
    epi,
    _ValueRequiredMixin,
    Dialog,
    _HasChildDialogMixin,

    PollDialog,
    DeleteableTextBox,
    template
) {

    return declare("whatever.editors.PollCreation", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin, _HasChildDialogMixin], {

        templateString: template,
        intermediateChanges: false,
        value: null,
        store: null,
        currentPollData: null,
        postCreate: function () {
            // call base implementation
            this.inherited(arguments);

            this.connect(this.selectPolls, "onChange", this._onInputWidgetChanged);

            this._refreshDropDown(this.get('value'));
            this._refreshAnswers(this.get('value'));
        },
        isValid: function () {
            return this.selectPolls.isValid();
        },
        // Setter for value property
        _setValueAttr: function (value) {
            this.selectPolls.set("value", value);
            this._set("value", value);
        },
        _setReadOnlyAttr: function (value) {
            this._set("readOnly", value);
            this.selectPolls.set("readOnly", value);
        },
        _updateValue: function (value) {
            if (this._started && epi.areEqual(this.value, value)) {
                return;
            }

            this._set("value", value);
            this._refreshAnswers(value);
            this.onChange(value);
        },
        _createDialog: function () {

            this.pollDialog = new PollDialog({
            });

            this.dialog = new Dialog({
                title: "New poll",
                content: this.pollDialog,
                dialogClass: "epi-dialog-portrait"
            });

            this.connect(this.dialog, 'onExecute', '_onExecute');
            this.connect(this.dialog, 'onHide', '_onDialogHide');

            this.dialog.startup();
        },
        _onDialogHide: function () {
            this.focus();
        },
        _onButtonClick: function () {

            if (!this.dialog) {
                this._createDialog();
            }

            this.dialog.show(true);
        },
        _onExecute: function () {           
            var pollData = this.pollDialog.get('value');
            var poll;
            jQuery.ajax({
                url: '/api/polls?pollHeading=' + pollData.pollHeading + '&answers='+pollData.answers,
                type: 'POST',
                contentType: 'application/json; charset=utf-8',
                success: function (result) {
                    poll = JSON.parse(result);
                },
                async: false
            });

            this._refreshDropDown(poll.PollId);
            this.selectPolls.refresh();
        },
        _refreshDropDown: function (selected) {
            
            var pollData = [];
            jQuery.ajax({
                url: '/api/polls',
                contentType: 'application/json; charset=utf-8',
                success: function (result) {
                    result = JSON.parse(result);
                    //push empty result in case they do not want a poll
                    pollData.push({ name: '', id: -1 });
                    for (var i = 0; i < result.length; i++) {
                        pollData.push({ name: result[i].Header, id: result[i].PollId });
                    }
                },
                async: false
            });

            var store = new Memory({
                data: pollData
            });

            this.selectPolls.store = store;
            this.selectPolls.set('value', selected);
        },
        _refreshAnswers: function (selected) {
            this.titlePane.destroyDescendants();
            
            if (selected <= 0)
                return;

            var selectedPoll;
            jQuery.ajax({
                url: '/api/polls/'+selected,
                contentType: 'application/json; charset=utf-8',
                success: function (result) {
                    selectedPoll = JSON.parse(result);
                    
                },
                async: false
            });
            
            for (var i = 0; i < selectedPoll.Answers.length; i++) {
                var textBox = new DeleteableTextBox({
                    name: 'text',
                    readOnly: true,
                    value: selectedPoll.Answers[i].Answer,
                    style: 'display:table; margin-bottom:10px;',
                    answerId: selectedPoll.Answers[i].AnswerId
            });
                this.titlePane.addChild(textBox, i);
            }
        },
        _onDeletePollClick: function() {
            if (confirm('Are you sure you want to delete this magnificent poll?')) {
                jQuery.ajax({
                    url: '/api/polls/' + this.get('value'),
                    type: 'DELETE',
                    contentType: 'application/json; charset=utf-8',
                    success: function (result) {
                    },
                    async: false
                });

                this._refreshAnswers(-1);
                this._refreshDropDown(-1);
            }
        },
        _onInputWidgetChanged: function(value) {
             this._updateValue(value);
        }
    });
});

Allright, now that’s quite a mouthful of javascript. Lets go through that top to bottom. First we declare our dependencies:

define([
    //These are some basic dojo-modules we need
    "dojo/_base/connect",
    "dojo/_base/declare",
    "dojo/store/Memory",

    //Some dijits that we want to utilize and inherit
    "dijit/_CssStateMixin",
    "dijit/_Widget",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/form/FilteringSelect",
    "dijit/TitlePane",

    //Oh look, epi has quite a few modules that we can use as well.
    //Unfortunately there's close to zero documentation, but hopefully we'll get some in the future.
    //Anyway, these are some modules that we need...
    "epi/dependency",
    "epi/epi",
    "epi/shell/widget/_ValueRequiredMixin",
    "epi/shell/widget/dialog/Dialog",
    "epi/cms/widget/_HasChildDialogMixin",

    //Look at this, I've even put some dependencies to modules in the same namespace
    // as our PollCreation dijit. I'll get back to these in a second. 
    "whatever/editors/PollDialog",
    "whatever/editors/DeleteableTextBox",
    "dojo/text!./templates/poll.html"
   //This last one is interesting... We actually say here that we have a dependency to a plain html file
   // this file needs to be loaded and put into memory before we can run our module, I'll get back to this one as well.
]

We then pass in our function which has an argument for each of our dependencies:

function (
    connect,
    declare,
    Memory,

    _CssStateMixin,
    _Widget,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    FilteringSelect,
    TitlePane,

    dependency,
    epi,
    _ValueRequiredMixin,
    Dialog,
    _HasChildDialogMixin,

    PollDialog,
    DeleteableTextBox,
    template
) {

You could name these arguments anything you want, but it’s good practice to name them the same as the dependency.

Allright, moving on… we then from our function return our declared dijit module:

 return declare("whatever.editors.PollCreation", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin, _HasChildDialogMixin], {

        templateString: template,
        intermediateChanges: false,
        value: null,
        store: null,
        currentPollData: null,

Note how we inherit all functionality from _Widget, _TemplatedMixin, _WidgetsInTemplateMixin etc. especially the _TemplatedMixin is interesting as it allows us to use a HTML template for our module. Remember "dojo/text!./templates/poll.html"  from our dependencies? This is where we make use of it. We’ve put the value of the loaded poll.html into the template variable. The path to our poll.html is relative to our module which means that the complete path to the html template is ~/ClientResources/Scripts/Editors/templates/poll.html. All right, so we’ve put the html template into the templateString variable inside our module, which means our module can access that variable using this.templateString which we shall see in practice soon.

Here’s the poll.html template:

<div class="dijitInline">
    <div style="position: relative;">
        <div data-dojo-attach-point="selectPolls" data-dojo-type="dijit.form.FilteringSelect" data-dojo-props="autoComplete: false, required: false" style="width: 400px"></div>
        <span class="epiDeleteIcon" data-dojo-attach-event="click: _onDeletePollClick"
              style="width:16px; height: 16px; display:inline-block; display: inline-block; margin-left: 5px; 
position: relative; top: 3px; background: url('../Shell/7.7.0.0/ClientResources/epi/themes/sleek/epi/images/icons/commonIcons16x16.png'); background-position-y: -1168px;" alt=""></span>
        <span data-dojo-attach-point="addButton" data-dojo-attach-event="click: _onButtonClick"
              style="position:relative; top: 3px; width: 16px; height: 16px; display: inline-block; 
background: url('../Shell/7.7.0.0/ClientResources/epi/themes/sleek/epi/images/icons/commonIcons16x16.png'); background-position-y: -1216px;"></span>
    </div>
    <div style="margin-left:20px;margin-top:10px;" data-dojo-attach-point="titlePane" data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Answers'">
    </div>
</div>

Yeah, it’s quite a mess but not really that complicated. It’s very important to note that any template that is to be used by a Dijit module must have exactly one root element. If you by mistake add two divs as root element for example the EPiServer forms editing mode will simply fail to load… silently. No errors! Nothing! This can be very frustrating, but as long as you remember to use only one root node you should be fine.

Anyway, lets look at the interesting parts of the template. We have our root node (just a plain old div) and then another div with  position relative. Then we have a div with some dojo-markup on it, the data-dojo-attach-point tells our module that we want to reference this div from our JavaScript file using the selectPolls variable and the data-dojo-type attribute tells us that this div is actually a dijit.form.FilteringSelect module and as such has a lot of functionality and properties of it’s own (this is why we need to declare a dependency to the _WidgetsInTemplateMixin, we have widgets in our template). We then have a couple of buttons for deleting and creating a poll, they have data-dojo-attach-event attributes to specify what functions in our module should be called when they are clicked. Finally we have another module which is a dijit.TitlePane that will show our answers.

Like I said, not too complicated, but a lot of steps already and we haven’t even looked at the module in editmode yet!

Lets keep going through our module, next up are all the functions.

//The postCreate function is called directly after our module has been created.
//This is the place to do any intialization that we need. 
postCreate: function () {
            // this.inherited is a function in Dojo that lets us call the base implementation
            // of the currently executing function. Pretty useful!
            this.inherited(arguments);
            
            //We connect any change events of our filteringselect to our own function defined further down
           // Remember that selectPolls was defined in our poll.html template with a data-dojo-attach-point
           // this is why we have access to it in this context
            this.connect(this.selectPolls, "onChange", this._onInputWidgetChanged);

            //We call our two methods to update the dropdown and answers box
            this._refreshDropDown(this.get('value'));
            this._refreshAnswers(this.get('value'));
        },
        //Pretty self explanatory, returns wether our module is valid or not
       // what's interesting here is that epi will call isValid on every module to make sure the page is valid
      // This is the place to do any validation of the value entered by the user, in our case there won't really be any.
        isValid: function () {
            return this.selectPolls.isValid();
        },
        // Setter for value property
        _setValueAttr: function (value) {
           //When the value of this module is set we also want to set the value of our filteringSelect
            this.selectPolls.set("value", value);
            this._set("value", value);
        },
       // This function is not strictly necessary, but if the module is in readonly mode it could be useful
      // (perhaps the editor is editing the page in a language where this property should not be available)
        _setReadOnlyAttr: function (value) {
            this._set("readOnly", value);
            this.selectPolls.set("readOnly", value);
        },
      //update the value of the module
        _updateValue: function (value) {
            if (this._started && epi.areEqual(this.value, value)) {
                return;
            }
           //We set the value and make sure to refresh the answers box
            this._set("value", value);
            this._refreshAnswers(value);
            this.onChange(value);
        },
      //Create dialog? What's this about?
      //This is where we pop another dialog to let the user create a new poll, it will be called from our buttonClick function
        _createDialog: function () {
            //Here we create a PollDialog which is another module that we haven't built yet, we'll get to it soon enough
            this.pollDialog = new PollDialog({
            });
            //Here we create an epi dialog (which is why we needed a dependency to epi/shell/widget/dialog)
            this.dialog = new Dialog({
                title: "New poll",
                content: this.pollDialog,
                dialogClass: "epi-dialog-portrait"
            });

           //onExecute is triggered when the user clicks OK on the dialog
           //onHide is triggered, well, when the dialog is hidden
           //we got some logic to run when these two occur so we just attach our own event handlers.
            this.connect(this.dialog, 'onExecute', '_onExecute');
            this.connect(this.dialog, 'onHide', '_onDialogHide');

            this.dialog.startup();
        },
        _onDialogHide: function () {
            this.focus();
        },
        _onButtonClick: function () {
           //If the dialog hasn't been previously created we create it
            if (!this.dialog) {
                this._createDialog();
            }
           //Show it! This function is called from the data-dojo-attach-event of our button in poll.html
            this.dialog.show(true);
        },
        _onExecute: function () {           
           //This is triggered when the user clicks OK on the dialog
           // We get the value of our dialog (more on this later when we look at the dialog module)
            var pollData = this.pollDialog.get('value');
            var poll;
           //What? JQuery? Yes, jQuery! You can of course use the Dojo store functionality but I like to have better control over my ajax calls.
            jQuery.ajax({
                url: '/api/polls?pollHeading=' + pollData.pollHeading + '&answers='+pollData.answers,
                type: 'POST',
                contentType: 'application/json; charset=utf-8',
                success: function (result) {
                    poll = JSON.parse(result);
                },
                async: false //We run this synchronously 
//for simplicity here so we don’t have to worry about handling everything in a callback.
}); this._refreshDropDown(poll.PollId); this.selectPolls.refresh(); }, _refreshDropDown: function (selected) { var pollData = []; jQuery.ajax({ url: '/api/polls', contentType: 'application/json; charset=utf-8', success: function (result) { result = JSON.parse(result); //push empty result in case they do not want a poll pollData.push({ name: '', id: -1 }); for (var i = 0; i < result.length; i++) { pollData.push({ name: result[i].Header, id: result[i].PollId }); } }, async: false }); //This is where we need the dojo/store/Memory dependency we simply create a dojo
//store of in memory objects and let that be the store of our FilteredDropdown
var store = new Memory({ data: pollData }); this.selectPolls.store = store; this.selectPolls.set('value', selected); }, _refreshAnswers: function (selected) { this.titlePane.destroyDescendants(); //destroy any eventual previous answers if (selected <= 0) return; var selectedPoll; jQuery.ajax({ url: '/api/polls/'+selected, contentType: 'application/json; charset=utf-8', success: function (result) { selectedPoll = JSON.parse(result); }, async: false }); //DeleteableTextBox? It’s a simple module that we’ll create soon for (var i = 0; i < selectedPoll.Answers.length; i++) { var textBox = new DeleteableTextBox({ name: 'text', readOnly: true, value: selectedPoll.Answers[i].Answer, style: 'display:table; margin-bottom:10px;', answerId: selectedPoll.Answers[i].AnswerId }); this.titlePane.addChild(textBox, i); } }, _onDeletePollClick: function() {
//This method should be pretty self explanatory, we delete the poll and refresh
//our dropdown and answers
if (confirm('Are you sure you want to delete this magnificent poll?')) { jQuery.ajax({ url: '/api/polls/' + this.get('value'), type: 'DELETE', contentType: 'application/json; charset=utf-8', success: function (result) { }, async: false }); this._refreshAnswers(-1); this._refreshDropDown(-1); } }, _onInputWidgetChanged: function(value) {
//This function is important, if the value in the FilteredDropDown
//changes we publish the event to inform EPi that the property has changed.
this._updateValue(value); } });

That’s it! But since we haven’t created the DeleteableTextBox and the PollDialog yet we still aren’t done. Lets have a look at the DeleteableTextBox real quick.

define([
"epi",
"dojo",
"dijit/form/TextBox",
"dojo/text!./templates/deleteabletextbox.html"],
function (epi, dojo, TextBox, template) {
    return dojo.declare("whatever.editors.DeleteableTextBox", [TextBox], {
        templateString: template,
        answerId: -1,
        postCreate: function () {
            this.inherited(arguments);
            this.connect(this.clearButton, 'onclick', dojo.hitch(this, function () {
                jQuery.ajax({
                    url: '/api/answers/' + this.answerId,
                    type: 'DELETE',
                    contentType: 'application/json; charset=utf-8',
                    success: function (result) {
                    },
                    async: false
                });
                this.destroy();
            }));
        },
        _valueChanged: function () {
            if (this.onTextChange) {
                this.onTextChange();
            }
        }
    });
});

Now that’s a lot simpler. We just create a simple module that inherits from dijit.form.TextBox and add some functionality for deleting the associated answer. Here’s the html template.

<div class="dijit dijitReset dijitInline dijitLeft" waiRole="presentation">
    <div class="dijitReset dijitInputField dijitInputContainer">
        <div style="float: left;width:100%;">
            <div style="float: left; padding-right: 16px;">
                <input class="dijitReset dijitInputInner" dojoAttachPoint="textbox,focusNode" dojoAttachEvent="onkeyup: _valueChanged" autocomplete="off" />
            </div>
        </div>
        <span class="epiDeleteIcon" dojoAttachPoint="clearButton" style="width:16px; height: 16px; float: left; margin-left: -16px; position:relative; top:3px;" alt=""></span>
        <div style="clear: both; width: 0; height: 0; border: 0; padding: 0; margin: 0;"></div>
    </div>
</div>

All right, almost done. Lets have a look at the dialog.

define([
    "dojo/_base/connect",
    "dojo/_base/declare",

    "dijit/_CssStateMixin",
    "dijit/_Widget",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",

    "epi/dependency",
    "epi/epi",
    "epi/shell/widget/_ValueRequiredMixin",

    "dojo/text!./templates/polldialog.html"
],
function (
    connect,
    declare,

    _CssStateMixin,
    _Widget,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,

    dependency,
    epi,
    _ValueRequiredMixin,

    template
) {
    return declare("whatever.editors.PollDialog", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin], {

        templateString: template,
        intermediateChanges: false,
        value: null,
        store: null,
        postCreate: function () {
            // call base implementation
            this.inherited(arguments);
        },
        focus: function() {
            this.input.focus();
        },
        parseValues: function() {
            var data = { pollHeading: this.input.get('value'), answers: [] }

            data.answers = $(this.qlist).find('input').map(function() {
                if ($(this).val()) {
                    return $(this).val();
                }
            }).get();

            return data;
        },
        _getValueAttr: function () {
            return this.parseValues();
        }   
    });
});

Pretty straightforward as well. The only thing really worth mentioning is the parseValues function that makes sure that we return a JavaScript object that corresponds to a complete poll, this is why we in the _onExecuted function can get a poll to post from the dialog like this var pollData = this.pollDialog.get('value');.

Here’s the html template for the dialog.

<div>
    <div class="editor-label" style="float: left;" dojoattachpoint="labelContainer">
        <label dojoattachpoint="label">Choose a heading for your poll</label>
    </div>
    <div style="clear: both; height: 10px;"></div>

    <div class="editor-field" dojoattachpoint="inputContainer">
        <div style="width:520px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="input"></div>
    </div>
    <div style="clear: both;"></div>
    <div style="padding:20px 0 0 20px;">
    <ul dojoattachpoint="qlist">
        <li style="margin-bottom:10px;">Answer 1: <div style="width:437px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="q1"></div></li>
        <li style="margin-bottom:10px;">Answer 2: <div style="width:437px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="q2"></div></li>
        <li style="margin-bottom:10px;">Answer 3: <div style="width:437px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="q3"></div></li>
        <li style="margin-bottom:10px;">Answer 4: <div style="width:437px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="q4"></div></li>
        <li style="margin-bottom:10px;">Answer 5: <div style="width:437px;" data-dojo-type="dijit.form.TextBox" dojoattachpoint="q5"></div></li>
    </ul>
</div>
</div>

And now we’re finally done with the plugin. We just need to setup a property on one of our ContentTypes like this.

        [ClientEditor(ClientEditingClass = "whatever.editors.PollCreation")]
        public virtual int Poll { get; set; }

Ok, lets have a look at the result.

Our property should now look something like this.

poll1

And if we select a poll:

poll2

Deleting a poll is as simple as clicking on the waste basket icon, and if we click the + we’ll see our dialog in action:

image

And that’s it! It took some effort and we could definitely have polished this a bit more, but I hope building custom properties for EPiServer now feels a bit more doable.

You might have noted that I do not have a POST method in my web api controller which means that we’ll get an error when we try to create a new poll. That could be easily remedied by adding a post method, something like this:

public SimplePoll Post(string pollHeading, string answers)
        {
            var poll = new SimplePoll()
            {
                PollId  = 666,
                Header = pollHeading,
                Answers = new List<SimpleAnswer>()
            };
            int i = 500;

            if (string.IsNullOrEmpty(answers))
            {
                return poll;
            }

            var answersArray = answers.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            foreach (var item in answersArray)
            {
                poll.Answers.Add(new SimpleAnswer() { Answer = item, AnswerId = i++ });
            }

            return poll;
        }
However this still won’t work as we do not persist any data. But if you had an underlying database or some other type of persistence it would work as expected.