This project is read-only.

Extending the CMS

NOTE: All the various extensions ( Widgets, Models, Macros, Information ) are currently loaded via attributes applied to classes.
Assemblies that contain extensions must be listed in the following configuration setting in web.config. The assembly names must be listed in a comma delimited format.

<appSettings>
      ...
      ...
      <add key="assembliesForExtensions" value="CommonLibrary.Web.Modules"/>
</appSettings>

Types of extensions

  1. Widgets
  2. Models
  3. Macros
  4. Tasks
  5. Information
  6. Extensions

Widgets

  • A widget is a small reusable visual component that generates html and that can be placed and moved around in zones of a page
  • Examples include the "Pages", "Users", "Archives", "Categories", "Tags" that appear on pages in the CMS
  • The widget can either build up html on it's own or can be configured to have a user control ( .ascx ) build up the html.

Creating a new widget

  1. Create new class that extends from WidgetInstance.
  2. Add the Widget attribute on top of the class as in the example ( specify "Name", and "IsEditable" properties )
  3. Any properties that need to be stored need to have the PropertyDisplay attribute applied to them
  4. Override the IsSelfRenderable method and return true if your widget will render html on it's own ( by return html from the Render() method )
  5. Implement the Render() method to return html if your widget is self-renderable, otherwise, specify the path to the .ascx file in the Widget Attribute via "Path" parameter
  6. For more examples see the Widgets in <ROOT>\src\lib\CommonLibrary.Web.Modules\src\Extensions\Widgets\

Example

using System;
using System.Web;
using System.Text;

using ComLib;
using ComLib.Web.Lib.Attributes;
using ComLib.Web.Modules.MenuEntrys;
using ComLib.Web.Modules.Widgets;


namespace ComLib.CMS.Models.Widgets
{
    /// <summary>
    /// Pages Widget
    /// </summary>
    [Widget(Name = "Pages", IsCachable = true, IsEditable = true, SortIndex = 5)]    
    public class Pages : WidgetInstance
    {
        /// <summary>
        /// Gets or sets the number of records to display.
        /// </summary>
        /// <value>The number of entries.</value>
        [PropertyDisplay(Label = "Front Pages Only", Order = 1, DefaultValue = "", Description = "Whether or not to only show the front pages")]
        public bool FrontPagesOnly { get; set; }


        /// <summary>
        /// Whether or not this can render html itself or uses a control to do it.
        /// </summary>
        /// <value></value>
        public override bool IsSelfRenderable
        {
            get { return true; }
        }


        /// <summary>
        /// Renders this instance as Html.
        /// </summary>
        /// <returns></returns>
        public override string Render()
        {
            var menuitems = MenuEntry.FrontPages();
            var buffer = new StringBuilder();


            buffer.Append("<ul>" + Environment.NewLine);
            foreach (var item in menuitems)
            {
                string link = string.Format("<li><a href=\"{0}\">{1}</a></li>", HttpUtility.HtmlEncode(item.UrlAbsolute), HttpUtility.HtmlEncode(item.Name));
                buffer.Append(link);
            }
            buffer.Append("</ul>" + Environment.NewLine);
            string html = buffer.ToString();
            return html;
        }
    }
}

Optional Steps

  1. Add support for providing description and examples for the properties of your widget: Add entries in Resources.csv.config for your widget. Follow the example for W_Gravatar

Models

Models are basically entities that are persisted in some underlying datastore.
  1. Examples of modules includes Event, BlogPost, Pages, Links
  2. Typically support CRUD features ( Create / Retrieve / Update / Delete, Get, Find )
  3. Typically have a ModelForm.ascx for creating/editing the model.
  4. Typically have a ModelList.ascx for showing a list / indexing all the instances of the model.
  5. Typically have a ModelDetails.ascx for showing the details for a single instance of the model.
  6. Typically may leverage ActiveRecord functionality by extending the model from ActiveRecordBaseEntity base class
  7. Typically has at least one controller following the MVC paradigm, named ModelNameController.cs
  8. Supports multiple life-cylce methods that the cms system can call. These include OnBeforeSave, OnAfterSave, OnAfterDelete among others
  9. Supports validation by having a validation method that performs the validation.
  10. Supports plugging in a Repository that is responsible for storing/retriveing the model from some datastore.
  11. Supports both an in-memory based repository as well as a real sql repository.
  12. Take a look at source code for the Event model at <ROOT>\src\lib\CommonLibrary.Web.Modules\src\_models\Link\Link.cs and LinkRepository.cs

NOTE: The Model and it's corresponding Sql Repository code can be autogenerated. ( This involves more elaborate documentation that is not yet available ).

Create a new Model

Below are the various classes that are typically used in a model.

Model classes
Class Example Description
<ModelName>.cs Post.cs Model / Entity that contain all the various properties that are persisted to the datastore
<ModelName>Controller.cs PostController.cs ASP.NET MVC Controller class for the model
<ModelName>Extensions.cs PostExtensions.cs Extension methods. Also if the Post.cs is a partial class that was auto-generated via a code-generator, then this class implements the various life-cycle callback methods, and validation code.
<ModelName>Helper.cs PostHelper.cs Class for various helper methods for the model.
<ModelName>Repository.cs PostRepository.cs The repository class that creates/retrieves/finds models


Model Views
File Location Purpose
ModelForm.ascx <ROOT>\src\apps\CommonLibrary.CMS\views\<ModelName>\ | The editor for the model
ModelList.ascx <ROOT>\src\apps\CommonLibrary.CMS\views\<ModelName>\ | Used to index / show all the models
ModelDetails.ascx <ROOT>\src\apps\CommonLibrary.CMS\views\<ModelName>\ | To display a single model


Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ComLib;
using ComLib.Entities;
using ComLib.Web.Lib.Core;
using ComLib.ValidationSupport;
using ComLib.Web.Lib.Attributes;
using ComLib.Web.Lib.Models;


namespace ComLib.Web.Modules.Links
{
    
    /// <summary>
    /// Link class extensions.
    /// </summary>    
    [Model(Id = 3, DisplayName = "Link", Description= "Link to external site or resource", IsPagable = true,
        IsExportable = true, IsImportable = true, FormatsForExport = "xml,csv,ini", FormatsForImport = "xml,csv,ini",
        RolesForCreate = "${Admin}", RolesForView = "?", RolesForIndex = "?", RolesForManage = "${Admin}", 
        RolesForDelete = "${Admin}",  RolesForImport = "${Admin}", RolesForExport = "${Admin}")]
    public partial class Link : ActiveRecordBaseEntity<Link>, IEntity, IEntitySortable, IEntityClonable
    {
          // ... Properties
          
    }
}

Model Implementation

  1. Extend from ActiveRecordBaseEntity<T> to have activerecord behaviour
  2. Override the GetValidatorInternal() method to validate itself.
  3. Have a repository class that extends from RepositoryBase<T>, where the class overrides and provide implementations for the Create and Update and methods.
  4. Implement a class ModelRowMapper that can map records from the datastore to instances of the model.
  5. To support paging and indexing, the model uses a stored procedures for this. See example in \install\installmodelevent.sql.

Model Configuration

  1. A model can either have a "Model" attribute applied to the class which the CMS system will search for to determine if a class represents a model
  2. A model can optionally be configured in CommonLibrary.CMS\config\Data\Models.ini.config


Tasks

Tasks are processes that are scheduled to run at a specific time or interval or the tasks can be on-demand which will run only when a user has selected to run the task.



Macros

Macros are small pieces of text that can be placed in an html page but are interpreted by the CMS and get converted to html.
  • A macro is very similar to an html tag.
  • A macro begins with $
  • A macro can have attributes
  • A macro can either be written as an empty tag or an open-close tag
  • A macro can have inner content if it's an open-close tag

$[mymacro]
$[mymacro id="2" /]
$[mymacro id="2"] inner content [/mymacro]

Examples

Macro name Description Example
Date Renders the current date $[date format="MM/dd/YYYY" adddays="1" /]
ShowForRoles Only displays the inner content if the current user is in the role supplied $[showforroles roles="Admin"]Administrator should only see this [/showforroles]


Example Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComLib.Authentication;
using ComLib.Web.Templating;
using ComLib.Web.Lib.Services.Macros;


namespace ComLib.Web.Modules.Macros
{
    [Macro( Name = "showforroles", DisplayName = "Show For Roles", Description = "Shows the content supplied for the roles specified", IsReusable = true, HasInnerContent = true)]
    [MacroParameter( Name = "roles", Description = "Name of the roles to show the content for", DataType = typeof(string), Example = "Admin", ExampleMultiple = "? | * | Admin | Users,Moderators" )]
    public class ShowForRolesMacro : IMacro
    {
        /// <summary>
        /// Process the tag.
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        public string Process(Tag tag)
        {
            if (!tag.Attributes.Contains("roles"))
                return string.Empty;

            var roles = tag.Attributes["roles"] as string;
            if (roles == "?")
                return tag.InnerContent;

            bool isAuthenticated = Auth.IsAuthenticated();
            if (roles == "*" && isAuthenticated)
                return tag.InnerContent;

            string result = Auth.IsUserInRoles(roles)
                          ? tag.InnerContent
                          : string.Empty;
            return result;
        }
    }
}

Information

Information extensions are simple classes that just return html or text and are used for simple reporting purposes
The example below is an information extension that is used to get the main configuration settings of the system.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using ComLib.Authentication;
using ComLib.Environments;
using ComLib.Configuration;
using ComLib.Logging;
using ComLib.Notifications;
using ComLib.Web.Lib.Core;
using ComLib.Web.Lib.Attributes;
using ComLib.Web.Lib.Services.Information;


namespace ComLib.Web.Modules.Information
{

    [Info(Name = "System", Description = "System Summary", Roles = "Admin")]
    public class SystemInfo : IInformation
    {
        /// <summary>
        /// The supported formats.
        /// </summary>
        public string SupportedFormats { get { return "html"; } }


        /// <summary>
        /// The current format to get the information in.
        /// </summary>
        public string Format { get; set; }


        /// <summary>
        /// Gets the information.
        /// </summary>
        /// <returns></returns>
        public string GetInfo()
        {
            IList<KeyValuePair<string, string>> summary = new List<KeyValuePair<string, string>>();
            summary.Add(new KeyValuePair<string, string>("Environment", string.Format("{0}[{1}]", Env.Name, Env.EnvType.ToString())));
            summary.Add(new KeyValuePair<string, string>("Configuration", Env.RefPath));
            summary.Add(new KeyValuePair<string, string>("Database Server", Config.Get<string>("Database", "server")));
            summary.Add(new KeyValuePair<string, string>("Database Name", Config.Get<string>("Database", "database")));
            summary.Add(new KeyValuePair<string, string>("LogLevel", Logger.Default.Level.ToString()));
            summary.Add(new KeyValuePair<string, string>("Emails", Notifier.Settings.EnableNotifications.ToString()));
            summary.Add(new KeyValuePair<string, string>("Machine", Environment.MachineName));
            summary.Add(new KeyValuePair<string, string>("Started", ((DateTime)HttpContext.Current.Application["start_time"]).ToString("yyyy-MMM-dd : HH:mm:ss")));
            summary.Add(new KeyValuePair<string, string>("User", Auth.UserShortName));
            summary.Add(new KeyValuePair<string, string>("Version", typeof(ComLib.BoolMessage).Assembly.GetName().Version.ToString() + " - CommonLibrary.dll" ));
            summary.Add(new KeyValuePair<string, string>("Version", typeof(ModuleMap).Assembly.GetName().Version.ToString() + " - CommonLibrary.Web.Lib.dll"));
            summary.Add(new KeyValuePair<string, string>("Version", typeof(ComLib.Web.Modules.Events.Event).Assembly.GetName().Version.ToString() + " - CommonLibrary.Web.Modules.dll"));

            var buffer = new StringBuilder();
            buffer.Append("<table class=\"systemlist\">");
            buffer.Append("<tr><th>Area</th><th>Setting</th></tr>");
            foreach (var entry in summary)
            {
                buffer.Append("<tr>");
                buffer.Append("<td>" + entry.Key + "</td>");
                buffer.Append("<td>" + entry.Value + "</td>");
                buffer.Append("</tr>");
            }
            buffer.Append("</table>");
            string html = buffer.ToString();
            return html;
        }
    }
}

Last edited Mar 15, 2011 at 4:53 AM by kishore_reddy, version 26

Comments

No comments yet.