Go Back

Creating Custom Sitefinity Modules

These are my notes for the Creating Custom Sitefinity Modules webinar.

General Information

Intra-site Module versus Pluggable Modules

Both types are capable of achieving the same end functionality and the only difference is in the actual implementation.

Intra-Site modules - simpler/quicker to develop. Hard to transfer.

Pluggable modules - a bit more complex to implement. Easily transferred/installed.

Removing Existing Sitefinity Modules

To remove existing Sitefinity modules, remove the mapping from the <telerik><framework><modules> section in the ~/web.config file.

<telerik>
  <framework>
      <modules>
        <add type="Telerik.Cms.Engine.GenericContentModule, Telerik.Cms.Engine"/>
        <add type="Telerik.News.NewsModule, Telerik.News"/>
        <add type="Telerik.Blogs.BlogsModule, Telerik.Blogs"/>
        <add type="Telerik.Lists.ListModule, Telerik.Lists"/>
        <add type="Telerik.Polls.PollModule, Telerik.Polls"/>
        <add type="Telerik.Forums.ForumsModule, Telerik.Forums"/>
        <add type="Telerik.Libraries.LibrariesModule, Telerik.Libraries"/>
        <add type="Telerik.Events.EventsModule, Telerik.Events"/>
        <add type="Telerik.Notifications.Newsletters.NewsletterModule, Telerik.Notifications"/>
      </modules>
  </framework>
</telerik>

Creating a new Module

All Sitefinity Modules must inherit from the Telerik.WebModule class.  Classes that inherit from Telerik.WebModule are required to implement the following members:

  • Name
  • Title
  • Description
  • Controls

Below is the bare minimum amount of code needed to add a new module to Sitefinity. 

Create a ~/App_Code/ContactsModule.cs containing the following code:

namespace Contacts
{
    public class ContactsModule : Telerik.WebModule
    {
        public ContactsModule()
        {
        }

        private System.Collections.Generic.IList<Telerik.Web.IToolboxItem> toolboxItems;

        public override string Name
        {
            get { return "Contacts"; }
        }

        public override string Title
        {
            get { return "Contacts"; }
        }

        public override string Description
        {
            get { return "Module for managing web site contacts."; }
        }

        public override System.Collections.Generic.IList<Telerik.Web.IToolboxItem> Controls
        {
            get
            {
                return toolboxItems;
            }
        }
    }
}

Adding a Control Panel & Command Panel

What is a ControlPanel and CommandPanel?

To add a Control Panel to the module, add the following code to ~/App_Code/ContactsModule.cs:

public override System.Web.UI.Control CreateControlPanel(System.Web.UI.TemplateControl parent)
{
    return parent.LoadControl("~/Custom/Admin/Contacts/ControlPanel.ascx");
}

Create 2 new UserControls:

  • ~/Custom/Admin/Contacts/ControlPanel.ascx
    Implements the Telerik.Web.IControlPanel interface and references the CommandPanel.
  • ~/Custom/Admin/Contacts/CommandPanel.ascx
    Implements the Telerik.Web.ICommandPanel interface.

~/Custom/Admin/Contacts/ControlPanel.ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ControlPanel.ascx.cs" Inherits="Custom_Admin_Contacts_ControlPanel" %>
ControlPanel

~/Custom/Admin/Contacts/ControlPanel.ascx.cs:

using System;
using System.Web.UI;
using Telerik.Web;

public partial class Custom_Admin_Contacts_ControlPanel : UserControl, IControlPanel
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    #region IControlPanel Members

    public ICommandPanel[] CommandPanels
    {
        get { return new ICommandPanel[] {(ICommandPanel) Page.LoadControl("~/Custom/Admin/Contacts/CommandPanel.ascx")}; }
    }

    public void Refresh()
    {
        // Since it is a user control, causing a postback is enough to do a refresh
    }

    public string Status
    {
        get { return ""; }
    }

    public string Title
    {
        get { return "Contacts"; }
    }

    #endregion
}

~/Custom/Admin/Contacts/CommandPanel.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CommandPanel.ascx.cs" Inherits="Custom_Admin_Contacts_CommandPanel" %>

CommandPanel

~/Custom/Admin/Contacts/CommandPanel.ascx.cs

using System;
using System.Web.UI;
using Telerik.Web;

public partial class Custom_Admin_Contacts_CommandPanel : UserControl, ICommandPanel
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    public IControlPanel ControlPanel
    {
        get { return ctrlPnl; }
    }

    public string Name
    {
        get { return "CommandPanel Name"; }
    }

    public void Refresh()
    {
        // Since it is a user control, causing a postback is enough to do a refresh
    }

    public string Title
    {
        get { return "CommandPanel Title"; }
    }

    IControlPanel ctrlPnl;
}

Control Panel HTML Templates

The following HTML templates can be used to implement the look & feel used by other Sitefinity modules.  These templates consist only of HTML.  To make this code functional, replace the static HTML with server tags.

Blank - No items added.

<div class="ContorlPanelTitle">
    <h1>
        <a href="#">Contacts Module</a>
    </h1>
</div>

<div class="ToolsAll">
    <a href="#" class="CmsButLeft new"><strong class="CmsButRight light">Create an item</strong></a>
    <div class="clear"></div>
</div>
<div class="workArea">
    <h2 class="gridTitle">All Items</h2>

    <div id="empty">
        <h2 class="gridTitle">Nothing has been created yet.</h2>
        <p><a class="mainLink" href="#"><strong>Create your first item</strong></a></p>       
    </div>
</div>

Add a New Item

<div class="ToolsAll">
    <div class="backWrapp">
        <a href="#" class="actions back">Cancel and go back</a>
    </div>
</div>

<div class="workArea insert">
    <div class="mainForm">
        <p class="mand">* Mandatory fields</p>
        <h3>Product Name</h3>

        <fieldset class="set">
            <div class="setIn title">
                <input type="text" />
            </div>
        </fieldset>
        <div class="bottom">
            <div>
                <!-- -->
            </div>
        </div>
        
        <h3>Product Details</h3>

        <fieldset class="set">
            <div class="setIn">
                <p class="example">Some additional instructions.</p>
                <ol>
                    <li>
                        <label for="input2">Price</label>
                        <input id="input2" type="text" />
                    </li>
                    <li>
                        <label for="input3">Category</label>
                        <input id="input3" type="text" />
                    </li>
                    <li>
                        <label for="input4">Sale</label>
                        <input id="input4" type="text" />
                    </li>
                </ol>
            </div>
        </fieldset>
        
        <div class="bottom">
            <div>
                <!-- -->
            </div>
        </div>
        
        <p class="button_area bot">
            <a class="CmsButLeft okdark" href="#"><strong class="CmsButRight dark">Create this item</strong></a>
            <span>or</span>
            <a class="cmscclcmd" href="#">Cancel</a>
        </p>
    </div>
</div>

Command Panel

<h2>Example CommandPanel</h2>
<dl id="expMenu">
    <dt id="all"><a class="sel" href="#">All forums</a></dt>
    <dd>Here you can read, sort, edit and manage all forums.</dd>
    <dt id="all"><a href="#">Categories</a></dt>
    <dd>Here you can create and manage categories.</dd>
    <dt id="globalPerm"><a href="#">Permissions</a></dt>
    <dd>From here you can control who is allowed to read, edit or delete forums.</dd>
</dl>

More soon...

Final Result - Contacts Intra-site Module

~/App_Code/ContactsModule.cs

namespace Contacts
{
    public class ContactsModule : Telerik.WebModule
    {
        public ContactsModule()
        {
        }

        private System.Collections.Generic.IList<Telerik.Web.IToolboxItem> toolboxItems;

        public override string Name
        {
            get { return "Contacts"; }
        }

        public override string Title
        {
            get { return "Contacts"; }
        }

        public override string Description
        {
            get { return "Module for managing web site contacts."; }
        }

        public override System.Collections.Generic.IList<Telerik.Web.IToolboxItem> Controls
        {
            get
            {
                return toolboxItems;
            }
        }

        public override System.Web.UI.Control CreateControlPanel(System.Web.UI.TemplateControl parent)
        {
            return parent.LoadControl("~/Custom/Admin/Contacts/ControlPanel.ascx");
        }
    }
}

~/Custom/Admin/Contacts/ControlPanel.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ControlPanel.ascx.cs" Inherits="Custom_Admin_Contacts_ControlPanel" %>

<div class="ContorlPanelTitle">
    <h1><a href="#">Contacts Module</a></h1>
</div>

<div class="ToolsAll">
    <asp:HyperLink ID="CreateNewButton1" class="CmsButLeft new" runat="server"><strong class="CmsButRight light">Create an item</strong></asp:HyperLink>
    <div class="clear"></div>
</div>

<asp:LinqDataSource ID="LinqDataSource1" runat="server" 
    ContextTypeName="Contacts.Data.DatabaseDataContext" TableName="Contacts" 
    EnableDelete="True" EnableInsert="True" EnableUpdate="True">
</asp:LinqDataSource>

<asp:LinqDataSource ID="LinqDataSource2" runat="server" 
    ContextTypeName="Contacts.Data.DatabaseDataContext" EnableDelete="True" 
    EnableInsert="True" EnableUpdate="True" TableName="Contacts" Where="ID == @ID">
    <WhereParameters>
        <asp:ControlParameter ControlID="GridView1" DbType="Guid" 
            DefaultValue="SelectedValue" Name="ID" PropertyName="SelectedValue" />
    </WhereParameters>
</asp:LinqDataSource>

<div class="workArea">
    <asp:MultiView ID="MultiView1" runat="server">
        <asp:View ID="EmptyView" runat="server">
                <h2 class="gridTitle">All Items</h2>

                <div id="empty">
                    <h2 class="gridTitle">Nothing has been created yet.</h2>
                    <p><asp:HyperLink ID="CreateNewButton2" class="mainLink" runat="server"><strong>Create your first item</strong></asp:HyperLink></p>
                </div>
        </asp:View>
        <asp:View ID="ListView" runat="server">
            <h2 class="gridTitle">All Contacts</h2>

            <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" DataSourceID="LinqDataSource1" CssClass="listItems" GridLines="None" OnRowCommand="GridView1_RowCommand">
                <Columns>
                    <asp:CommandField ShowSelectButton="true" ShowDeleteButton="true" />
                    <asp:BoundField DataField="FirstName" HeaderText="First Name" 
                        SortExpression="FirstName" />
                    <asp:BoundField DataField="LastName" HeaderText="Last Name" 
                        SortExpression="LastName" />
                    <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                    <asp:BoundField DataField="EmailAddress" HeaderText="Email Address" 
                        SortExpression="EmailAddress" />
                    <asp:BoundField DataField="Phone" HeaderText="Phone" SortExpression="Phone" />
                </Columns>
            </asp:GridView>
        </asp:View>
        <asp:View ID="EditContact" runat="server">
            <div class="insert">
                <div class="mainForm">
                    <h3>Contact Details</h3>

                    <asp:FormView ID="FormView1" runat="server" DataKeyNames="ID" DataSourceID="LinqDataSource2" OnInit="FormView1_Init" OnDataBound="FormView1_DataBound" OnItemCommand="FormView1_ItemCommand">
                        <EditItemTemplate>
                            <fieldset class="set">
                                <div class="setIn">
                                    <p class="example">Enter contact information in the form below:</p>
                                    <ol>
                                        <li>
                                            <asp:Label AssociatedControlID="FirstNameTextBox" Text="First Name" runat="server" />
                                            <asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstName") %>' />
                                        </li>
                                        <li>
                                            <asp:Label ID="Label1" AssociatedControlID="LastNameTextBox" Text="Last Name" runat="server" />
                                            <asp:TextBox ID="LastNameTextBox" runat="server" Text='<%# Bind("LastName") %>' />
                                        </li>
                                        <li>
                                            <asp:Label ID="Label2" AssociatedControlID="TitleTextBox" Text="Title" runat="server" />
                                            <asp:TextBox ID="TitleTextBox" runat="server" Text='<%# Bind("Title") %>' />
                                        </li>
                                        <li>
                                            <asp:Label ID="Label3" AssociatedControlID="EmailAddressTextBox" Text="Email Address" runat="server" />
                                            <asp:TextBox ID="EmailAddressTextBox" runat="server" Text='<%# Bind("EmailAddress") %>' />
                                        </li>
                                        <li>
                                            <asp:Label ID="Label4" AssociatedControlID="PhoneTextBox" Text="Phone" runat="server" />
                                            <asp:TextBox ID="PhoneTextBox" runat="server" Text='<%# Bind("Phone") %>' />
                                        </li>
                                    </ol>
                                </div>
                            </fieldset>
                                
                            <div class="bottom">
                                <div><!-- --></div>
                            </div>
                            
                            <p class="button_area bot">
                                <asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert" CssClass="CmsButLeft okdark"><strong class="CmsButRight dark">Create this Contact</strong></asp:LinkButton>
                                <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update" CssClass="CmsButLeft okdark"><strong class="CmsButRight dark">Update this Contact</strong></asp:LinkButton>
                                <span>or</span>
                                <asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False" CommandName="Cancel" CssClass="cmscclcmd" Text="Cancel"/>
                            </p>
                            
                        </EditItemTemplate>
                    </asp:FormView>
                </div>
            </div>
        </asp:View>
    </asp:MultiView>
</div>

~/Custom/Admin/Contacts/ControlPanel.ascx.cs

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

public partial class Custom_Admin_Contacts_ControlPanel : UserControl, IControlPanel
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack == false)
        {
            // Default to List View
            MultiView1.SetActiveView(ListView);

            // The "action" parameter in our querystring allows us to toggle between modes.
            // Kind of lame...but so was everything else.
            SetViewToAction();

            // Preset NavigateUrl of HyperLinks -- append "action" to current URL
            SetNavigateUrls();
        }
    }

    protected void Page_PreRender(object sender, EventArgs e)
    {
        // After updating data this flag tells us whether we need to Refresh our data. 
        // This refresh needs to take place here in Page_PreRender, which is why I'm
        // using a temporary flag to make this happen.
        if (RefreshData == true)
        {
            GridView1.DataBind();
        }

        // If our current view is ListView but we have no records, display the Empty view.
        if (MultiView1.GetActiveView() == ListView && GridView1.Rows.Count == 0)
        {
            MultiView1.SetActiveView(EmptyView);
        }

    }

    /// <summary>
    /// I'm using this so I don't have to duplicate my entire EditItemTemplate in my FormView tag.
    /// </summary>
    protected void FormView1_Init(object sender, EventArgs e)
    {
        FormView1.InsertItemTemplate = FormView1.EditItemTemplate;
    }

    /// <summary>
    /// I'm using this so I don't have to duplicate my entire EditItemTemplate in my FormView tag.
    /// </summary>
    protected void FormView1_DataBound(object sender, EventArgs e)
    {
        if (FormView1.CurrentMode == FormViewMode.Edit)
        {
            LinkButton InsertButton = FormView1.FindControl("InsertButton") as LinkButton;
            LinkButton UpdateButton = FormView1.FindControl("UpdateButton") as LinkButton;

            InsertButton.Visible = false;
            UpdateButton.Visible = true;
        }
        else if (FormView1.CurrentMode == FormViewMode.Insert)
        {
            LinkButton InsertButton = FormView1.FindControl("InsertButton") as LinkButton;
            LinkButton UpdateButton = FormView1.FindControl("UpdateButton") as LinkButton;

            InsertButton.Visible = true;
            UpdateButton.Visible = false;
        }
    }

    /// <summary>
    /// When an item is clicked (Select, Delete) in GridView, this is executed.
    /// </summary>
    protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
     {
        if (e.CommandName.Equals("Select"))
        {
            // Switch to the FormView.  We're now editing an item.
            MultiView1.SetActiveView(EditContact);
            FormView1.ChangeMode(FormViewMode.Edit);
        }
        else if (e.CommandName.Equals("Delete"))
        {
            RefreshData = true;
        }
     }

    /// <summary>
    /// Executed when a FormView command (Insert, Update, Cancel) is executed.
    /// </summary>
    protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
    {
        MultiView1.SetActiveView(ListView);
        RefreshData = true;
    }

    /// <summary>
    /// Load the Command Panels associated with this Control Panel
    /// </summary>
    public ICommandPanel[] CommandPanels
    {
        get
        {
            return new ICommandPanel[] { (ICommandPanel)Page.LoadControl("~/Custom/Admin/Contacts/CommandPanel.ascx") };
        }
    }

    public void Refresh()
    {
        // Since it is a user control, causing a postback is enough to do a refresh
    }

    public string Status
    {
        get { return ""; }
    }

    public string Title
    {
        get { return "Control Panel Status"; }
    }

    private void SetViewToAction()
    {
        string action = Request.QueryString["action"];

        switch (action)
        {
            case "new":
                MultiView1.SetActiveView(EditContact);
                FormView1.ChangeMode(FormViewMode.Insert);
                break;
            default:
                MultiView1.SetActiveView(ListView);
                break;
        }
    }

    private void SetNavigateUrls()
    {
        string Url = Request.ServerVariables["URL"] + "?module=" + Request.QueryString["module"];
        CreateNewButton1.NavigateUrl = Url + "&action=new";
        CreateNewButton2.NavigateUrl = Url + "&action=new";
    }

    private bool RefreshData = false;
}

~/Custom/Admin/Contacts/CommandPanel.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CommandPanel.ascx.cs" Inherits="Custom_Admin_Contacts_CommandPanel" %>

<h2>CommandPanel</h2>
<dl id="expMenu">
    <dt id="all"><asp:HyperLink ID="ViewAllButton" runat="server">View All Contacts</asp:HyperLink></dt>
    <dd>Here you can read, sort, edit and manage all contacts.</dd>
    <dt id="all"><asp:HyperLink ID="AddButton" runat="server">Add Contact</asp:HyperLink></dt>
    <dd>Here you can create new contacts.</dd>
</dl>

~/Custom/Admin/Contacts/CommandPanel.ascx.cs

using System;
using System.Web.UI;
using Telerik.Web;

public partial class Custom_Admin_Contacts_CommandPanel : UserControl, ICommandPanel
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack == false)
        {
            // Preset NavigateUrl of HyperLinks -- append "action" to current URL
            SetNavigateUrls();
        }
    }

    public IControlPanel ControlPanel
    {
        get { return ctrlPnl; }
    }

    public string Name
    {
        get { return "CommandPanel Name"; }
    }

    public void Refresh()
    {
        // Since it is a user control, causing a postback is enough to do a refresh
    }

    public string Title
    {
        get { return "CommandPanel Title"; }
    }

    private void SetNavigateUrls()
    {
        string Url = Request.ServerVariables["URL"] + "?module=" + Request.QueryString["module"];
        ViewAllButton.NavigateUrl = Url + "&action=list";
        AddButton.NavigateUrl = Url + "&action=new";
    }

    IControlPanel ctrlPnl;
}

Comments  1

  • John 30 Mar

    Are web.config settings needed? I ask because I did nothing in web.config before switching to the model you provided here. Everything used to work but now tools do not appear and I cannot get the Control Panel "views" to show. Any ideas? Thanks
Post a comment!
  1. Formatting options
       
     
     
     
     
       
  2. I'm sorry for the CAPTCHA. You have spammers to thank for this: