I recently had some time over to play around with the next release of EPiServer, what the guys over there call CMS6, and the new feature EPiServer Site Center. The build I used was 220.127.116.11.
Getting it all up and running was easy enough with the public templates project. Now as for the Site Center (from here on known as SC) itself, it still needs some polishing since a lot of stuff don’t work yet, but it’s a early release anyways.
The first thing I did was to go through the “Creating a gadget” example (hello world, wohoo). And as I am new to MVC also, this was pretty much uncharted territory. Yes, site center uses MVC, hooray! I won’t go into detail on how Mvc works here, but let’s just say that it is stateless as opposed to webforms, so no more silly postbacks and messing around with viewstate. Oh, and also MVC gives us back our <form> tag, yay!
Writing gadgets is fairly straightforward, all you need to do is add [Gadget] as a attribute to your MVC Controller, register your assembly as a possible container of gadgets in web.config, and you’re ready to go. The first thing I noticed is that SC always use the “Index” action to render the gadget. And that it also always passes “gadgetId” as an argument. “gadgetId” is a guid generated by SC and is supposed to be the unique identifier for the gadget. I had some issues with saving/loading gadgets as it seemed to always generate a new Guid every time I recompiled, but it might not be implemented yet. Alternatively, I did it all wrong… Anyways, you can still write your fancy actions and play around with it.
Anyways, let’s look at some code, the example EP provided with the release was so dull, since it didn’t actually do anything fun, so I decided to make a “quicknews” gadget, pay no attention to the silly things the “Save” action does, this was just to play around:
[Gadget( Name = "Quicknews" )]
public class QuickNewsController : ControllerBase
public ActionResult Index( Guid gadgetId )
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult Save( FormCollection form )
PageReference parent = DataFactory.Instance.GetPage( PageReference.StartPage )
.Property.Get( "NewsRoot" ).Value as PageReference;
PageData newsPage = DataFactory.Instance.GetDefaultPageData( parent,
"[Public] News item" );
newsPage.PageName = form.Get( "PageName" );
newsPage.Property[ "MainIntro" ].Value = form.Get( "Intro" );
newsPage.Property[ "MainBody" ].Value = form.Get( "MainBody" );
DataFactory.Instance.Save( newsPage, SaveAction.Publish );
return View( "Index" );
[GadgetAction( ActionType.Menu, Text = "Settings" )]
public ActionResult Settings( Guid gadgetId )
The first thing I found when playing around with Reflector (the most important tool any EPiServer developer can have) in the SC dll’s, is the [GadgetAction] attribute, if you set the actiontype to Menu, you will get a fancy menu option in the gadget, which, if clicked, will automatically call that action (ideally a ActionResult with a view). I have done that in the example above to make a configuration menu, very nice. I would assume there will be more options coming there in future SC releases.
Onward to the views. Let’s take a look at some code:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ModelBase>" %>
<%@ Import Namespace="System.Web.Mvc.Html"%>
<%@ Import Namespace="Dropit.Gadgets.Models"%>
<%@ Import Namespace="EPiServer.Shell.Extensions" %>
<% Html.BeginGadgetForm( "Save" ); %>
Page name:<br />
<br /><br />
<br /><br />
Main body:<br />
<br /><br />
<input type="submit" value="Save" />
<input type="button" value="Clear form" onclick="epi.gadget.loadView(this);" />
<% Html.EndForm(); %>
Another fun feature of SC is that you cannot make forms and post them as you like, since your form action would take you directly to your defined action ( <form action=”/controller/action/params”>), which would of course be an action in your controller. Now your controller have no idea what the SC is, and will render directly to your browser, bummer. After reading a blog post by Roger Wirz at the EPiServer labs site, I noticed him using a Html helper method called Html.BeginGadgetForm(…). Html helpers are MVC functions that do exactly what they are called, they help you write html, and it is really easy to make your own, check the asp.net MVC site to see how they are created.
Anyways, the BeginGadgetForm helper puts another action on the form element, which help us post it correctly so the result will be rendered in the SC instead of directly to the browser. It might be done in more instances also, since the built in example does not seem to have this action if you view the files, possibly by using jQuery to parse all the forms or submit buttons, or hooking onto the renderer (someone who knows how it all work, feel free to enlighten me). Ah well, what really needs to be done is add, either to your form element, or as an action on your element that is used to submit the form. If you reflect the BeginGadgetForm, you see that it adds:
onsubmit ="epi.gadget.postAjaxForm(this); return false;"
Which will make it post the form via ajax instead of submitting the form normally (and taking you out of the SC as mentioned above). The input fields at the end of the form does exactly the same as if you would use the extension method GadgetOkCancelButtons. The onclick on the cancel button is used to clear the fields by simply reloading the current view and fetch any saved settings again.
SC looks very promising, and the fact that it’s built with MVC makes it much cooler. I am looking forward to the next release, which will hopefully have some more features and documentation (especially regarding how we are supposed to save/load our gadgets, like a general guideline), and most of the quirks ironed out.
Creating gadgets is very simple, just make a Controller and a Partial View (usercontrol), and add the attribute [Gadget] to your controller., once you learn how to submit forms to SC, it gets even simpler.
If you are interested in learning more about gadgets, I recommend to look at Roger’s post I mentioned earlier as he build a little more advanced gadget there than in the example supplied with the CTP.