Overview Overview This walkthrough assumes that you understand the basics of the AdvantageModule/Dialog concepts that provided in Module Walkthrough (Dialog Content). If please refer to that tutorial, as this walkthrough will build upon previous concepts. In most cases a module using structured content will still require a dialog in order to provide context for front-end expression. This sample will use a "Blog" article as the typed data source. AdvantageModuleRewrite is an ASP.net user-control referred to as a "Rewrite Module" that inherit from the specific class. These controls provide the front-end expression of content. When working with tool-based "structured content", the information is supplied to the control based on a tool that is registered with the system. AdvantageModuleRewrite contains all of the functionality of an AdvantageModule The goal of this walkthrough will be to Create a front-end AdvantageModuleRewrite user-control that can used as both a "Blog List" control (when not rewriten) and a "Blog Artilce" control when it has been rewritten. AdvantageModuleRewrite In rewrite mode the information is retrieved from the "MyObject" property that is typed to the generic <T> in the module definition. Steps Intro This guide provides a step-by-step walkthrough for creating an "AdvantageModuleRewrite" with an already designed Tool (structure content) object. 1.Create structured content (Advantage Tool) This sample will use a predefined "BlogArticle" tool. For instructions for creating a tool see here. 2. Create a class to store dialog attributes This class will be used to store multiple attributes in a structured format. 3. Develop a Dialog Control The first step in the process is to create an admin data entry user-control that inherits from AdvantageAttributeControl. 4. Binding information that needs to be passed from Dialog to Module Using the control.ascx file create the html control elements In the control.ascx.cs file complete the methods "LoadDataFromObject(AdvantageAttributeArgs e)" and "SaveDataToObject()" storing/retrieving the "Attributes" information to be passed to the expression module. 5. Retrieving Information for front-end Using the control.ascx file create the html display elements In the control.ascx.cs retrieve the "Attributes" information to be passed from the dialog module for configuration display Based on if the module has been rewritten, show list mode or show detail mode. 6. Create an AdvantageModuleRewrite user-control The first step in the process is to create a front-end expression user-control that inherits from AdvantageModule. 5.Register the Module/Dialog The final step in creating an module is to register within the system. Registration makes the module available for use, linking it with the system's backend and frontend components. By following these steps—you can effectively set up an AdvantageModule for managing page content. Storing Data - Attributes Understanding AdvantageAttributes methods & properties Passing data between the administrative portal and the front end expression is used via the AdvantageAttributes. Storing information is done via the "Add" method. This method is overloaded allowing for native datatypes, as well as any class object that is serializable. Information is retrieved via the "GetAttribute" method. This method is overloaded allowing for native datatypes, as well as any class object that is serializable. Create the BlogAttributes class Storage Object (Optional) The attributes from the dialog may be stored directly, however developers may find it coinvent and easier to code if you store to a class. The class must be serializable to be used. .csnamespace AdvantageClient { /// <summary> /// provides the construct for passing data from dialog to module /// </summary> public class BlogAttributes { public static string Key { get { return "BlogAttributes"; } } public string LinkTo { get; set; } public string Lister { get; set; } public string Layout { get; set; } public bool ShowImage { get; set; } public bool ShowTags { get; set; } public string MaxItems { get; set; } public bool ShowFilters { get; set; } public string Pagination { get; set; } public string PerPage { get; set; } } } Create A Dialog Control The dialog control provides the data-entry screen for the AdvantageModule front-end display. Create new User control that inherits from AdvantageAttributeControl Add a html binding in the .ascx file. .ascx<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ArticleDialog.ascx.cs" Inherits="AdvantageClient.Modules_BlogArticleDialog" %> <%@ Register Assembly="AdvantageCMS.Web.UI" TagPrefix="Advantage" Namespace="AdvantageCMS.Web.UI" %> <style> .blog-layout-choice { list-style:none; float: left; } .blog-layout-choice li {width: 93px; float: left; margin: 0 4px 0 4px; } .blog-layout-choice li input { margin: .5em 0 0 0; } .blog-layout-choice li label { margin: 0 0 0 .2em; width: 5em !important; margin-top: 0.4em !important; margin-left: .5em !important; } .blog-layout-choice li:first-child { margin-left: 0; } .blog-layout-choice li .tile { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUMAAAA2CAYAAABdq/vBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3FpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzI3RTBEMkExQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI3RTBEMjkxQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJlMWVhNzQ0LWU4MjUtZTY0My05NTc4LTFkNzJjYzE5MTEzMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4IsgBOAAAE/UlEQVR42uydT0gUURzHnyEF/TMMJC9pUJfKoot1yEPQpRSSOgQWYXYqOnSLLqVdskO3sFNlhEKBQaB1CQzyUpci86SQ7kUxjLY0qIvNb3S22XVmd2bn7czO7OcDi+ub55ud73vzm9/v/db3qpSNracfHjR+bFNg8enni0tpebO8vOxaqaqqCg3RMFIN0U/DGFwV75Txox8BHek2hOwpdCOjIRpGqSH6aRiDhoANYjkRMC/t6aGul24Ha848QkM0jFRD9As+BtcZr04ELMi1AsfREA2j1hD9Ao7BdegDAIAxBADAGAIAYAwBADCGAAAYQwAAjCEAAMYQAABjCACAMQQAyEu19aapsVbduXgYRWwMjk6qwbdTnuujIRpGrSH6Fa9fxhjWbFqvWvbXo5yNsYk5X/XREA2j1hD9itePMBkAAGMIAJATJtv5/HVBjXxIBW786L4dji77nWcfM+87ju1WDXVbPJcLl9v2qm2bNjiec2B0UqXmF9ec30u50Nq8Ux3YtT3wtf9Y+qMGR6dUeulvaJ1p1ywJyDiUECdMDaPE7X5Bw3A0dDSGNx6/9z3X4zh/sXG9Sj09n/n93ZdZ84OOTcxmGZ//sX3h8vGv31c+49lDWeeStpt21RrHF9T49EqdnXWbbX9XuFyMopxr5PbJwNd+5f47LQ8UPzwYnsjSO8503H0Tun7lgCRAxu61o2EEGlaX8kOlf2c/jdpuvVbpoS5XY+OlvPXmK8c60vZwzwnV23XE8biXcvFA7YY30LVH8CTO1bvmzCN9bRv9Fuq1VIgnk4v1YEbD8DWsjtuF6vDa3FjxNg8xeVJG6DTCOh8OkDyqkQBDAgAhG0MdN6dMCMt3qXITBTranpn/ZYYWOhIo5UCcw2S8OQib2H21RpI7kqUtBdKutA8AeIYlxcoml6ptySa7feWGMJlrwMuEsvEMJeNbyratr90AAJS1Z6gDssmVBd4cJNIzBADAM1RkkwEAz1BfKEs2GQDibgwl41vKtmVxBACAsjeGZJMBoFwhm2wPwckmA+AZAgDgGYYE2WQAwDPUFcqSTQaAuHuGfcMTpuclK02fO7bHLJMssLWqtpdyN4bfz2QWdbRWwRZPz244C5XHHVlZHOhDNCxOQ0dj2FHA8Hglt52UYYTEYDUt1Sq1eiz1bTGzurSX8tbmBnOfgzUh+O+/tlWqV4ybGN7slavzl0u79i0BgiB71/Y+/xjqasOXW/clZkD3XW0xl6yXfqqc/TvqzX1s0DAaDR2NoXhh5zQZRDtOS++7ncut/Eqb8w3vVF/m/pyyz27lOlfUkXMMXj/OY7pIZE5Y+tqtvwENdUM2GQDAzTNMwgrJAJUO97EGYwh6sBJGgt/9of2We0kyoeHacrf26T88Q7w5jdgnrv3uD+23/KiqR8Miy53aT0L/cR8TJpcNbgmUIPtG5ytP4tYHpdYwX5Ir7v3HfewPEiglQgb2wOhkqOdM2mIVUWgYJSw2Qpic0PBuzgyDmAdCw6jgPsYzBADQ4xlCcCQ7aP7nDJ5ArDTEk8MYgmbM0I7wDg0hfmFyan6RZfNzGJ9e8FUfDfVoWKm4bV3hR0PGYPFjUDzDfuPVLQsjNFwYQDln+j0cR0NNGrJXclEaMgYDjkHxDGeMVyc65RXwSYE6aIiGUWuIfgHHoDVnKJXeGq9GNMtienWQeQEN0TBqDdEvgH7/BBgAFqwscR94x34AAAAASUVORK5CYII=) no-repeat 0 0; width: 75px; height: 55px; display: inline-block; } .blog-layout-choice li .listing { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUMAAAA2CAYAAABdq/vBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3FpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzI3RTBEMkExQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI3RTBEMjkxQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJlMWVhNzQ0LWU4MjUtZTY0My05NTc4LTFkNzJjYzE5MTEzMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4IsgBOAAAE/UlEQVR42uydT0gUURzHnyEF/TMMJC9pUJfKoot1yEPQpRSSOgQWYXYqOnSLLqVdskO3sFNlhEKBQaB1CQzyUpci86SQ7kUxjLY0qIvNb3S22XVmd2bn7czO7OcDi+ub55ud73vzm9/v/db3qpSNracfHjR+bFNg8enni0tpebO8vOxaqaqqCg3RMFIN0U/DGFwV75Txox8BHek2hOwpdCOjIRpGqSH6aRiDhoANYjkRMC/t6aGul24Ha848QkM0jFRD9As+BtcZr04ELMi1AsfREA2j1hD9Ao7BdegDAIAxBADAGAIAYAwBADCGAAAYQwAAjCEAAMYQAABjCACAMQQAyEu19aapsVbduXgYRWwMjk6qwbdTnuujIRpGrSH6Fa9fxhjWbFqvWvbXo5yNsYk5X/XREA2j1hD9itePMBkAAGMIAJATJtv5/HVBjXxIBW786L4dji77nWcfM+87ju1WDXVbPJcLl9v2qm2bNjiec2B0UqXmF9ec30u50Nq8Ux3YtT3wtf9Y+qMGR6dUeulvaJ1p1ywJyDiUECdMDaPE7X5Bw3A0dDSGNx6/9z3X4zh/sXG9Sj09n/n93ZdZ84OOTcxmGZ//sX3h8vGv31c+49lDWeeStpt21RrHF9T49EqdnXWbbX9XuFyMopxr5PbJwNd+5f47LQ8UPzwYnsjSO8503H0Tun7lgCRAxu61o2EEGlaX8kOlf2c/jdpuvVbpoS5XY+OlvPXmK8c60vZwzwnV23XE8biXcvFA7YY30LVH8CTO1bvmzCN9bRv9Fuq1VIgnk4v1YEbD8DWsjtuF6vDa3FjxNg8xeVJG6DTCOh8OkDyqkQBDAgAhG0MdN6dMCMt3qXITBTranpn/ZYYWOhIo5UCcw2S8OQib2H21RpI7kqUtBdKutA8AeIYlxcoml6ptySa7feWGMJlrwMuEsvEMJeNbyratr90AAJS1Z6gDssmVBd4cJNIzBADAM1RkkwEAz1BfKEs2GQDibgwl41vKtmVxBACAsjeGZJMBoFwhm2wPwckmA+AZAgDgGYYE2WQAwDPUFcqSTQaAuHuGfcMTpuclK02fO7bHLJMssLWqtpdyN4bfz2QWdbRWwRZPz244C5XHHVlZHOhDNCxOQ0dj2FHA8Hglt52UYYTEYDUt1Sq1eiz1bTGzurSX8tbmBnOfgzUh+O+/tlWqV4ybGN7slavzl0u79i0BgiB71/Y+/xjqasOXW/clZkD3XW0xl6yXfqqc/TvqzX1s0DAaDR2NoXhh5zQZRDtOS++7ncut/Eqb8w3vVF/m/pyyz27lOlfUkXMMXj/OY7pIZE5Y+tqtvwENdUM2GQDAzTNMwgrJAJUO97EGYwh6sBJGgt/9of2We0kyoeHacrf26T88Q7w5jdgnrv3uD+23/KiqR8Miy53aT0L/cR8TJpcNbgmUIPtG5ytP4tYHpdYwX5Ir7v3HfewPEiglQgb2wOhkqOdM2mIVUWgYJSw2Qpic0PBuzgyDmAdCw6jgPsYzBADQ4xlCcCQ7aP7nDJ5ArDTEk8MYgmbM0I7wDg0hfmFyan6RZfNzGJ9e8FUfDfVoWKm4bV3hR0PGYPFjUDzDfuPVLQsjNFwYQDln+j0cR0NNGrJXclEaMgYDjkHxDGeMVyc65RXwSYE6aIiGUWuIfgHHoDVnKJXeGq9GNMtienWQeQEN0TBqDdEvgH7/BBgAFqwscR94x34AAAAASUVORK5CYII=) no-repeat -81px 0; width: 75px; height: 55px; display: inline-block; } .blog-layout-choice li .image-tile { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUMAAAA2CAYAAABdq/vBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3FpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzI3RTBEMkExQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI3RTBEMjkxQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJlMWVhNzQ0LWU4MjUtZTY0My05NTc4LTFkNzJjYzE5MTEzMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4IsgBOAAAE/UlEQVR42uydT0gUURzHnyEF/TMMJC9pUJfKoot1yEPQpRSSOgQWYXYqOnSLLqVdskO3sFNlhEKBQaB1CQzyUpci86SQ7kUxjLY0qIvNb3S22XVmd2bn7czO7OcDi+ub55ud73vzm9/v/db3qpSNracfHjR+bFNg8enni0tpebO8vOxaqaqqCg3RMFIN0U/DGFwV75Txox8BHek2hOwpdCOjIRpGqSH6aRiDhoANYjkRMC/t6aGul24Ha848QkM0jFRD9As+BtcZr04ELMi1AsfREA2j1hD9Ao7BdegDAIAxBADAGAIAYAwBADCGAAAYQwAAjCEAAMYQAABjCACAMQQAyEu19aapsVbduXgYRWwMjk6qwbdTnuujIRpGrSH6Fa9fxhjWbFqvWvbXo5yNsYk5X/XREA2j1hD9itePMBkAAGMIAJATJtv5/HVBjXxIBW786L4dji77nWcfM+87ju1WDXVbPJcLl9v2qm2bNjiec2B0UqXmF9ec30u50Nq8Ux3YtT3wtf9Y+qMGR6dUeulvaJ1p1ywJyDiUECdMDaPE7X5Bw3A0dDSGNx6/9z3X4zh/sXG9Sj09n/n93ZdZ84OOTcxmGZ//sX3h8vGv31c+49lDWeeStpt21RrHF9T49EqdnXWbbX9XuFyMopxr5PbJwNd+5f47LQ8UPzwYnsjSO8503H0Tun7lgCRAxu61o2EEGlaX8kOlf2c/jdpuvVbpoS5XY+OlvPXmK8c60vZwzwnV23XE8biXcvFA7YY30LVH8CTO1bvmzCN9bRv9Fuq1VIgnk4v1YEbD8DWsjtuF6vDa3FjxNg8xeVJG6DTCOh8OkDyqkQBDAgAhG0MdN6dMCMt3qXITBTranpn/ZYYWOhIo5UCcw2S8OQib2H21RpI7kqUtBdKutA8AeIYlxcoml6ptySa7feWGMJlrwMuEsvEMJeNbyratr90AAJS1Z6gDssmVBd4cJNIzBADAM1RkkwEAz1BfKEs2GQDibgwl41vKtmVxBACAsjeGZJMBoFwhm2wPwckmA+AZAgDgGYYE2WQAwDPUFcqSTQaAuHuGfcMTpuclK02fO7bHLJMssLWqtpdyN4bfz2QWdbRWwRZPz244C5XHHVlZHOhDNCxOQ0dj2FHA8Hglt52UYYTEYDUt1Sq1eiz1bTGzurSX8tbmBnOfgzUh+O+/tlWqV4ybGN7slavzl0u79i0BgiB71/Y+/xjqasOXW/clZkD3XW0xl6yXfqqc/TvqzX1s0DAaDR2NoXhh5zQZRDtOS++7ncut/Eqb8w3vVF/m/pyyz27lOlfUkXMMXj/OY7pIZE5Y+tqtvwENdUM2GQDAzTNMwgrJAJUO97EGYwh6sBJGgt/9of2We0kyoeHacrf26T88Q7w5jdgnrv3uD+23/KiqR8Miy53aT0L/cR8TJpcNbgmUIPtG5ytP4tYHpdYwX5Ir7v3HfewPEiglQgb2wOhkqOdM2mIVUWgYJSw2Qpic0PBuzgyDmAdCw6jgPsYzBADQ4xlCcCQ7aP7nDJ5ArDTEk8MYgmbM0I7wDg0hfmFyan6RZfNzGJ9e8FUfDfVoWKm4bV3hR0PGYPFjUDzDfuPVLQsjNFwYQDln+j0cR0NNGrJXclEaMgYDjkHxDGeMVyc65RXwSYE6aIiGUWuIfgHHoDVnKJXeGq9GNMtienWQeQEN0TBqDdEvgH7/BBgAFqwscR94x34AAAAASUVORK5CYII=) no-repeat -162px 0; width: 75px; height: 55px; display: inline-block;} .blog-layout-choice li .full { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUMAAAA2CAYAAABdq/vBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3FpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzI3RTBEMkExQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI3RTBEMjkxQjc5MTFFNjg1MjFGQTBBQkIxRDFBN0QiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJlMWVhNzQ0LWU4MjUtZTY0My05NTc4LTFkNzJjYzE5MTEzMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2MWJjZmRhOS04NWRmLTZhNDQtYmJmMy01YjQ5YWY1MTRmZjQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4IsgBOAAAE/UlEQVR42uydT0gUURzHnyEF/TMMJC9pUJfKoot1yEPQpRSSOgQWYXYqOnSLLqVdskO3sFNlhEKBQaB1CQzyUpci86SQ7kUxjLY0qIvNb3S22XVmd2bn7czO7OcDi+ub55ud73vzm9/v/db3qpSNracfHjR+bFNg8enni0tpebO8vOxaqaqqCg3RMFIN0U/DGFwV75Txox8BHek2hOwpdCOjIRpGqSH6aRiDhoANYjkRMC/t6aGul24Ha848QkM0jFRD9As+BtcZr04ELMi1AsfREA2j1hD9Ao7BdegDAIAxBADAGAIAYAwBADCGAAAYQwAAjCEAAMYQAABjCACAMQQAyEu19aapsVbduXgYRWwMjk6qwbdTnuujIRpGrSH6Fa9fxhjWbFqvWvbXo5yNsYk5X/XREA2j1hD9itePMBkAAGMIAJATJtv5/HVBjXxIBW786L4dji77nWcfM+87ju1WDXVbPJcLl9v2qm2bNjiec2B0UqXmF9ec30u50Nq8Ux3YtT3wtf9Y+qMGR6dUeulvaJ1p1ywJyDiUECdMDaPE7X5Bw3A0dDSGNx6/9z3X4zh/sXG9Sj09n/n93ZdZ84OOTcxmGZ//sX3h8vGv31c+49lDWeeStpt21RrHF9T49EqdnXWbbX9XuFyMopxr5PbJwNd+5f47LQ8UPzwYnsjSO8503H0Tun7lgCRAxu61o2EEGlaX8kOlf2c/jdpuvVbpoS5XY+OlvPXmK8c60vZwzwnV23XE8biXcvFA7YY30LVH8CTO1bvmzCN9bRv9Fuq1VIgnk4v1YEbD8DWsjtuF6vDa3FjxNg8xeVJG6DTCOh8OkDyqkQBDAgAhG0MdN6dMCMt3qXITBTranpn/ZYYWOhIo5UCcw2S8OQib2H21RpI7kqUtBdKutA8AeIYlxcoml6ptySa7feWGMJlrwMuEsvEMJeNbyratr90AAJS1Z6gDssmVBd4cJNIzBADAM1RkkwEAz1BfKEs2GQDibgwl41vKtmVxBACAsjeGZJMBoFwhm2wPwckmA+AZAgDgGYYE2WQAwDPUFcqSTQaAuHuGfcMTpuclK02fO7bHLJMssLWqtpdyN4bfz2QWdbRWwRZPz244C5XHHVlZHOhDNCxOQ0dj2FHA8Hglt52UYYTEYDUt1Sq1eiz1bTGzurSX8tbmBnOfgzUh+O+/tlWqV4ybGN7slavzl0u79i0BgiB71/Y+/xjqasOXW/clZkD3XW0xl6yXfqqc/TvqzX1s0DAaDR2NoXhh5zQZRDtOS++7ncut/Eqb8w3vVF/m/pyyz27lOlfUkXMMXj/OY7pIZE5Y+tqtvwENdUM2GQDAzTNMwgrJAJUO97EGYwh6sBJGgt/9of2We0kyoeHacrf26T88Q7w5jdgnrv3uD+23/KiqR8Miy53aT0L/cR8TJpcNbgmUIPtG5ytP4tYHpdYwX5Ir7v3HfewPEiglQgb2wOhkqOdM2mIVUWgYJSw2Qpic0PBuzgyDmAdCw6jgPsYzBADQ4xlCcCQ7aP7nDJ5ArDTEk8MYgmbM0I7wDg0hfmFyan6RZfNzGJ9e8FUfDfVoWKm4bV3hR0PGYPFjUDzDfuPVLQsjNFwYQDln+j0cR0NNGrJXclEaMgYDjkHxDGeMVyc65RXwSYE6aIiGUWuIfgHHoDVnKJXeGq9GNMtienWQeQEN0TBqDdEvgH7/BBgAFqwscR94x34AAAAASUVORK5CYII=) no-repeat -243px 0; width: 75px; height: 55px; display: inline-block; } </style> <fieldset> <legend>Paths</legend> <div class="form-row"> <label>Detail Page</label> <telerik:RadTextBox runat="server" ID="tbLink" /> <p class="no-label control">(leave blank to link to self)</p> </div> <div class="form-row"> <label>List Page</label> <telerik:RadTextBox runat="server" ID="tbLister" /> <p class="no-label control">(leave blank to link to self)</p> </div> </fieldset> <fieldset> <legend>List Settings</legend> <div class="form-row checkedLabels"> <label>Layout</label> <asp:RadioButtonList ID="rblLayout" runat="server" RepeatLayout="UnorderedList" CssClass="blog-layout-choice"> <asp:ListItem Text="Tile" Value="tile" Title="Tile layout"><span class="tile"></span></asp:ListItem> <asp:ListItem Text="Listing" Value="listing" Title="Listing layout"><span class="listing"></span></asp:ListItem> <asp:ListItem Text="Image Tile" Value="image-tile" Title="Full image tile layout"><span class="image-tile"></span></asp:ListItem> <asp:ListItem Text="Full" Value="full" Title="Full width listing layout"><span class="full"></span></asp:ListItem> </asp:RadioButtonList> </div> <div class="form-row"> <label>Show Image</label> <Advantage:AdvantageCheckBox runat="server" id="cbImage" /> </div> <div class="form-row"> <label>Show Tags</label> <Advantage:AdvantageCheckBox runat="server" id="cbTags" /> </div> <div class="form-row"> <label>Show Applied Filters</label> <Advantage:AdvantageCheckBox runat="server" id="cbFilters" /> </div> <div class="form-row"> <label>Max Items To Show</label> <telerik:RadDropDownList ID="ddlMaxItems" runat="server"> <Items> <telerik:DropDownListItem Text="All" /> <telerik:DropDownListItem Text="3" Value="3" /> <telerik:DropDownListItem Text="5" Value="5" /> <telerik:DropDownListItem Text="10" Value="10" /> <telerik:DropDownListItem Text="20" Value="20" /> <telerik:DropDownListItem Text="50" Value="50" /> </Items> </telerik:RadDropDownList> </div> </fieldset> <fieldset> <legend>Pagination Settings</legend> <div class="form-row"> <label>Scrolling or Paging</label> <asp:RadioButtonList runat="server" ID="rdPagination" AutoPostBack="true"> <asp:ListItem Text="Infinite Scroll" Value="infinite" /> <asp:ListItem Text="Paging" Value="paging" /> </asp:RadioButtonList> </div> <asp:PlaceHolder runat="server" ID="phPaginationEntries"> <div class="form-row"> <label>Entries Per Page</label> <telerik:RadDropDownList ID="ddlPerPage" runat="server"> <Items> <telerik:DropDownListItem Text="All" /> <telerik:DropDownListItem Text="3" Value="3" /> <telerik:DropDownListItem Text="5" Value="5" /> <telerik:DropDownListItem Text="10" Value="10" /> <telerik:DropDownListItem Text="20" Value="20" /> <telerik:DropDownListItem Text="50" Value="50" /> </Items> </telerik:RadDropDownList> </div> </asp:PlaceHolder> </fieldset> Bind html control content to the attributes. .ascx.csnamespace AdvantageClient { public partial class Modules_BlogArticleDialog : AdvantageAttributeControl { protected override void OnInit(EventArgs e) { base.OnInit(e); rdPagination.SelectedIndexChanged += RdPagination_SelectedIndexChanged; } private void RdPagination_SelectedIndexChanged(object sender, EventArgs e) { phPaginationEntries.Visible = rdPagination.SelectedValue == "paging"; } protected override void SaveDataToObject() { //save to attributes/content... var attribs = new BlogAttributes(); attribs.LinkTo= tbLink.Text; attribs.Lister=tbLister.Text; attribs.layout=rblLayout.SelectedValue; attribs.ShowImage=cbImage.Checked; attribs.ShowTags=cbTags.Checked; attribs.MaxItems=ddlMaxItems.SelectedValue; attribs.ShowFilters=cbFilters.Checked; attribs.Pagination=rdPagination.SelectedValue; attribs.PerPage=ddlPerPage.SelectedValue; Attributes.Add(BlogAttributes.Key, attribs); } protected override void LoadDataFromObject(AdvantageAttributeArgs e) { //Load or initialize the Attributes storage class var attribs = Attributes.GetAttribute<BlogAttributes>(BlogAttributes.Key); if (attribs == null) attribs = new BlogAttributes(); //load screen with attributes/content..... tbLink.Text = attribs.LinkTo; tbLister.Text = attribs.Lister; rblLayout.SelectedValue = attribs.layout; cbImage.Checked = attribs.ShowImage; cbTags.Checked = attribs.ShowTags; cbFilters.Checked = attribs.ShowFilters; try { ddlPerPage.SelectedValue = attribs.PerPage; } catch { ddlPerPage.SelectedIndex = 0; } try { ddlMaxItems.SelectedValue = attribs.MaxItems; } catch { ddlMaxItems.SelectedIndex = 0; } try { rdPagination.SelectedValue = attribs.Pagination; phPaginationEntries.Visible = rdPagination.SelectedValue == "paging"; } catch { rdPagination.SelectedValue = "paging"; phPaginationEntries.Visible = true; } } } } Create AdvantageModuleRewrite The module control retrieves data set in the dialog and provides the front-end display. Create new User control that inherits from AdvantageModuleRewrite This control has dual modes. When the system detects that it has been "rewritten" based on the url SEOName, it will display the single article view panel. When it has not been rewritten, it will display the list view of articles. The list view uses an associated usercontrol that formats the tile. ascx file<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Article.ascx.cs" Inherits="AdvantageClient.Modules_BlogArticle" %> <!--Control that formats the listing article --> <%@ Register Src="BlogArticleListing.ascx" TagPrefix="advantage" TagName="BlogArticleListing" %> <asp:Panel CssClass="blog " runat="server" id="pnlBlog"> <!-- To change layout, use different classes next to .blog: tile, image-tile, listing --> <!-- When this control has NOT been rewritten, it will be a Blog Listing --> <asp:PlaceHolder runat="server" ID="phListing"> <asp:Literal runat="server" ID="litFilter"></asp:Literal> <asp:Literal runat="server" ID="litNoPosts"></asp:Literal> <asp:UpdatePanel runat="server" ID="upBlogPosts" ChildrenAsTriggers="true" UpdateMode="Conditional"> <ContentTemplate> <%-- Listing View --%> <asp:Repeater runat="server" ID="rptBlogList"> <ItemTemplate> <article class="listing"> <advantage:BlogArticleListing runat="server" Article='<%# (BlogArticle)Container.DataItem %>' ListPath='<%# ObjectAttributes.Lister%>' DetailPath='<%# ObjectAttributes.LinkTo %>' ShowComments='False' ShowImage='<%# ObjectAttributes.ShowImage %>' /> </article> </ItemTemplate> </asp:Repeater> <asp:HiddenField runat="server" ID="hdnMaxPage" Value="1" /> <asp:HiddenField runat="server" ID="hdnCurrentPage" Value="1" /> <asp:HiddenField runat="server" ID="hdnPageCount" Value="1" /> <div style="display: none"> <asp:Button runat="server" ID="btnPaging" /> </div> </ContentTemplate> </asp:UpdatePanel> <!-- Pager --> <asp:PlaceHolder ID="plcPager" runat="server" Visible="false"> <asp:Repeater ID="rptPager" runat="server" OnItemDataBound="rptPager_ItemDataBound"> <HeaderTemplate> <ul class="pagination clearfix"> <li> <asp:HyperLink Text="Prev" ID="hlPrev" CssClass="prev collapse" runat="server" NavigateUrl="javascript:void(0);" onclick="lnkPrev();" /> </li> </HeaderTemplate> <ItemTemplate> <asp:PlaceHolder ID="plc" runat="server" Visible='<%# Container.ItemIndex % PageSize == 0 %>'> <li> <asp:HyperLink ID="hlLink" runat="server" NavigateUrl="javascript:void(0);" /> </li> </asp:PlaceHolder> </ItemTemplate> <FooterTemplate> <li> <asp:HyperLink Text="Next" CssClass="next" ID="hlNext" onclick="lnkNext();" runat="server" NavigateUrl="javascript:void(0);" /> </li> </ul> </FooterTemplate> </asp:Repeater> </asp:PlaceHolder> </asp:PlaceHolder> <!-- When this control HAS been rewritten, Detail View --> <asp:PlaceHolder runat="server" ID="phSinglePost" Visible="false"> <asp:HyperLink runat="server" ID ="hlURL">Return</asp:HyperLink> <article class="detail"> <advantage:AdvantageDisplayImage runat="server" ID="picDetail" DefaultImage="Detail" /> <section class="main"> <h1><%= MyObject.Title %></h1> <time><%= MyObject.ArticleDate %></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> <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> </asp:PlaceHolder> </asp:Panel> <!--Registering bottom scripts --> <asp:PlaceHolder id="phBottomJavascriptBlog" runat="server"> <script> $(window).load(function () { blogLoadResize(); }); // this function is also called from the resize event in base-function.js blogLoadResize = function () { equalHeight('.blog.tile div article'); }; function lnkClick(i) { $('.pagination li a').removeClass('current'); $('.pagination li a[data-index=' + i + ']').addClass('current'); $('.next').removeClass("collapse"); $('.prev').removeClass("collapse"); $('#<%= hdnCurrentPage.ClientID %>').val(i); if (parseInt($('#<%= hdnCurrentPage.ClientID %>').val()) >= parseInt($('#<%= hdnPageCount.ClientID %>').val())) { $('.next').addClass("collapse"); } if (parseInt($('#<%= hdnCurrentPage.ClientID %>').val()) <= 1) { $('.prev').addClass("collapse"); } $('#<%= btnPaging.ClientID %>').click(); } function lnkPrev() { var prev = parseInt($('#<%= hdnCurrentPage.ClientID %>').val()) - 1; if (prev > 0) lnkClick(prev); else lnkClick(0); } function lnkNext() { var next = parseInt($('#<%= hdnCurrentPage.ClientID %>').val()) + 1; lnkClick(next); } </script> <!--Allow for infinit scrolling --> <asp:PlaceHolder id="phInfiniteScroll" runat="server" Visible="false"> <script type="text/javascript"> function pageLoad(sender, args) { if ($('#<%= hdnCurrentPage.ClientID %>').val() <= $('#<%= hdnMaxPage.ClientID %>').val()) { if ($(document).height() <= $(window).height()) { __doPostBack('<%= upBlogPosts.UniqueID %>', "ScrollOn"); } } $(window).scroll(function () { if ($(window).scrollTop() == $(document).height() - $(window).height()) { if ($('#<%= hdnCurrentPage.ClientID %>').val() <= $('#<%= hdnMaxPage.ClientID %>').val()) { __doPostBack('<%= upBlogPosts.UniqueID %>', "ScrollOn"); } } }); blogLoadResize(); }; </script> </asp:PlaceHolder> </asp:PlaceHolder> Bind attributes to the html control content. ascx.cs filenamespace AdvantageClient { public partial class Modules_BlogArticle : AdvantageModuleRewrite<BlogArticle> { #region Properties protected BlogAttributes ObjectAttributes; protected int PageSize = 10; private readonly List<string> filters = new(); private List<BlogArticle> blogPosts; protected List<BlogArticle> BlogPosts { get { //If null, get list from cache. blogPosts ??= ModuleEngine.WebCache_Get<List<BlogArticle>>("BlogArticleList"); //If still null, get list from database and cache it for 30 minutes. if (blogPosts == null) { blogPosts = ModuleEngine.GetAllPublishedObjects<BlogArticle>().OrderByDescending(o => o.ArticleDate) .ToList(); ModuleEngine.WebCache_Store("BlogArticleList", "Blog", blogPosts, 30, HttpContext.Current); } //There are no posts, return empty list if (blogPosts == null) { blogPosts = new List<BlogArticle>(); return blogPosts; } //Apply any filters blogPosts = ApplyFilters(blogPosts); return blogPosts; } } private List<BlogArticle> ApplyFilters(List<BlogArticle> blogPosts) { //If there is a max items attribute, take that many items if (ObjectAttributes.MaxItems.ToInteger(0) > 0) blogPosts = blogPosts.Take(int.Parse(ObjectAttributes.MaxItems)).ToList(); if (Request.QueryString["year"] != null) { filters.Add("Year"); var year = WebUtility.UrlDecode(Request.QueryString["year"]); blogPosts = blogPosts.Where(p => p.ArticleDate.Year.Equals(Convert.ToInt16(year))).ToList(); } if (Request.QueryString["month"] != null) { filters.Add("Month"); var month = WebUtility.UrlDecode(Request.QueryString["month"]); blogPosts = blogPosts.Where(p => p.ArticleDate.Month.Equals(Convert.ToInt16(month))).ToList(); } if (Request.QueryString["tag"] != null) { filters.Add("Tag"); var tag = WebUtility.UrlDecode(Request.QueryString["tag"]); blogPosts = blogPosts.Where(p => p.Tags.Contains(tag)).ToList(); } if (Request.QueryString["cat"] != null) { filters.Add("Category"); var cat = WebUtility.UrlDecode(Request.QueryString["cat"]); var bc = ModuleEngine .GetPublishedObjectsBykey<BlogCategory>("SEOName", eComparison.Equals, cat).FirstOrDefault(); if (bc != null) blogPosts = blogPosts.Where(p => p.Category.Equals(bc.MasterID)).ToList(); } return blogPosts; } #endregion #region Page Events protected override void OnInit(EventArgs e) { base.OnInit(e); btnPaging.Click += btnPaging_Click; //Load or initialize the object attribute ObjectAttributes = Attributes.GetAttribute<BlogAttributes>(BlogAttributes.Key) ?? new BlogAttributes(); } protected void Page_Load(object sender, EventArgs e) { InjectJavascriptIntoContentPlaceHolder(); if (IsReWrite) { //single article view phListing.Visible = false; phSinglePost.Visible = true; setOpenGraphTags(); hlURL.NavigateUrl = !string.IsNullOrWhiteSpace(ObjectAttributes.Lister) ? ObjectAttributes.Lister : CurrentNavigationPage.FullUrl; picDetail.AdvantageImage = MyObject.ArticleImage; if (ObjectAttributes.ShowTags) { rptTags.DataSource = MyObject.Tags; rptTags.DataBind(); } } else { //lister view pnlBlog.CssClass += $" {ObjectAttributes.Layout}"; var itemsPerPage = ObjectAttributes.PerPage; if (string.IsNullOrWhiteSpace(itemsPerPage) || itemsPerPage == "ALL") PageSize = 10; else PageSize = Convert.ToInt32(itemsPerPage); hdnPageCount.Value = (BlogPosts.Count / PageSize + 1).ToString(); if (!IsPostBack) { rebindPosts(); } else { //infinite scroll var passedArgument = Request.Params.Get("__EVENTARGUMENT"); if (passedArgument == "ScrollOn") { ScrollOn(); upBlogPosts.Update(); } } //display the applied filters if (filters.Count > 0 && ObjectAttributes.ShowFilters) litFilter.Text = string.Format("<p>You are currently filtering by : {0}</p>", string.Join(", ", filters.ToArray())); } } protected override void OnPreRender(EventArgs e) { //set the page title on single article if (IsReWrite) Page.Title = string.Format("{0} | {1}", Page.Title, MyObject.Title); base.OnPreRender(e); } #endregion #region Events private void btnPaging_Click(object sender, EventArgs e) { var t = Convert.ToInt32(hdnCurrentPage.Value); if (t > 0) t = t - 1; rptBlogList.DataSource = BlogPosts.Skip(t * PageSize).Take(PageSize); rptBlogList.DataBind(); upBlogPosts.Update(); } public void rptPager_ItemDataBound(object sender, RepeaterItemEventArgs e) { if ((e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) && e.Item.ItemIndex % PageSize == 0) { var hlLink = (HyperLink) e.Item.FindControl("hlLink"); hlLink.Text = string.Format("{0:0}", Math.Floor(e.Item.ItemIndex / (double) PageSize) + 1); hlLink.Attributes.Add("data-index", (Math.Floor(e.Item.ItemIndex / (double) PageSize) + 1).ToString()); if (hlLink.Text == "1") hlLink.CssClass = "paging" + e.Item.ItemIndex + hlLink.Text + " current"; else hlLink.CssClass = "paging" + e.Item.ItemIndex + hlLink.Text; hlLink.Attributes["onclick"] = string.Format("lnkClick({0:0})", Math.Floor(e.Item.ItemIndex / (double) PageSize) + 1); } } #endregion #region Private Methods /// <summary> /// Open Graph tags for Social Share. When shared site will scrape these details for the share text /// </summary> private void setOpenGraphTags() { var ogTitle = new HtmlMeta(); ogTitle.Attributes["property"] = "og:title"; ogTitle.Attributes["content"] = string.Format("{0}", MyObject.Title); var ogType = new HtmlMeta(); ogType.Attributes["property"] = "og:type"; ogType.Attributes["content"] = "website"; //don't add an image tag if it is not set or errors try { var ogImage = new HtmlMeta(); ogImage.Attributes["property"] = "og:image"; if (!string.IsNullOrEmpty(MyObject.ArticleImage.GetImageElementByName("Thumbnail").ImageUrl)) { ogImage.Attributes["content"] = string.Format("http://{0}{1}", Request.Url.Host, MyObject.ArticleImage.GetImageElementByName("Thumbnail").ImageUrl); Page.Header.Controls.Add(ogImage); } else if (!string.IsNullOrEmpty(MyObject.ArticleImage.GetImageElementByName("Desktop").ImageUrl)) { ogImage.Attributes["content"] = string.Format("http://{0}{1}", Request.Url.Host, MyObject.ArticleImage.GetImageElementByName("Thumbnail").ImageUrl); Page.Header.Controls.Add(ogImage); } } catch { //don't add an image tag } var ogUrl = new HtmlMeta(); ogUrl.Attributes["property"] = "og:url"; ogUrl.Attributes["content"] = string.Format("http://{0}{1}", Request.Url.Host, Request.RawUrl); var ogDescription = new HtmlMeta(); ogDescription.Attributes["property"] = "og:description"; if (!string.IsNullOrEmpty(MyObject.Summary)) ogDescription.Attributes["content"] = MyObject.Summary.Replace('\n', ' ').Replace('\r', ' '); Page.Header.Controls.Add(ogTitle); Page.Header.Controls.Add(ogType); Page.Header.Controls.Add(ogUrl); Page.Header.Controls.Add(ogDescription); } private void rebindPosts() { if (BlogPosts != null && BlogPosts.Count > 0) { rptBlogList.DataSource = BlogPosts.Take(PageSize); rptBlogList.DataBind(); rptPager.DataSource = BlogPosts; rptPager.ItemDataBound += rptPager_ItemDataBound; hdnMaxPage.Value = (BlogPosts.Count() / PageSize).ToString(); if (!IsPostBack) { rptPager.DataBind(); if (ObjectAttributes.Pagination != "infinite") { if (BlogPosts.ToList().Count() / (double) PageSize > 1) plcPager.Visible = true; } else { phInfiniteScroll.Visible = true; } } } else { litNoPosts.Text = string.Format("<p>No posts found matching the selected criteria. <a href='{0}'>Return</a></p>", CurrentNavigationPage.FullUrl); } } private void ScrollOn() { try { var currentPage = hdnCurrentPage.Value; hdnCurrentPage.Value = (Convert.ToInt16(currentPage) + 1).ToString(); var take = 0; if (PageSize * (Convert.ToInt16(currentPage) + 1) < BlogPosts.Count()) take = PageSize * (Convert.ToInt16(currentPage) + 1); else take = BlogPosts.Count(); rptBlogList.DataSource = BlogPosts.Take(take); rptBlogList.DataBind(); } catch { } } private void InjectJavascriptIntoContentPlaceHolder() { RegisterBottomScript(phBottomJavascriptBlog); } #endregion } } Tile view control This sample uses a separate control to provide the html markup for the individual listview items. Although not required, it simplifies the html markup in the BlogArticle control. BlogArticleListing View<%@ Control Language="C#" AutoEventWireup="true" CodeFile="BlogArticleListing.ascx.cs" Inherits="AdvantageClient.Modules_BlogArticleListing" %> <asp:PlaceHolder runat="server" ID="phTitle"> <img src='<%# Article.ArticleImage.GetImageElementByName("Medium").ImageUrl %>' alt='<%# Article.ArticleImage.AltText %>' /> </asp:PlaceHolder> <section class="main"> <h2><%# Article.Title %></h2> <time><%# "Posted on: " + Article.ArticleDate.ToString("dd MMMM yyyy") %></time> <%# GetCategory(Article.Category) %> <p><%# Article.Summary %></p> <p><%# Article.Author %></p> </section> <asp:Repeater runat="server" ID="rptTags"> <HeaderTemplate> <section class="tags"> <ul> </HeaderTemplate> <ItemTemplate> <li> <a href='<%# CurrentNavigationPage.FullUrl + "?tag=" + System.Net.WebUtility.UrlEncode(Container.DataItem.ToString()) %>'>#<%# Container.DataItem %></a> </li> </ItemTemplate> <FooterTemplate> </ul> </section> </FooterTemplate> </asp:Repeater> <footer> <div class="meta" style="display:none;">xxx comments</div> <ul class="controls"> <asp:PlaceHolder runat="server" ID="phReadMore"> <li> <a title='<%# Article.Title %>' href='<%#$"/{DetailPath}{Article.SEOName}" %>'> Read More </a> </li> </asp:PlaceHolder> </ul> </footer> namespace AdvantageClient { public partial class Modules_BlogArticleListing : AdvantageModule { public BlogArticle Article { get; set; } public string ListPath { get; set; } public string DetailPath { get; set; } public bool ShowComments { get; set; } public bool ShowImage { get; set; } public bool ShowTags { get; set; } protected void Page_Load(object sender, EventArgs e) { phTitle.Visible = ShowImage; if (ShowTags) { rptTags.DataSource = Article.Tags; rptTags.DataBind(); } } protected string GetCategory(Guid masterId) { string retVal = string.Empty; BlogCategory category = ModuleEngine.GetPublishedObjectByMasterId<BlogCategory>(masterId); if (category != null) { string href = string.Format("/{0}?cat={1}", ListPath, category.SEOName); retVal = @"<div class=""category""> <a href=""{0}"">{1}</a> </div>"; retVal = String.Format(retVal, href, category.Name); } return retVal; } } } Register the module How to register a Module in Advantage CSP Go to Sytem Administrator > Modules > Modules The module should be available in your widgets list inside the page manager.