Data Components Data Components The "Data Components" section you've described focuses on the elements within the Advantage CSP framework designed to facilitate data entry and management from the admin interface for display on the website's front end. This part of the framework ensures that content managers or administrators can efficiently input and update content that visitors will see on the live site. Let's delve into the specifics of these components, particularly focusing on Modules & Dialogs. Overview AdvantageModule: This is a front-end component used to display unstructured content on the website. "Unstructured" here implies that the content does not follow a rigid database schema or format, allowing for more flexible and varied content types such as text, images, videos, or custom HTML blocks. The AdvantageModule serves as a container for this content on the website, enabling it to be placed and styled according to the site's design requirements. AdvantageAttributeControl: Accompanying the AdvantageModule is the AdvantageAttributeControl, a dialog-based control within the admin interface. This control is specifically designed for content managers to input or update the data that the AdvantageModule will display. It provides a user-friendly interface for managing the content of modules, ensuring that administrators can easily modify the website's content without needing to directly interact with the site's underlying code or databases.Modules & Dialogs For unstructured page content the front end module (AdvantageModule) displays on the front end. An associated dialog control (AdvavantageAttributeControl) is used to enter the data in the page manager. Modules & Dialogs are registered in the System Administrator -> Module section. Integration and Registration System Administrator -> Module Section: To make these components available for use, they must be registered within the System Administrator area, under the Module section. This registration process involves specifying which modules and dialog controls are available for use, configuring settings related to their operation, and defining how they interact with the rest of the system. Modules & Dialogs Registration: This registration is a crucial step as it ensures that the modules and dialogs are recognized by the Advantage CSP framework and can be properly utilized within the admin interface and reflected on the front end. It also allows for the setting of permissions, ensuring that only authorized users can edit or manage the content within these modules. Benefits Flexibility and Ease of Use: By separating the content management (via AdvantageAttributeControl) from the content display (via AdvantageModule), the framework allows for a flexible and intuitive approach to website content management. Administrators can easily update content without needing technical knowledge of web development, while developers can focus on the design and functionality of the website without being bogged down by content changes. Customization and Scalability: The system allows for the customization of modules and dialogs, meaning that as the needs of the application grow or change, new types of content can be easily incorporated. This scalability is key to maintaining a dynamic and engaging website over time. Conclusion In summary, the Data Components of the Advantage CSP framework, particularly the Modules & Dialogs, offer a streamlined and efficient way for administrators to manage website content. By providing tools that are both powerful and user-friendly, the framework supports the ongoing need for websites to adapt and evolve their content to meet user needs and expectations. AdvantageAttributeControl (Dialogs, Custom Properties) AdvantageAttributeControl: System.Web.UI.UserControl Members & Properties AdvantageAttributeControl is an ASP.NET control that extends System.Web.UI.UserControl. It serves multiple purposes within the AdvantageCSP framework, including acting as a dialog for customizing module content and as a "Custom Properties" control for enabling domain-specific custom logic during the rendering of an Advantage page. Interactions with AdvantageCSP Framework SiteSettings: Provides access to the site settings module defined for the domain. ConfigurationEngine: A reference to the CMSConfigurationEngine. ModuleEngine: A reference to the AdvantageModuleEngine. AdminDomain: A reference to the current domain. AdminLanguage: A reference to the current language. CurrentSitePage: A reference to the SitePage (page content) being rendered. CurrentNavigationPage: A reference to the Navigation being rendered. CurrentSql: A reference to the current Advantage SQL connection context. CurrentUser: A reference to the current logged-in user. Attributes: Access to methods to set data. ShowStatus: Renders a message to the user. Primary Usage To use AdvantageAttributeControl Dialog: for customizing AdvantageModule/AdvantageModuleRewrite content Custom Properties: Enable domain-specific custom content (custom properties citation needed). Example Html<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ContentAreaDialog.ascx.cs" Inherits="AdvantageClient.Modules_ContentAreaDialog" %> <fieldset> <legend> Content Area </legend> <Advantage:AdvantageEditor id="edtContent" runat="server" ></Advantage:AdvantageEditor> </fieldset> Code Behindusing AdvantageCMS.Core.Common.BaseClasses; namespace AdvantageClient { public partial class Modules_ContentAreaDialog : AdvantageAttributeControl { protected override void SaveDataToObject() { Attributes.Add("Content", edtContent.Content); } protected override void LoadDataFromObject(AdvantageAttributeArgs e) { edtContent.Content = SafeString(Attributes.GetString("Content")); } public override bool ValidateSave(out string message) { message = string.Empty; bool retval = true; if (string.IsNullOrEmpty(edtContent.Content)) { message = "Please enter Content.<br/>"; retval = false; } return retval; } } } Retrieving/Saving Data SaveDataToObject() This method is responsible for saving data from the control to an object. In this specific implementation, it adds the content of the edtContent control to the Attributes collection (AdvantageAttributeArgs) under the key "Content". This is typically called during the save process to persist user input or control states. LoadDataFromObject(AdvantageAttributeArgs e): This method loads data from an object into the control. It takes an AdvantageAttributeArgs object as a parameter, which likely contains the data to be loaded. In this implementation, it retrieves the content stored in the "Content" attribute of the Attributes collection and sets it to the edtContent control. This method is typically called during the load process to restore previously saved data. ValidateSave(out string message): This method validates data before saving it. It checks if the content of the edtContent control is empty or null. If the content is missing, it sets an error message indicating that the user should enter the content. This method returns a boolean value indicating whether the data is valid or not. If valid, it returns true; otherwise, it returns false, along with an error message. This is often used to ensure data integrity and to prompt users to fill in required fields before proceeding with the save operation. Conclusion AdvantageAttributeControl provides a versatile tool for integrating custom logic and content customization within the AdvantageCSP framework. By leveraging its interactions with various framework components, developers can create dynamic and tailored user experiences for their web applications. AdvantageModule AdvantageModule: System.Web.UserControl The Advantage module is the main front-end component responsible for rendering content to the user. It plays a crucial role in presenting information and functionality within the Advantage CSP system. Methods & Properties Registration Once created, the module must be registered in the Advantage CSP admin under System Administrator -> Modules. Data Retrieval The module retrieves data primarily through the following sources: Attributes Property: Provides data entered via the admin through the associated Dialog (AdvantageAttributeControl). ModuleEngine: This object facilitates retrieval from the database of all "Structured Content". SiteSettings: Custom control developed for your specific project and registered in the Domain Manager. Caching Because Advantage CSP is multi-site, multi-lingual, The WebCacheManager is available to ensure that no cache is overwritten with the same name. Example ContentArea.ascx<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ContentArea.ascx.cs" Inherits="AdvantageClient.Modules_ContentArea" %> <asp:Literal ID="litContent" runat="server"></asp:Literal> ContentArea.ascx.csusing System; using AdvantageCMS.Core.Common.BaseClasses; namespace AdvantageClient { public partial class Modules_ContentArea : AdvantageModule { protected void Page_Load(object sender, EventArgs e) { litContent.Text = Attributes.GetString("Content"); } } } AdvantageModuleRewrite AdvantageModuleRewrite :AdvantageModule Methods & Properties AdvantageModuleRewrite is a front-end component that inherits from AdvantageModule. It provides additional functionality for URL rewriting and object handling. Primary Properties IsRewrite: boolean - True if this module is rewritten. MyObject: <T> - Object type loaded with the specific record. Usage When on a page, if the URL ends with the SEO name of the specified object, the module will have the following properties set. Implementation of the control requires <T> to be defined as the proper BusinessObject class. Example This example uses an Event Business Object. The front-end module is able to determine if the page has been rewritten to a specified object. If so, it will show the detail view. If it is not rewritten, it will display a list of events. Events.ascx<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Events.ascx.cs" Inherits="AdvantageClient.Modules_Events" %> <!--Helper controls --> <%@ Register Src="../../Controls/Navigation/Pagination.ascx" TagPrefix="uc1" TagName="Pagination" %> <%@ Register Src="../../Controls/Helpers/DropdownCheckbox.ascx" TagPrefix="uc1" TagName="DropdownCheckbox" %> <!--domain based style sheet--> <asp:PlaceHolder runat="server" ID="phTop"> <link href="/<%=CurrentDomain.FolderName %>/css/popper.css" rel="stylesheet"> </asp:PlaceHolder> <asp:UpdatePanel runat="server" ID="upEvents"> <ContentTemplate> <!-- List view (not rewritten) --> <asp:Panel runat="server" ID="pnlListView"> <div class=" row col-sm-12"> <asp:Panel runat="server" ID="pnlPosts" CssClass="col-lg-9 fade eventContent"> <asp:Repeater runat="server" ID="rptEventsList"> <ItemTemplate> <asp:Panel CssClass="card mb-5 " runat="server" ID="pnlItem"> <div class="cardWrapper row "> <div class="col-sm-3 p-0"> <advantage:AdvantageDisplayImage runat="server" ImageCssClass="img-fluid blogListImage" ID="aiImage" /> </div> <div class="col col-sm-9 colContent"> <h2 class="card-title h4"> <asp:Literal runat="server" ID="litTitle" /> </h2> <div class="meta mb-1"> <span class="date">Published <asp:Literal runat="server" ID="litDate" /></span> </div> <div class="intro mb-2"> <asp:Literal runat="server" ID="litSummary" /> </div> <div class="intro mb-2"> <asp:HyperLink class="btn btn-primary" runat="server" ID="hypButton"></asp:HyperLink> </div> </div> </div> </asp:Panel> </ItemTemplate> </asp:Repeater> <asp:Repeater runat="server" ID="rptEventsCard"> <ItemTemplate> <asp:Panel CssClass="card mb-5 " runat="server" ID="pnlItem"> <div class="cardWrapper"> <asp:HyperLink class="img-circle link" runat="server" ID="hypImage"> <advantage:AdvantageDisplayImage runat="server" ImageCssClass="img-fluid blogCardListImage" ID="aiImage" /> </asp:HyperLink> <div class="card-body"> <div class="small text-muted"> <asp:Literal runat="server" ID="litDate" /> </div> <h2 class="card-title h4"> <asp:Literal runat="server" ID="litTitle" /> </h2> <p class="card-text"> <asp:Literal runat="server" ID="litSummary" /> </p> <asp:HyperLink class="btn btn-primary" runat="server" ID="hypButton"></asp:HyperLink> </div> </div> </asp:Panel> </ItemTemplate> </asp:Repeater> </asp:Panel> <div class="col-lg-3"> <div class="col-sm-12"> <uc1:DropdownCheckbox runat="server" ID="DropdownCheckbox1" /> </div> <div class="col-sm-12"> <div id="calendar"></div> </div> </div> </div> <div class="blog container px-4"> <uc1:Pagination runat="server" ID="pager1" /> </div> </asp:Panel> <!-- Detail view (rewritten) --> <asp:Panel runat="server" ID="pnlDetailView"> <div class="blogContainer"> <asp:HyperLink runat="server" ID="hlURL" CssClass="btn btn-lg btn-primary">Return</asp:HyperLink> <br /> <br /> <article class="detail row"> <section class="sidebar col-md-4"> <advantage:AdvantageDisplayImage runat="server" ID="picDetail" DefaultImage="Mobile" /> </section> <section class="main col-md-8"> <h2><%= MyObject.Title %></h2> <time><%= MyObject.StartDate %></time> <div class="category"> <asp:HyperLink runat="server" ID="hlCategory"></asp:HyperLink> </div> <asp:PlaceHolder ID="phSummary" runat="server"> <p><%= MyObject.Summary %></p> </asp:PlaceHolder> <asp:PlaceHolder ID="phContent" runat="server"> <%= MyObject.Content %> </asp:PlaceHolder> </section> <!-- Tags --> <asp:Repeater runat="server" ID="rptTags"> <HeaderTemplate> <section class="tags"> <ul> </HeaderTemplate> <ItemTemplate> <a href='<%# CurrentNavigationPage.FullUrl + "?tag=" + System.Net.WebUtility.UrlEncode(Container.DataItem.ToString()) %>'>#<%# Container.DataItem %></a> </ItemTemplate> <FooterTemplate> </ul> </section> </FooterTemplate> </asp:Repeater> </article> </div> </asp:Panel> <!-- Call to trigger postback --> <asp:Button runat="server" ID="btnPostback" Style="display: none;" /> </ContentTemplate> </asp:UpdatePanel> <%-- documentation https://fullcalendar.io/docs --%> <asp:PlaceHolder runat="server" ID="phBottom"> <!-- for Bootstrap 5 domain specific css/js--> <link href="/<%= CurrentDomain.FolderName %>/css/Calendar/bootstrap-icons.css" rel="stylesheet"> <link href="/<%= CurrentDomain.FolderName %>/css/Calendar/calendar.css" rel="stylesheet" /> <script src="/<%= CurrentDomain.FolderName %>/js/Calendar/calendar.js"></script> <script src="/Scripts/umd/popper.min.js"></script> <script src="/<%= CurrentDomain.FolderName %>/js/tooltip.min.js"></script> <script> function setCalendar(events, intialDate, startRange, endRange, listPath) { const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { eventDidMount: function (info) { var tooltip = new Tooltip(info.el, { title: '<table><tr><td style="font-weight:bold;">' + info.event.title + '</td></tr><tr><td>' + info.event.extendedProps.summary + '</td></tr><tr><td>' + info.event.start.toLocaleDateString('en-us', { weekday: "long", year: "numeric", month: "short", day: "numeric" }) + '</td></tr></table>', placement: 'top', trigger: 'hover', container: 'body', html: true }); }, eventClick: function (info) { document.location.href = listPath + info.event.extendedProps.seoName; info.jsEvent.preventDefault(); // prevents browser from following link in current tab. }, handleWindowResize: true, themeSystem: 'bootstrap5', height: 'auto', contentHeight: 400, expandRows: true, headerToolbar: { left: 'title', right: 'prev,next' }, initialView: 'dayGridMonth', initialDate: intialDate, navLinks: true, // can click day/week names to navigate views editable: true, selectable: true, nowIndicator: true, dayMaxEvents: true, // allow "more" link when too many events events: events, validRange: { start: startRange, end: endRange } }); calendar.render(); document.getElementById("<%= pnlPosts.ClientID %>").classList.add("show"); } //Dropdown list will call this function to process changes. function AdvSetCategory_ClientScriptId(sender, items) { var url = AdvUpdateQueryString_ClientScriptId("tag", ""); const values = items.map(item => item.value); const valueList = values.join('.'); url = AdvUpdateQueryString_ClientScriptId("category", valueList, url); AdvResetPagination_ClientScriptId(url); } function AdvUpdateQueryString_ClientScriptId(key, value, url) { if (!url) url = window.location.href; const theUrl = new URL(url); const search_params = theUrl.searchParams; if (value === null || value === "") search_params.delete(key); else search_params.set(key, value); theUrl.search = search_params.toString(); return theUrl; } function AdvResetPagination_ClientScriptId(url) { if (url.href !== window.location.href) { url.hash = ''; refreshList(url.href); } } function refreshList(url) { var addPush = url !== null; if (url === null) url = window.location.pathname + window.location.search; if (addPush) window.history.pushState("", "", url); var el = document.getElementById('<%=this.Page.Form.ClientID%>'); if (el == null) el = document.getElementsByTagName('body')[0].getElementsByTagName('form')[0]; if (el != null) el.setAttribute('action', url); __doPostBack('<%= btnPostback.ClientID%>', ''); } window.addEventListener('popstate', function (event) { refreshList(null); }); </script> </asp:PlaceHolder> Events.ascx.csusing System; using System.Collections.Generic; using System.Linq; using System.Net.Mime; using System.Web; using System.Web.UI.WebControls; using AdvantageCMS.Core.Common; using AdvantageCMS.Core.Common.BaseClasses; using AdvantageCMS.Core.Common.Enums; using AdvantageCMS.Core.Utils; using AdvantageCMS.Web.UI; using Newtonsoft.Json; namespace AdvantageClient { public partial class Modules_Events : AdvantageModuleRewrite<Event> { private void SetDialogValues() { _detailPath = Attributes.GetString("DetailPath"); _listPath = Attributes.GetString("ListPath"); ItemsPerRow = Attributes.GetString("ItemsPerRow").ToInteger(1); ItemsPerPage = Attributes.GetInt("ItemsPerPage").ToInteger(0); ButtonText = Attributes.GetString("ButtonText"); pager1.PageIndexChanged += Pager1_OnPageIndexChanged; _pageId = (HttpContext.Current?.Request["page"] ?? "1").ToInteger(1); } #region Page Events protected override void OnInit(EventArgs e) { base.OnInit(e); rptEventsCard.ItemDataBound += rptEvent_ItemDataBound; rptEventsList.ItemDataBound += rptEvent_ItemDataBound; SetDialogValues(); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); //Show Panel and initialize on a detail record or a list view depending on if rewritten if (IsReWrite) { pnlListView.Visible = false; pnlDetailView.Visible = true; hlURL.NavigateUrl = ListPath.Substring(0,ListPath.Length-1); setImage(picDetail, MyObject); } else { pnlListView.Visible = true; pnlDetailView.Visible = false; Page.ClientScript.GetPostBackEventReference(this, ""); Categories = Server.UrlDecode(UrlUtil.GetParameter(Request.RawUrl, "Category")); if (ItemsPerPage > 0) pager1.SetPagination(SelectedEvents.Count, ItemsPerPage); } } protected override void OnPreRender(EventArgs e) { if (!IsReWrite) { LoadDropDown(); RegisterTopScript(phTop); var script = this.RenderControlContent(phBottom).Replace("_ClientScriptId", "_" + this.ClientID); phBottom.Visible = false; RegisterBottomScript(phBottom.ClientID, script); Helper.Registerpostbackscript($"setCalendar({EventArray},'{InitialDate}','{StartRange}','{EndRange}', '{ListPath}');"); BindList(PageId); } base.OnPreRender(e); } #endregion Page Events #region List Events and Methods private void Pager1_OnPageIndexChanged(object sender, int page) { _pageId = page; } private void BindList(int pageNum) { var ds = SelectedEvents; if (ItemsPerPage > 0) { ; ds = ds.Skip((pageNum - 1) * ItemsPerPage).ToList(); ds = ds.Take(ItemsPerPage).ToList(); } if (ItemsPerRow == 1) { rptEventsList.DataSource = ds; rptEventsList.DataBind(); rptEventsList.Visible = false; rptEventsList.Visible = true; } else { rptEventsCard.DataSource = ds; rptEventsCard.DataBind(); rptEventsList.Visible = false; rptEventsCard.Visible = true; } } private void rptEvent_ItemDataBound(object sender, RepeaterItemEventArgs e) { Event itm = ((Event)e.Item.DataItem); Panel pnlItem = e.Item.FindControl("pnlItem") as Panel; AdvantageDisplayImage aiImage = e.Item.FindControl("aiImage") as AdvantageDisplayImage; HyperLink hypButton = e.Item.FindControl("hypButton") as HyperLink; HyperLink hypImage = e.Item.FindControl("hypImage") as HyperLink; Literal litTitle = e.Item.FindControl("litTitle") as Literal; Literal litSummary = e.Item.FindControl("litSummary") as Literal; Literal litDate = e.Item.FindControl("litDate") as Literal; if (pnlItem != null) pnlItem.CssClass += $" {ItemsPerRowCss}"; setImage(aiImage, itm); if (hypButton != null) { hypButton.NavigateUrl = DetailPath + itm.SEOName; hypButton.Text = ButtonText; } if (litTitle != null) litTitle.Text = itm.Title; if (litSummary != null) litSummary.Text = itm.Summary; if (hypImage != null) hypImage.NavigateUrl = DetailPath + itm.SEOName; if (litDate != null) litDate.Text = itm.StartDate.ToShortDateString(); } private void setImage(AdvantageDisplayImage aiImage, Event itm) { //if we have a custom image in the event, use it. If not, use the image from the eventtype. if (aiImage != null) { if ((itm.Image?.ActiveImageElements?.Count ?? 0) > 0 && itm.Image.ActiveImageElements.Any(ee => (ee.ImageUrl ?? "") != "")) aiImage.AdvantageImage = itm.Image; else { EventType et = EventTypes.FirstOrDefault(ee => ee.MasterID.Equals(itm.EventType)); if ((et?.Image?.ActiveImageElements?.Count ?? 0) > 0 && et.Image.ActiveImageElements.Any(ee => (ee.ImageUrl ?? "") != "")) aiImage.AdvantageImage = et.Image; } } } private void LoadDropDown() { bool openDdl = (IsPostBack && LastCategories != Categories); LastCategories = Categories; DropdownCheckbox1.OnChange = "AdvSetCategory_" + this.ClientID; DropdownCheckbox1.OpenDropDown = openDdl; if (DropdownCheckbox1.Control.Items.Count == 0) { var x = Events.Select(ee => ee.EventType).Distinct().ToList(); ModuleEngine.GetPublishedObjectByMasterIds<EventType>(x); DropdownCheckbox1.Control.DataValueField = "SEOName"; DropdownCheckbox1.Control.DataTextField = "Name"; DropdownCheckbox1.Control.DataSource = ModuleEngine.GetPublishedObjectByMasterIds<EventType>(x); DropdownCheckbox1.Control.DataBind(); } foreach (ListItem item in DropdownCheckbox1.Control.Items) item.Selected = SelectedEventTypes.Count == 0 || SelectedEventTypes.Any(ee => ee.SEOName.ToString().Equals(item.Value)); } #endregion List Events and Methods #region Properties private int _pageId = 1; private int PageId { get { return _pageId; } } private string _detailPath; protected string DetailPath { get { if (string.IsNullOrEmpty(_detailPath)) _detailPath = ClientHelper.GetItemPath(this.Page, true, IsReWrite); return _detailPath; } } private string _listPath; protected string ListPath { get { if (string.IsNullOrEmpty(_listPath)) _listPath = ClientHelper.GetItemPath(this.Page, false, IsReWrite); return _listPath; } } private string _buttonText = "Event Details"; protected string ButtonText { get { return _buttonText; } set { if (!string.IsNullOrEmpty(value)) _buttonText = value; } } protected int ItemsPerPage { get; set; } = 9; protected int ItemsPerRow { get; set; } = 1; protected string ItemsPerRowCss { get { if (ItemsPerRow == 1) return " col-lg-12 "; else if (ItemsPerRow == 2) return " col-lg-6 "; else return " col-lg-12 "; } } protected string LastCategories { get { return ViewState["LastCategories"].ToString(); } set { ViewState["LastCategories"] = value; } } protected string Categories { get; set; } protected List<string> CategoryList { get { return Categories.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries).ToList(); } } private List<EventType> _eventsTypes = null; public List<EventType> EventTypes { get { if (_eventsTypes == null) _eventsTypes = ModuleEngine.GetAllPublishedObjects<EventType>().Where(ee=>Events.Any(y=>y.EventType.Equals(ee.MasterID))).ToList(); return _eventsTypes; } } private List<EventType> _selectedEventTypes = null; public List<EventType> SelectedEventTypes { get { if (_selectedEventTypes == null) _selectedEventTypes = EventTypes.Where(ee => CategoryList.Any(y => ee.SEOName.Equals(y))).ToList(); return _selectedEventTypes; } } private List<Event> _events = null; public List<Event> Events { get { if (_events == null) _events = ModuleEngine.GetPublishedObjectsBykey<Event>("StartDate", eComparison.GreaterOrEquals, DateTime.Now.AddDays(-30)).OrderBy(ee=>ee.StartDate).ToList(); return _events; } } private List<Event> _selectedEvents = null; public List<Event> SelectedEvents { get { if (_selectedEvents == null) { _selectedEvents = Events; if (SelectedEventTypes!=null && SelectedEventTypes.Count>0) _selectedEvents = _selectedEvents.Where(ee => SelectedEventTypes.Any(y => ee.EventType.Equals(y.MasterID))).OrderBy(ee => ee.StartDate).ToList(); } return _selectedEvents; } } public string EventArray { get { if (SelectedEvents == null) return null; return JsonConvert.SerializeObject(SelectedEvents.Select(ee => ee.CalendarItem).ToList()); } } public string InitialDate { get { return DateTime.Now.ToString("yyy-MM-dd"); } } public string StartRange { get { var min = Events.Min(ee => ee.StartDate); min = new DateTime(min.Year, min.Month, 1); return min.ToString("yyy-MM-dd"); } } public string EndRange { get { var max = Events.Max(ee => ee.EndDate); max = new DateTime(max.Year, max.Month + 1, 1).AddDays(-1); return max.ToString("yyy-MM-dd"); } } public string Styles { get { string s = string.Empty; var x= ModuleEngine.GetAllPublishedObjects<EventType>(); foreach (EventType type in x) s += type.CssClasses + Environment.NewLine; return s; } } #endregion Properties } }