Part 3: Creating the start page – Create an EPiServer site from scratch

Posted on December 10, 2009 by Frederik Vig in EPiServer

In this part we’re creating the start page for our site. Lets start by taking a look at the page we’re implementing.

JungleLand start page

We have one featured news story at the top, followed by a list of 4 news stories. Lets start with that. Create the page type, by creating a new class in your PageTypes folder in Visual Studio, give it a name of StartPage. Make it derive from BasePageData.

namespace Jungle.Templates.Jungle.PageTypes
{
    public class StartPage : BasePageData
    {
    }
}

Add a PageType attribute to StartPage.

[PageType(
    Name = "[Jungle] Start page",
    Filename = "/default.aspx",
    SortOrder = 1200)]
public class StartPage : BasePageData
{
}

This does the same as manually going to admin mode, creating a new page type there, and adding the information below.

Field Value
Name [Jungle] Start page
File name /default.aspx
Sort index 1200

Page Type Builder will do this for us, so no more repetitive creating Page Types with the same properties and information :). If you wish to learn more about the Page Type Builder project see its project home page.

Remember that you have an easy reference to the start page with PageReference.StartPage, which makes it a great place to store properties that get used through out the site. You can of course also use dynamic properties for this, but it is recommend to not overuse them because of the performance implications (recursively goes up the page structure to find the properties value).

We’ll probably come back here later and add new properties, but for now lets just add properties for the news functionality. We need a property that holds a reference to the featured news story, one that references the news container page, and one with how many news stories to display.

using EPiServer.Core;
using EPiServer.DataAbstraction;
using PageTypeBuilder;
 
namespace Jungle.Templates.Jungle.PageTypes
{
    [PageType(
        Name = "[Jungle] Start page",
        Filename = "/default.aspx",
        SortOrder = 1200)]
    public class StartPage : BasePageData
    {
        [PageTypeProperty(
            EditCaption = "Featured news story",
            Type = typeof(PropertyPageReference))]
        public virtual PageReference FeaturedNews
        {
            get;
            set;
        }
 
        [PageTypeProperty(
            EditCaption = "Page to get news from",
            Type = typeof(PropertyPageReference))]
        public virtual PageReference NewsContainerPage
        {
            get;
            set;
        }
 
        [PageTypeProperty(
            EditCaption = "Number of news stories to display",
            DefaultValueType = DefaultValueType.Value,
            DefaultValue = "4",
            Type = typeof(PropertyNumber))]
        public virtual int NewsCount
        {
            get;
            set;
        }
    }
}

If you build and login to admin mode, you should see our new Page Type created there, with the three properties we just created. Pretty cool huh? 🙂

Properties for [Jungle] start page in admin mode

Lets create our start page by going to edit mode (http://jungle/cms/UI/edit/), right-click the root folder, and choose Create new. Choose our newly created [Jungle] Start Page, give it a name of Jungle start, and Save and Publish it.

After creating our start page, we need to update the start page id in episerver.config to use it instead of the one created by Public Templates. Get the id by hovering over Jungle start in edit mode.

Start page id in edit mode

Open up episerver.config and search (ctrl + f) for pagestartid. Replace “3” with the new start pages id (26 in my case). Save, and verify the change by going back to edit mode (notice that the globe is now next to Jungle start).

New start page

We can now go back to admin mode and edit [Jungle] Start page, click Settings, and uncheck Available in Edit mode. No need to have unnecessary page types available.

Implementing

Delete default.aspx, right-click JungleSite, and choose Add, New Item. Choose Web Form and give it the name default.aspx. Delete everything except the first line, and add MasterPageFile=”~/Templates/Jungle/MasterPages/Site.Master”. You should now have something like this:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Jungle._default" MasterPageFile="~/Templates/Jungle/MasterPages/Site.Master" %>

Since our master page doesn’t have any ContentPlaceHolders yet, we need to add some. Before we can do that, we need to know what markup differences there is between style.html (which we use in site.master), and index.html.

I find the quickest and easiest approach is to use Firebug for this. Open up index.html and styles.html from the Jungleland downloaded files, in Firefox. Start Firebug by pressing F12. Use Firebugs inspect tool to inspect the markup of both.

Inspecting the start page markup with Firebug

Inspecting the default markup with Firebug

They are both pretty similar as you can see. The featured news story is placed right after <div id=”header”> and the content inside <div id=”main”> is a bit different. The rest is the same.

Open up Site.master and add two ContentPlaceHolders. One right after PageHeader, and one inside <div id=”main”>.

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="Jungle.Templates.Jungle.MasterPages.Site" %>
<%@ Register TagPrefix="Jungle" TagName="Header" Src="~/Templates/Jungle/Units/Header.ascx" %>
<%@ Register TagPrefix="Jungle" TagName="PageHeader" Src="~/Templates/Jungle/Units/PageHeader.ascx" %>
<%@ Register TagPrefix="Jungle" TagName="PageFooter" Src="~/Templates/Jungle/Units/PageFooter.ascx" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" runat="server" id="HtmlElement">
 
<head runat="server">
    <title runat="server"></title>
 
    <Jungle:Header runat="server" />
</head>
 
<body>
<form runat="server">
<!-- wrap -->
<div id="wrap">
 
	<!-- header -->
	<Jungle:PageHeader runat="server" />
	<asp:ContentPlaceHolder runat="server" ID="FeaturedContentRegion" />
	<!-- content -->
	<div id="content-wrap" class="clear">
 
		<div id="content">		
 
			<!-- main -->
			<div id="main">	
			    <asp:ContentPlaceHolder runat="server" ID="MainContentRegion" />
			<!-- /main -->	
			</div>
 
			<!-- sidebar -->
			<div id="sidebar">
 
				<div class="sidemenu">	
					<h3>Sidebar Menu</h3>
					<ul>				
						<li><a href="index.html">Home</a></li>
						<li><a href="index.html#TemplateInfo">TemplateInfo</a></li>
						<li><a href="style.html">Style Demo</a></li>
						<li><a href="blog.html">Blog</a></li>
						<li><a href="archives.html">Archives</a></li>
						<li><a href="http://www.styleshout.com/">More Free Templates</a></li>	
						<li><a href="http://www.4templates.com/?aff=ealigam">Premium Templates</a></li>	
					</ul>	
				</div>
 
				<div class="sidemenu">
					<h3>Sponsors</h3>
					<ul>
						<li><a href="http://themeforest.net?ref=ealigam">ThemeForest</a><span>Your Choice for Site Templates, Wordpress, Joomla and CMS Themes</span></li>
						<li><a href="http://www.4templates.com/?aff=ealigam">4templates</a><span>Low Cost Hi-Quality Templates</span></li>
						<li><a href="http://store.templatemonster.com?aff=ealigam">TemplateMonster</a><span>Delivering the Best Templates on the Net!</span></li>
						<li><a href="http://tinyurl.com/3cgv2m">Text Link Ads</a><span>Monetized your website</span></li>
						<li><a href="http://www.fotolia.com/partner/114283">Fotolia</a><span>Free stock images or from $1</span></li>
						<li><a href="http://www.dreamhost.com/r.cgi?287326">Dreamhost</a><span>Premium webhosting</span></li>
					</ul>
				</div>		
 
				<h3>Image Gallery </h3>					
 
				<p class="thumbs">
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>	
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>
					<a href="index.html"><img src="images/thumb.jpg" width="40" height="40" alt="thumbnail" /></a>				
				</p>					
 
			<!-- /sidebar -->				
			</div>		
 
		<!-- /content -->	
		</div>				
	<!-- /content-wrap -->	
	</div>	
<!-- /wrap -->
</div>
 
<!-- footer -->	
<Jungle:PageFooter runat="server" />
</form>
</body>
</html>

Back in default.aspx we can now add the Content controls.

<asp:Content runat="server" ContentPlaceHolderID="FeaturedContentRegion">   
</asp:Content>
 
<asp:Content runat="server" ContentPlaceHolderID="MainContentRegion">
</asp:Content>

Lets open up index.html in a code editor, and copy everything that is inside <div id=”main”> into MainContentRegion in default.aspx.

If we take a closer look at the markup we just copied, we see that most of it is pretty similar. After every second news story we have a <div class=”fix”></div> (for clearing the floats), we also need to add a class of either odd or even. We can delete the comments markup (our site will not have comment functionality).

<a href="index.html" class="comment">2 Comments</a>

Lets add a Repeater control, its great for lists like this.

<asp:Repeater runat="server">
        <ItemTemplate>
 
        </ItemTemplate>
        <AlternatingItemTemplate>
 
        </AlternatingItemTemplate>
    </asp:Repeater>

Place the first news story in the ItemTemplate and the second in the AlternatingItemTemplate.

<asp:Repeater runat="server">
        <ItemTemplate>
            <div class="block odd">
                <a title="" href="index.html">
                    <img src="images/thumb-1.jpg" class="thumbnail" alt="img" width="240px" height="100px" /></a>
                <div class="blk-top">
                    <h4>
                        <a href="index.html">Aliquam Risus Justo Lorem Ipsum Dolor Sit Amet</a></h4>
                    <p>
                        <span class="datetime">August 27, 2009</span></p>
                </div>
                <div class="blk-content">
                    <p>
                        Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec libero. Suspendisse
                        bibendum. Cras id urna. Morbi tincidunt, orci ac convallis aliquam, lectus turpis
                        varius lorem, eu posuere nunc justo tempus leo. Donec mattis, purus nec placerat
                        bibendum, dui pede condimentum odio, ac blandit ante orci ut diam. Cras fringilla
                        magna. Phasellus suscipit, leo a pharetra condimentum, lorem tellus eleifend magna,
                        eget fringilla velit magna id neque.
                    </p>
                    <p>
                        <a class="more" href="index.html">continue reading &raquo;</a></p>
                </div>
            </div>
        </ItemTemplate>
        <AlternatingItemTemplate>
            <div class="block even">
                <a title="" href="index.html">
                    <img src="images/thumb-2.jpg" class="thumbnail" alt="img" width="240px" height="100px" /></a>
                <div class="blk-top">
                    <h4>
                        <a href="index.html">Aliquam Risus Justo</a></h4>
                    <p>
                        <span class="datetime">August 26, 2009</span></p>
                </div>
                <div class="blk-content">
                    <p>
                        Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec libero. Suspendisse
                        bibendum. Cras id urna. Morbi tincidunt, orci ac convallis aliquam, lectus turpis
                        varius lorem, eu posuere nunc justo tempus leo. Donec mattis, purus nec placerat
                        bibendum, dui pede condimentum odio, ac blandit ante orci ut diam. Cras fringilla
                        magna. Phasellus suscipit, leo a pharetra condimentum, lorem tellus eleifend magna,
                        eget fringilla velit magna id neque. <a class="more" href="index.html">continue reading
                            &raquo;</a>
                    </p>
                    <p>
                    </p>
                </div>
            </div>
            <div class="fix">
            </div>
        </AlternatingItemTemplate>
    </asp:Repeater>

Lets clean up a little and place everything that is inside <div class=”block”> into its own user control. Name it NewsStory. Register the user control in default.aspx and add it to ItemTemplate and AlternatingItemTemplate.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Jungle._default"
    MasterPageFile="~/Templates/Jungle/MasterPages/Site.Master" %>
<%@ Register TagPrefix="Jungle" TagName="NewsStory" Src="~/Templates/Jungle/Units/NewsStory.ascx" %>
<asp:Content runat="server" ContentPlaceHolderID="FeaturedContentRegion">
</asp:Content>
<asp:Content runat="server" ContentPlaceHolderID="MainContentRegion">
    <h3>
        Recent Articles</h3>
    <asp:Repeater runat="server">
        <ItemTemplate>
            <div class="block odd">
                <Jungle:NewsStory runat="server" />
            </div>
        </ItemTemplate>
        <AlternatingItemTemplate>
            <div class="block even">
                <Jungle:NewsStory runat="server" />
            </div>
            <div class="fix"></div>
        </AlternatingItemTemplate>
    </asp:Repeater>
</asp:Content>

Go back to NewsStory.ascx, and open up the code-behind file. Replace System.Web.UI.UserControl with UserControlBase<BasePageData>.

Delete everything inside the NewsStory class, and add a new public property called NewsPage of type PageData.

// using EPiServer.Core;
public PageData NewsPage
{
	get; set;
}

Go back to NewsStory.ascx, and replace all the href attributes with

href="<%= NewsPage.LinkURL %>"

Add the PageName and PageStartPublish date as well.

<a href="<%= NewsPage.LinkURL %>">
    <img src="images/thumb-2.jpg" class="thumbnail" alt="img" width="240px" height="100px" />
</a>
<div class="blk-top">
    <h4><a href="<%= NewsPage.LinkURL %>"><%= NewsPage.PageName %></a></h4>
    <p><span class="datetime"><%= NewsPage.StartPublish.ToString("dd MMMM yyyy")%></span></p>
</div>
<div class="blk-content">
    <p>
        Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec libero. Suspendisse
        bibendum. Cras id urna. Morbi tincidunt, orci ac convallis aliquam, lectus turpis
        varius lorem, eu posuere nunc justo tempus leo. Donec mattis, purus nec placerat
        bibendum, dui pede condimentum odio, ac blandit ante orci ut diam. Cras fringilla
        magna. Phasellus suscipit, leo a pharetra condimentum, lorem tellus eleifend magna,
        eget fringilla velit magna id neque. 
 
        <a class="more" href="<%= NewsPage.LinkURL %>">continue reading &raquo;</a>
    </p>
</div>

Here’s a nice overview of the DateTime.ToString() patterns.

We now have the image and text left. For this we’re going to use Extension Methods. Create a new class in the Util folder, call it Extensions.cs. Make the class static, and add a new public static method called HtmlImage.

public static string HtmlImage(this PageData page, string imagePropertyName, string imageAltTextPropertyName)
{
}

We need to add some code for getting the list image and return the correct markup.

public static string HtmlImage(this PageData page, string imagePropertyName, string imageAltTextPropertyName)
{
    if (page.PageLink == null || page.PageLink.ID < 1)
    {
	return string.Empty;
    }
 
    if (page.IsValue(imagePropertyName))
    {
	string imageAlternativeText = page.PageName.ToWebString();
 
	if (page.IsValue(imageAltTextPropertyName))
	{
	    imageAlternativeText = page[imageAltTextPropertyName].ToWebString();
	}
 
	return string.Format("<img src=\"{0}\" alt=\"{1}\"", page[imagePropertyName], imageAlternativeText);
    }
 
    return string.Empty;
}

The next extension method we need to add is for the text.

public static string PreviewText(this PageData page, int length)
{
]

Most of the code is taken from PageList.ascx in the Public Templates, and modified a little for our use.

public static string PreviewText(this PageData page, int length)
{
    string previewText = string.Empty;
 
    if (page.PageLink == null || page.PageLink.ID < 1)
    {
	return previewText;
    }
 
    if (page.IsValue("MainIntro"))
    {
	return StripPreviewText(page["MainIntro"].ToWebString(), length);
    }
 
    if (page.IsValue("MainBody"))
    {
	previewText = page["MainBody"].ToWebString();
    }
 
    if (string.IsNullOrEmpty(previewText))
    {
	return previewText;
    }
 
    //If the MainBody contains DynamicContents, replace those with an empty string
    var regexPattern = new StringBuilder(@"<span[\s\W\w]*?classid=""");
    regexPattern.Append(DynamicContentFactory.Instance.DynamicContentId.ToString());
    regexPattern.Append(@"""[\s\W\w]*?</span>");
    previewText = Regex.Replace(previewText, regexPattern.ToString(), string.Empty, RegexOptions.IgnoreCase | RegexOptions.Multiline);
 
    return TextIndexer.StripHtml(previewText, length);
}
 
/// <summary>
/// Strips a text to a given length without splitting the last word.
/// </summary>
/// <param name="previewText">The string to shorten</param>
/// <returns>A shortened version of the given string</returns>
private static string StripPreviewText(string previewText, int maxLength)
{
    if (previewText.Length <= maxLength)
    {
	return previewText;
    }
    previewText = previewText.Substring(0, maxLength);
    // The maximum number of characters to cut from the end of the string.
    int maxCharCut = (previewText.Length > 15 ? 15 : previewText.Length - 1);
    int previousWord = previewText.LastIndexOfAny(new char[] { ' ', '.', ',', '!', '?' }, previewText.Length - 1, maxCharCut);
    if (previousWord <= 0)
    {
	previewText = previewText.Substring(0, previousWord);
    }
    return previewText + " ...";
}

Great, we can now update NewsStory.ascx, to use the extension methods instead.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="NewsStory.ascx.cs" Inherits="Jungle.Templates.Jungle.Units.NewsStory" %>
<%@ Import Namespace="Jungle.Templates.Jungle.Util"%>
<a href="<%= NewsPage.LinkURL %>">
    <%= NewsPage.HtmlImage("ListImage", "PageName") %>
</a>
<div class="blk-top">
    <h4><a href="<%= NewsPage.LinkURL %>"><%= NewsPage.PageName %></a></h4>
    <p><span class="datetime"><%= NewsPage.StartPublish.ToString("dd MMMM yyyy")%></span></p>
</div>
<div class="blk-content">
    <p>
        <%= NewsPage.PreviewText(200) %>
 
        <a class="more" href="<%= NewsPage.LinkURL %>">continue reading &raquo;</a>
    </p>
</div>

Some of the code I don’t like.

<h4><a href="<%= NewsPage.LinkURL %>"><%= NewsPage.PageName %></a></h4>

For one, we don’t Html encode the PageName. We could of course do something like this.

h4><a href="<%= NewsPage.LinkURL %>"><%= NewsPage.PageName.ToWebString() %></a></h4>

ToWebString() is an extension method that does the same as the HtmlEncode method, but escapes some tags (which are set in web.config).

Lets instead use EPiCode.Extensions new HtmlLink methods, which does all this for us. We now only have to type NewsPage.HtmlLink().

<h4><%= NewsPage.HtmlLink() %></h4>

Back in default.aspx we have to set the NewsPage property.

<asp:Content runat="server" ContentPlaceHolderID="MainContentRegion">
    <h3>Recent Articles</h3>
    <asp:Repeater runat="server">
        <ItemTemplate>
            <div class="block odd">
                <Jungle:NewsStory runat="server" NewsPage="<%# Container.DataItem %>" />
            </div>
        </ItemTemplate>
        <AlternatingItemTemplate>
            <div class="block even">
                <Jungle:NewsStory runat="server" NewsPage="<%# Container.DataItem %>" />
            </div>
            <div class="fix">
            </div>
        </AlternatingItemTemplate>
    </asp:Repeater>
</asp:Content>

We now need to data bind our Repeater. Go the code-behind file (default.aspx.cs), derive from TemplatePage, remove everything inside the class.

using Jungle.Templates.Jungle.PageTypes;
using PageTypeBuilder.UI;
 
namespace Jungle
{
    public partial class _default : TemplatePage<StartPage>
    {
    }
}

Lets retrieve the children of NewsContainerPage, Filter for visitors, Sort them, and only add as many as specified by the editor.

For more on EPiServer filtering see: EPiServer filter – part 1 and EPiServer filter – part 2: create your own filter.

protected override void OnLoad(System.EventArgs e)
{
    base.OnLoad(e);
 
    if (!PageReference.IsNullOrEmpty(CurrentPage.NewsContainerPage))
    {
	var news = CurrentPage.NewsContainerPage.GetChildren();
 
	if (news == null)
	{
	    return;
	}
 
	FilterForVisitor.Filter(news);
 
	new FilterPropertySort("PageStartPublished", FilterSortDirection.Descending).Filter(news);
 
	if (CurrentPage.NewsCount != 0)
	{
	    new FilterCount(CurrentPage.NewsCount).Filter(news);
	}
 
	this.rptNewsList.DataSource = news;
	this.rptNewsList.DataBind();
    }
}

Featured news story

Great, we can now start on the featured news story!

Lets copy the featured markup from index.html and paste it into FeaturedContentRegion in default.aspx:

<asp:Content runat="server" ContentPlaceHolderID="FeaturedContentRegion">
    <div id="featured">
        <div id="featured-block" class="clear">
            <div id="featured-ribbon">
            </div>
            <a name="TemplateInfo"></a>
            <div class="image-block">
                <a href="index.html" title="">
                    <img src="images/img-featured.jpg" alt="featured" width="350px" height="250px" /></a>
            </div>
            <div class="text-block">
                <h2>
                    <a href="index.html">Read me first</a></h2>
                <p class="post-info">
                    Posted by <a href="index.html">erwin</a> | Filed under <a href="index.html">templates</a>,
                    <a href="index.html">internet</a></p>
                <p>
                    <strong>JungleLand 1.0</strong> is a free, W3C-compliant, CSS-based website template
                    by <a href="http://www.styleshout.com/">styleshout.com</a>. This work is distributed
                    under the <a rel="license" href="http://creativecommons.org/licenses/by/2.5/">Creative
                        Commons Attribution 2.5 License</a>, which means that you are free to use and
                    modify it for any purpose. All I ask is that you include a link back to <a href="http://www.styleshout.com/">
                        my website</a> in your credits. For more free designs, you can visit <a href="http://www.styleshout.com/">
                            my website</a> to see my other works.
                </p>
                <p>
                    Good luck and I hope you find my free templates useful!</p>
                <p>
                    <a href="index.html" class="more-link">Read More</a></p>
            </div>
        </div>
    </div>
</asp:Content>

In the code-behind we need to add a property for the featured news page.

using EPiCode.Extensions;
using EPiServer.Core;
using EPiServer.Filters;
using Jungle.Templates.Jungle.PageTypes;
using PageTypeBuilder.UI;
 
namespace Jungle
{
    public partial class _default : TemplatePage<StartPage>
    {
        private PageData _featuredNewsPage;
        protected PageData FeaturedNewsPage
        {
            get
            {
                if (_featuredNewsPage == null)
                {
                    if (!PageReference.IsNullOrEmpty(CurrentPage.FeaturedNews))
                    {
                        _featuredNewsPage = CurrentPage.FeaturedNews.GetPage();
                    }
                }
 
                return _featuredNewsPage;
            }
        }
 
        protected override void OnLoad(System.EventArgs e)
        {
            base.OnLoad(e);
 
            this.NewsStories();
        }
 
        private void NewsStories()
        {
            if (!PageReference.IsNullOrEmpty(CurrentPage.NewsContainerPage))
            {
                var news = CurrentPage.NewsContainerPage.GetChildren();
 
                if (news == null)
                {
                    return;
                }
 
                FilterForVisitor.Filter(news);
 
                new FilterPropertySort("PageStartPublished", FilterSortDirection.Descending).Filter(news);
 
                if (CurrentPage.NewsCount != 0)
                {
                    new FilterCount(CurrentPage.NewsCount);
                }
 
                this.rptNewsList.DataSource = news;
                this.rptNewsList.DataBind();
            }
        }
    }
}

As you can see I’ve refactored the code a little, which makes it easier to read and manage.

In default.aspx we can now replace the dummy text.

<asp:Content runat="server" ContentPlaceHolderID="FeaturedContentRegion">
    <div id="featured">
        <div id="featured-block" class="clear">
            <div id="featured-ribbon">
            </div>
            <a name="TemplateInfo"></a>
            <div class="image-block">
                <a href="<%= FeaturedNewsPage.LinkURL %>">
                    <%= FeaturedNewsPage.HtmlImage("FeaturedNewsImage", "PageName")%>
                </a>
            </div>
            <div class="text-block">
                <h2><%= FeaturedNewsPage.HtmlLink() %></h2>
                <p class="post-info">
                    Posted by <a href="<%= FeaturedNewsPage.LinkURL %>"><%= FeaturedNewsPage.Author() %></a></p>
                <p><%= FeaturedNewsPage.PreviewText(300) %></p>
                <p>
                    <a href="<%= FeaturedNewsPage.LinkURL %>" class="more-link">Read More</a></p>
            </div>
        </div>
    </div>
</asp:Content>

The Author extension method looks like this.

public static string Author(this PageData page)
{
	if (page.PageLink == null || page.PageLink.ID < 1)
	{
		return string.Empty;
	}
 
	return page.IsValue("MetaAuthor") ? page["MetaAuthor"].ToWebString() : string.Empty;
}

Again, I’m not a fan of this code.

<a href="<%= FeaturedNewsPage.LinkURL %>"><%= FeaturedNewsPage.Author() %></a>

Lets again use the HtmlLink extension method instead.

<%= FeaturedNewsPage.HtmlLink(p => p.Author()) %>

Less to type, and more readable.

Thats it! The next post: Part 4: Creating the standard page.

Related Posts: