Extending the User Interface of EPiServer 7
You have probably not missed that EPiServer 7 comes with a brand new user interface for content editing. This is built on top of a new UI-framework that is also used on the new dashboard. In this blog series I’ll explain how to implement some of the most common extension scenarios.
Note: The code in this blog series is based on the latest build and might not work in EPiServer 7 preview or other later builds.
How the UI is built up
In previous versions of EPiServer CMS the user interface consisted of a simple page containing a table structure with a bunch of iframes where each function was plugged in or in some cases hard coded. The page was running in quirks document mode to enable resizing of the each iframe.
In EPiServer 7, the UI-framework that is used to build the dashboard and CMS edit view works quite different:
- A simple page is loaded. It contains some script and styling references and a JavaScript bootstrapper that is responsible for setting up the UI.
- The bootstrapper fetches the components for the specific view using a REST-based store, start them up and adds them to their correct hierarchical placement.
All this is done on the client using the JavaScript framework Dojo. It’s still possible to use your jQuery-style gadgets to plug in to the new UI-framework but writing components in Dojo will give you more built in functions and styling for free. It’s up to you to decide what suits your needs and skills best.
If you are new to Dojo (which I guess most of you are) there is an introduction section in the framework SDK that points to some good resources to get started. For instance, there are some good tutorials here:
- Arrays Made Easy
- Creating a custom widget
- Events with Dojo
- Getting Started with Deferreds
- Classy JavaScript with dojo.declare
Setting up a module
If you don’t already have a shell module set up we need to add this to be able to add plug-ins to the user interface. The simplest way to get started is to add the following configuration into a file named module.config to the root folder of your site:
<?xml version="1.0" encoding="utf-8"?>
<module>
<assemblies>
<!-- This adds the Alloy template assembly to the "default module" -->
<add assembly="EPiServer.Templates.Alloy" />
</assemblies>
<dojoModules>
<!-- Add a mapping from alloy to ~/ClientResources/Scripts to the dojo loader configuration -->
<add name="alloy" path="Scripts" />
</dojoModules>
</module>
Note: The assembly should match the name of your template assembly in this case.
Using web forms
We recommend writing your plug-ins using Dojo or potentially jQuery to get a nice integrated look and feel. Sometimes you just want to add something simple or quickly convert a plug-in for CMS 6 –> 7. To enable this we have added an attribute to be able to load a web forms page or user control inside an iframe. A simple example of this could look like this:
using EPiServer.Shell.ViewComposition;
using EPiServer.UI;
using EPiServer.Web;
namespace EPiServer.Templates.Alloy
{
[IFrameComponent(Url = "~/IFramePageExample.aspx",
ReloadOnContextChange = true,
PlugInAreas = "/episerver/cms/assets",
Title = "Iframe test",
Categories="cms",
MinHeight = 100,
MaxHeight = 500)]
public partial class IframePageExample : ContentBaseWebForm
{}
}
Note: We are inheriting from the class ContentBaseWebForm that is located in the EPiServer.UI assembly since we want to be able to handle all content types and not just pages. The attribute itself is defined in the EPiServer.Shell assembly.
Update: In EPiServer 7.5, the base class to use is: EPiServer.Shell.WebForms.ContentWebFormsBase
ReloadOnContextChange: Can be defined to reload the iframe content with a new content id each time the user changes context.
PlugInPaths: Can be used to auto-plug in a component into a given location in the user interface.
Categories: Defines where this components can/should be added. The current implementation is to only show components to the user that matches the category of the view. Currently used categories are “cms” and “dashboard” but we will probably add more categories as we convert more views to the new framework.
To see that we are loading the current item we add a simple output of the name in the code-front file:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="IframePageExample.aspx.cs" Inherits="EPiServer.Templates.Alloy.IframePageExample" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
</head>
<body>
<form id="form1" runat="server">
<div>
<%
1: =CurrentContent.Name
%>
</div>
</form>
</body>
</html>
Which will result in the following in the UI:
We will continue writing more samples and update the links below:
Extending the User Interface of EPiServer 7
Creating a Dojo based component
Creating a content search component
Adding a more advanced property editor
Great. Looking forward reading more in this series.
The example does not compile with version EPiServerCMS7.0.586.1.
ComponentAttribute.PlugInAreas Property is a string value.
Can you explain a bit more about the 'PlugInPaths = PlugInAreas.AssetsPanel' ?
@Rene: I have updated the sample code to work with the EPiServer 7 release. Regarding PlugInAreas as a string, for the new user interface we use strings as identifiers in many cases where we might have used enums before. The reason for this is simply to be more extendable. For instance, the attributes defined on the Framework level should have no knowledge of our products of potential third party code that want's to create plug in areas.
Since we are using strings as identifiers, we have added classes with constant strings to help you developers. In this case, the plug-in-area is currently defined in the EPiServer.CMS.Shell.UI-assembly. Since this is located in an add-on you have the option to take a dependency to this or just to use a hard coded string as in the changed example above. I will write a separate blog posts about the pros and cons about taking a dependency to an add-on.
I added a button the the Html and an eventhandler in codebehind, but by pressing the button, after postback the current page is reloaded in the window:
I done something wrong?
@Botond: Clicking on a button in web forms WILL do a post back since that's how web forms work. Or are you meaning something else?
No, I mean, I implemented everything that you describe in the top.
I have a button in the aspx file:
I have the event in the codebehind:
public void ReindexButton_Click(object sender, EventArgs e)
{...}
When I click on the button, the event is not fired, and instead of the page where the button was clicked, is loading the page that is opened in the Episerver editor.
Very strange...
@Botond: I have reported a bug for the issue. The work around is to turn of the FURL handling for the current request. Here's a code sample on how to do it:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (UrlRewriteProvider.Module != null)
{
UrlRewriteProvider.Module.FURLRewriteResponse = false;
}
}
Cool, it's working now. Thanks!
Hi,
but still no joy.
Can you use the IFrameComponent with MVC ?
I have been trying the following :
[EPiServer.Cms.Shell.ViewComposition.IFrameComponent( Url="~/MyPlugin", ReloadOnContextChange=true, PlugInAreas=EPiServer.Cms.Shell.PlugInArea.AssetsPanel, IsAvailableForUserSelection=true )]
public class MyPluginController : ActionControllerBase //PartialContentController
{
// PartialContentController override
//public override ActionResult Index(IContentData currentContent)
//{
// return View();
//}
public ActionResult Index()
{
return View();
}
}
tried both derriving from ActionControllerBase and PartialContentControler
I have defined my assembly in the module.config file.
I can see the request made for my controller from the browser.
http://localhost:37002/MyPlugin/Index?uri=epi.cms.contentdata%3A%2F%2F%2F496267_497264&id=496267_497264
But it never hits my controller action and returns a 404.
I've tried adding the route in the EPiServer.Global.RegisterRoutes override but then the plugin just loads the main controller template for the Home Page?
Is there any example of creating a simple IFrameComponent in MVC ? What I need to derrive from, any config needed ?
regards,
Danny
Hi Danny!
There is no built in support at the moment for this at the moment but you can add it yourself without to much hassle. First, you need to add the standard MVC controller route in an initialization module:
RouteTable.Routes.MapRoute("StandardMvcRoute", "{controller}/{action}", new { action = "index"});
Then you also need to add a model binder that resolves the URL part of the query string to a ContentData object (comes in the format uri=epi.cms.contentdata://8_176).
Hi,
Is it possible to create an edit panel plug-in using this technique? If so what would the PlugInAreas string be. I have tried "episerver/cms/editpanel" but I can't see my plug-in.
/Paul.
It's currently not possible to add different views of a content the same way as you could in the edit panel of EPiServer 4-6. We are currently working on implementing support to configure different views for a content for the next release. This the goal is mainly to support internal needs this will probably simplify this. We're still early in the implementation process so I won't promise anything but we'll of course document this if we feel that it's in a state that's usable by partners.
Is ContentBaseWebForm removed from EPi 7.5? It seems to be removed from the assembly.
Try using EPiServer.Shell.WebForms.ContentWebFormsBase (still in the EPiServer.UI assembly). I'll have a blog post around this and some other changes soon.
Can I use this Iframe method to list pages (links to epi pages) and make them draggable into a Content Area?
I tried just with normal links but that didn't work.
What is needed to make the links drag & dropable?
@Freguz: Making drag and drop from another iframe is quite tricky. I would recommend this approach instead: http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2012/11/Creating-a-component-that-searches-for-content/
Here's an example based on EPiServer 7.5 (or .6, rather) with MVC for an edit-mode widget: http://tedgustaf.com/blog/2014/6/create-an-episerver-widget-for-edit-mode/