Tool Walkthrough: Managing Structured Content in Advantage CSP

Overview

Structured Content (Tool)

Overview

Content that follows a specific, repeatable format that is stored and available throughout the project (i.e. Blog, News, Events, etc.)

Structured content consists of:

  • A Class that inherits from  BusinessObject<T> to provide the data structure.
  • A user-control that inherits from ToolGridList<T> to provide the list interface for managing entries.
  • A user-control that inherits from ToolControl<T> to provide the interface for managing data entry.

Once created the Control must be registered: System Administrator -> Data Sections -> Tools, and then provide permissions to the applicable roles.

Usage

Data can be accessed via the AdvantageModuleEngine that is available in the following components 

  • AdvantagePageTemplate
  • AdvantageModule
  • AdvantageModuleRewrite

There are many ways to access the data.  Please review the Methods of AdvantageModuleEngine.

 

Data Entity (BusinessObject)

BusinessObject<T> : BusinessObjectBase

Methods & Properties

Overview

A BusinessObject is a container that holds the data entity you are creating. There is no need to interact with the database. All CRUD and versioning happens automatically when you inherit from BusinessObject<self>

Any public property with a GET/SET will automatically be saved.


Excluding a public property is possible if you decorate the property with [XMLIgnore].

Override methods (Mandatory)

SetSummaryDataRow -> Used to create the data expression that will be used to fill the list tool for managing the data add/edit screen.

Override methods (optional)

SetSearchableProperties -> used to allow data to be indexed for performance benefits during searching and display.

Usage

  • Creating fields are done using properties.
  • Specifying List fields for the tool control is done inside the SetSummaryDataRow method using AddSummaryDataRow(title, data)

Example

 

BlogArticle BusinessObject

namespace AdvantageClient
{
    /// <summary>
    /// Summary description for BlogArticle
    /// </summary>
    public class BlogArticle : BusinessObject<BlogArticle>
    {
        //Blogs are limited to the current domain only!
        public override bool RestrictToCurrentDomain { get { return true; } }

        //Title of article
        public string Title { get; set; }

        //Image of article
        private AdvantageImage _image = null;
        public AdvantageImage Image
        {
            get
            {
                if (_image == null)
                {
                    this._image = new AdvantageImage();

                    this._image.AddImageElement(new AdvantageImageElement() { ImageName = "Mobile",  });
                    this._image.AddImageElement(new AdvantageImageElement() { ImageName = "Desktop", BreakPointName = "Small"});
                    this._image.DefaultElementName = "Mobile";
                }
                return _image;
            }
            set { _image = value; }
        }

        //Date of article
        private DateTime? _postedDate = null;
        public DateTime PostedDate
        {
            get
            {
                if (_postedDate==null) _postedDate=DateTime.Now;
                return (DateTime) _postedDate;
            }
            set { _postedDate = value; }
        }

        //Summary of article
        public string Summary { get; set; }
        
        //Content of article
        public string Content { get; set; }

        //Author of article
        public string Author { get; set; }

        //Is it a featured article
        public bool IsFeatured { get; set; }

        //Tags of article
        public List<string> Tags { get; set; }

        //Categories of article
        public List<Guid> Categories { get; set; }


        
        //Defines the columns and data to be displayed in the lister.
        // (Title,Date)
        protected override void SetSummaryDataRow()
        {
            if (string.IsNullOrEmpty(Title)) Title = "";
            if (Title.Length > 50)
            {
                AddSummaryDataRow("Title", Title.PadRight(50).Substring(0, 50) + "...");
            }
            else
            {
                AddSummaryDataRow("Title", Title);
            }
            AddSummaryDataRow("Date", PostedDate);
        }


        //Defines indexed lookup values that can be used when retrieving lists
        //when using AdvantageModuleEngine.
        protected override void SetSearchableProperties()
        {
            if (Tags != null) foreach (string tag in Tags) AddSearchableProperty("Tag", tag);
            if (Categories != null && Categories.Count > 0)
            {
                //create an entry for each category so it can be retrieved by the AdvantageModuleEngine
                foreach (Guid category in Categories)
                    AddSearchableProperty("Category", category);
            }
            AddSearchableProperty("PostedDate", PostedDate);
        }

        //Optional  PostPublish is called after the object is published.
        public override bool PostPublish(ActionArgs e, out eCMSEngineEventStatus status, out string message)
        {



            //This example stores the list of blog articles in cache on the front-end expression.
            //Once an article is published, the cache should be invalidated.
            AdvantageCMS.Core.Utils.Cache.AdvantageCacheManager cache = new AdvantageCacheManager(eAdvantageCacheContainerType.WebServer, e.AdminDomainId, e.AdminLanguageId, e.CurrentSql);
                cache.Invalidate(new string[] { "Blog" });

            //This example also logs each article view in a separate object.
            //Create an initial log object if it does not exist.
            using (AdvantageModuleEngine me = new AdvantageModuleEngine(e.CurrentSql, e.AdminDomainId, e.AdminLanguageId))
            {
                
                List<BlogLog> bls = me.GetPublishedObjectsBykey<BlogLog>("ArticleID", eComparison.Equals, this.MasterID);
                if ((bls == null) || (bls.Count == 0))
                {
                    BlogLog bl; bl = new BlogLog();
                    bl.ArticleID = this.MasterID;
                    bl.Views = 0;
                    ToolHelperResult success = me.BusinessHelper.PublishObject<BlogLog>(e, bl);
                }
            }

            return base.PostPublish(e, out status, out message);
        }

    }
}

Supporting classes/objects for example

BlogLog BusinessObject

namespace AdvantageClient
{
    public class BlogLog : BusinessObject<BlogLog>
    {
        public Guid ArticleID { get; set; }

        public int Views { get; set; }

        protected override void SetSummaryDataRow()
        { AddSummaryDataRow("Display", ArticleID); }

        protected override void SetSearchableProperties()
        { AddSearchableProperty("ArticleID", ArticleID); }

        public override bool Validate(eCMSActions action, ActionArgs e, out eCMSEngineEventStatus status, out string message)
        {
            status = eCMSEngineEventStatus.Success;
            message = string.Empty;

            if (action == eCMSActions.Publish && ArticleID == Guid.Empty)
            {
                message += "Article Id cannot be blank.<br/>";
                status = eCMSEngineEventStatus.Warning;
            }
            return status == eCMSEngineEventStatus.Success;
        }

    }
}

 


 

Tool List Control

ToolGridList<T>:ToolListBase (usercontrol)

Properties & Methods

Overview

A user-control that allows a developer to create a list of all the structured content of BusinessObject<T> to be managed in the admin interface.

  

Usage

Adding the control and registering it with the associated BusinessObject allows for management of the entry records. 

  • Define the GridList Control to bind.
  • Defined the fields to display based on the SetSummaryDataRow method defined in the BusinessObject<T>
  • Override the GetDataSource() method to customize display entries.
 

Example

BlogArticleList.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="BlogArticleList.ascx.cs" Inherits="AdvantageClient.ClientAdmin_BlogArticleList" %>


<!-- This is the control that will be used to display the list of blog articles -->
<advantage:AdvantageToolList runat="server" ID="StandardAdvantageToolListControl1" />

BlogArticleList.ascx.cs

namespace AdvantageClient
{

    public partial class ClientAdmin_BlogArticleList : ToolGridList<BlogArticle>
    {
        protected override void OnInit(EventArgs e)
        {
            grdToolList = StandardAdvantageToolListControl1.Grid;
            base.OnInit(e);
        }

        public override void DefineGridColumns()
        {
            DefineColumn("Title", "Title", eDataType.String, true, true, true);
            DefineColumn("Date", "Date", eDataType.Date, true, true, true);
            DefineColumn("My Custom Field", "MyField", eDataType.String, true, true, true);
        }

        //Simple example showing how you can intercept the display and add a custom field
        public override DataTable GetDataSource()
        {
            var dt = base.GetDataSource();
            dt.Columns.Add("MyField", typeof(string));
            foreach (DataRow row in dt.Rows)
            {
                row["MyField"] = row["Title"]+"-"+row["Date"].ToString();
            }
            return dt;
        }

    
    }

 

 

 

Tool Control

ToolControl<T>:ToolListBase (usercontrol)

Properties & Methods

Overview

A user-control that binds the GFUI interface to the BusinessObject<T>. 

Developers use the following methods:

  • protected override void LoadDataFromObject(ActionArgs e) -> Allows for loading data from the BusinessObject<T> to the GUI
  • protected override void SaveDataToObject() -> Allows for saving from the GUI to the BusinessObject<T>

Example

Blog article with categories and tags

BlogArticle.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="BlogArticle.ascx.cs" Inherits="AdvantageClient.ClientAdmin_BlogArticle" %>

<%@ Register TagPrefix="Advantage" Namespace="AdvantageCMS.Web.UI" Assembly="AdvantageCMS.Web.UI" %>

<asp:PlaceHolder runat="server" ID="phTop">
    <style>
        input[type="text"].four-row {
            height: 200px !important;
        }
    </style>
</asp:PlaceHolder>

<!-- Tab strip UI -->
<telerik:RadTabStrip runat="server" ID="tabConfig" SelectedIndex="0" AutoPostBack="false" MultiPageID="multiPage1">
    <Tabs>
        <telerik:RadTab Text="Properties" Selected="true" PageViewID="pvProperties"></telerik:RadTab>
        <telerik:RadTab Text="Taxonomy" PageViewID="pvTaxonomy"></telerik:RadTab>
        <telerik:RadTab Text="Content" PageViewID="pvContent"></telerik:RadTab>
    </Tabs>
</telerik:RadTabStrip>
<telerik:RadMultiPage ID="multiPage1" runat="server" SelectedIndex="0">
    <!--Article Properties-->
    <telerik:RadPageView runat="server" ID="pvProperties">


        <fieldset>
            <legend>Article Information</legend>

            <div class="form-row">
                <label>Title</label>
                <telerik:RadTextBox runat="server" ID="txtTitle"></telerik:RadTextBox>
            </div>
            <div class="form-row">
                <label>SEO Name</label>
                <telerik:RadTextBox runat="server" ID="txtSEO"></telerik:RadTextBox>
            </div>
            <div class="form-row">
                <label>Date</label>
                <telerik:RadDatePicker ID="dtDate" runat="server"></telerik:RadDatePicker>
            </div>

            <div class="form-row">
                <label>Summary</label>
                <telerik:RadTextBox runat="server" ID="txtSummary" Rows="4" CssClass="four-row" TextMode="Multiline" MaxLength="500"></telerik:RadTextBox>
            </div>

            <div class="form-row">
                <label>Published By</label>
                <telerik:RadTextBox runat="server" ID="txtPublishedBy"></telerik:RadTextBox>
            </div>
            <div class="form-row">
                <label>Is Featured</label>
                <asp:CheckBox runat="server" ID="chkFeaturedPage" />
            </div>
            <div class="form-row">
                <label>Article Image</label>
                <advantage:AdvantageSelectorImage runat="server" ID="imgArticle" />
            </div>
        </fieldset>
    </telerik:RadPageView>
    <!--Taxonomy (Categories, Tags)-->
    <telerik:RadPageView runat="server" ID="pvTaxonomy">
        <fieldset>
            <legend>Taxonomy</legend>
            <div class="form-row">
                <label>Categories</label>
                <asp:CheckBoxList runat="server" RepeatColumns="4" ID="chkLstCategories" />
            </div>


            <div class="form-row">
                <label>Tags</label>
                <telerik:RadAutoCompleteBox RenderMode="Lightweight" runat="server" ID="txtTagList" AllowCustomEntry="True" Filter="Contains" InputType="Token" EnableClientFiltering="True" AutoPostBack="false" Width="72%">
                    <TokensSettings AllowTokenEditing="False" />
                </telerik:RadAutoCompleteBox>
            </div>

        </fieldset>
    </telerik:RadPageView>
    <!--Content-->
    <telerik:RadPageView runat="server" ID="pvContent">
        <advantage:AdvantageEditor ID="editorContent" runat="server" Title="Content"></advantage:AdvantageEditor>
    </telerik:RadPageView>
</telerik:RadMultiPage>




<asp:PlaceHolder runat="server" ID="phBottom"></asp:PlaceHolder>

 

BlogArticle.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using AdvantageCMS.Core.Admin.BaseClasses;
using AdvantageCMS.Core.Admin.Event;
using AdvantageCMS.Core.Common;
using AdvantageCMS.Core.Common.BaseClasses;
using AdvantageCMS.Core.Common.Engine;
using AdvantageCMS.Core.Common.Extension;
using Telerik.Web.UI;


namespace AdvantageClient
{

    public partial class ClientAdmin_BlogArticle : ToolControl<BlogArticle>
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            //Register the script to top of page
            RegisterTopScript(phTop);
            //Register the script to bottom of page
            RegisterBottomScript(phBottom);

            //Load the categories to checklist control
            if (chkLstCategories.Items.Count == 0)
            {
                chkLstCategories.DataSource = ModuleEngine.GetAllPublishedObjects<BlogCategory>().OrderBy(cat => cat.Name);
                chkLstCategories.DataTextField = "Name";
                chkLstCategories.DataValueField = "MasterID";
                chkLstCategories.DataBind();
            }

        }

        //Save data to object (called when save/publish button is clicked)
        protected override void SaveDataToObject()
        {
            if (string.IsNullOrEmpty(txtSEO.Text)) txtSEO.Text = txtTitle.Text;
            MyObject.SEOName = txtSEO.Text.UrlFriendly();
            MyObject.Title = txtTitle.Text;
            MyObject.Image = imgArticle.GetAdvantageImage();
            MyObject.PostedDate = (dtDate?.SelectedDate ?? DateTime.Now);
            MyObject.Content = editorContent.Content;
            MyObject.Summary = txtSummary.Text;
            MyObject.Author = txtPublishedBy.Text;
            MyObject.IsFeatured = chkFeaturedPage.Checked;
  
            List<Guid> categories = new List<Guid>();
            foreach (var item in chkLstCategories.GetCheckedItems())
                categories.Add(item);

            MyObject.Categories = categories;
            List<string> tags = new List<string>();
            foreach (AutoCompleteBoxEntry entry in txtTagList.Entries)
                tags.Add(entry.Text);
        

            MyObject.Tags = tags;

            MyObject.Content = editorContent.Content;
        }

        //Load data from object (called when add/edit button is clicked)
        protected override void LoadDataFromObject(ActionArgs e)
        {
            txtTitle.Text = MyObject.Title;
            editorContent.Content = MyObject.Content;
            txtSummary.Text = MyObject.Summary;
            txtPublishedBy.Text = MyObject.Author;
            txtSEO.Text = MyObject.SEOName;
            
            imgArticle.SetAdvantageImage(MyObject.Image);

  
            LoadTags(MyObject.Tags);
            
            if (MyObject.Categories != null)
                chkLstCategories.SetCheckedItems(MyObject.Categories);


            chkFeaturedPage.Checked = MyObject.IsFeatured;
            dtDate.SelectedDate = MyObject.PostedDate;
        
            tabConfig.SelectedIndex = 0;
        }

        //Get all tags and load to autocompletebox
        private void LoadTags(List<string> tagList)
        {
            txtTagList.Entries.Clear();
            List<string> tags = ModuleEngine.GetPublishedKeyValueObjects<BlogArticle, string>("Tag");
            if (tagList != null)
            {
                foreach (string s in tagList)
                {
                    tags.Add(s);
                    AutoCompleteBoxEntry e = new AutoCompleteBoxEntry(s, s);
                    txtTagList.Entries.Add(e);
                }
            }
            txtTagList.DataSource = tags.Distinct();
            txtTagList.DataBind();
        }
    }
}

 

  
Back to Top Button