1
Nov
2006
I meant to post about this, oh, three months ago? I guess things got away from me.
You know those lists that Community Server offers for blogs? The ones I've got on the right side of my skin for blogs that I read and sites that I find myself at more than I should? Were you aware that those aren't sorted? This can be a bit misleading because they're sorted in your Control Panel, but on the page itself, they appear in whatever order they were originally entered, just like that image to the right.
Thankfully, we can address this with a very small amount a C#. The skin file that contains the UI for CS's lists is Skin-LinkList.ascx. The general structure of this file is a pair of nested Repeater tags. The outer Repeater iterates over your link categories (ID="Categories"), while the inner Repeater iterates over the links themselves (ID="Links"). Using a couple of strategically-placed ASP.NET event handlers, we can sort the contents of one or both of these Repeaters before they begin iterating. I only sort the inner Repeater in my skin, so that's the example I'll give.
The first thing you need to do is to hook the DataBinding event on the repeater. Locate the Repeater with the ID "Links" and add a reference to your event handler:
<asp:Repeater ID="Links" OnDataBinding="Links_DataBinding" runat="server">
Now you just need to add the Links_DataBinding method itself:
<script runat="server">
protected void Links_DataBinding( object sender, EventArgs e )
{ Repeater links = sender as Repeater;
if ( links == null )
return;
// The list of links is provided as an ArrayList. Note that is is extremely
// possible that this will change in future revisions of CS. This becomes a
// possible breaking point for this skin.
ArrayList items = links.DataSource as ArrayList;
if ( items == null )
return;
// Sort the items using their default comparer. They're all strings, so
// this is a straightforward sort.
items.Sort();
}
</script>
This is really, really simple. The data source being passed to the Repeater is an ArrayList of strings, which are sorted easily enough. We get access to the data source (using the necessary safeguards to ensure that we don't blow anything up in the name of something so trivially cosmetic) and simply call ArrayList.Sort(). That's it!
Interestingly enough, if you look in the CS database schema, you'll notice that the table that contains links (cs_Links) also has a column called "SortOrder". I did some digging around in the source code, and I don't believe that this field is currently referenced anywhere in the application. I assume that it could be used as the basis for a control panel modification that would allow you to reorder your links on a whim, should anyone feel like going that far.
21
Aug
2006
Seeing Jaxon's post about fixing the search on his blog prompted me to do a search on my own blog and see what happened. I was happy to discover that it works, but the EntryDate control that I had put together was not producing any dates. This confused me.
The search results page for your blog is generated by Skin-IndividualSearchContainer.ascx. This page contains an instance of the <Blog:EntryList> control. This is where my confusion arose from, as I was certain that I had covered the EntryList case, as that is what the front page of my blog uses (verily, the front page of the site still displays dates correctly, as expected).
After rooting around a bit, I discovered the problem. The EntryList control, at its heart, relies on a Repeater to display its content. When the EntryList occurs inside of EntryListContainer, it is populated with instances of WeblogPost. However, when it occurs in IndividualSearchContainer, it is populated with instances of IndexPost. Both of these contain the post date, but I was only checking for the presence of the former. By adding support for the latter, everything is once again sunshine and rainbows.
An updated version of the assembly can be found here, complete with source code.
14
Aug
2006
[Update] Due to something else I'm working on, I've shortened the name of the control to just "EntryDate".
Something that I'd been meaning to do was to take that code I came up with for parsing out the Community Server post dates and distill it down to a web custom control. Well, I've finally gotten around to it.
The control is called EntryDate, and inherits from the CS WeblogThemedControl. This means that it uses the same skinning motif as the rest of the built-in CS blog controls, so you drop a reference to EntryDate where you want it and fill out the UI details on Skin-EntryDate.ascx. Here is an example pulled from my own blog skin:
Skin-EntryView.ascx
<%@ Control Language="C#" %>
<%@ Import Namespace="CommunityServer.Components" %>
<%@ Register TagPrefix="CS" Namespace="CommunityServer.Controls" Assembly="CommunityServer.Controls" %>
<%@ Register TagPrefix="Blog" Namespace="CommunityServer.Blogs.Controls" Assembly="CommunityServer.Blogs" %>
<%@ Register TagPrefix="nf" Namespace="NinjaFish.CommunityServer.Controls" Assembly="NinjaFish.CommunityServer" %>
<div class="entry-view">
<nf:EntryDate runat="server" />
<h1 class="entry-title"><asp:Literal ID="EntryTitle" runat="server" /></h1>
<asp:Literal ID="EntryBody" runat="server" />
<div class="entry-footer">
<div class="entry-published-date">
<CS:ResourceControl ResourceName="Feedback_FilterPublished" runat="server" /> <asp:Literal ID="EntryDesc" Text="Perma Link" runat="server" />
</div>
<asp:HyperLink ID="EditLink" Visible="False" runat="server" /> <Blog:CategoryTagControl ID="Tags" runat="server" />
<Blog:DownloadAttachmentLink ID="Attachment" runat="server" />
</div>
</div>
The tag that does the fun bit is <nf:TemplatedEntryDate runat="server" />, while the contents of the skin file can be found below:
Skin-EntryDate.ascx
<%@ Control Language="C#" %>
<span class="entry-date">
<asp:Label ID="EntryDay" CssClass="entry-day" runat="server" />
<asp:Label ID="EntryMonth" CssClass="entry-month" runat="server" />
<asp:Label ID="EntryYear" CssClass="entry-year" runat="server" />
</span>
That's it! The look and feel of the date is up to you; you don't even have to use my markup, and the control supports attributes to render different date formats for each part of the date. More details in the readme file.
Also, because I was too lazy to strip it out and I guess it works as intended, the <CS:Head> replacement I spoke about in this post is included, as well.
You can download the assembly and source here.
2
Aug
2006
While I don't think there's anything terribly fancy about the particular skin I've put together, there is one part of it that required some research, and I've had a couple of questions about it already: the post dates. I've seen this particular style of date (the big one, to the left of this paragraph) on a few other blogs, and wanted it for myself. The problem is that Community Server doesn't give you a particularly handy way to get at this information, so you have to do a bit of extra work to get it.
If you open up Skin-EntryView.ascx in the default skin, you will see the following:
<asp:Literal id="EntryDesc" Text="Perma Link" Runat="server" />
This is the control into which CS embeds the post date, and it is the easiest place from which to extract that information. For this skin, I’ve created the following markup to hold the date information:
<span class="entry-date">
<asp:Label ID="entryDay" CssClass="entry-day" runat="server" />
<asp:Label ID="entryMonth" CssClass="entry-month" runat="server" />
<asp:Label ID="entryYear" CssClass="entry-year" runat="server" />
</span>
The Label entryDay will hold the day, entryMonth the month, and entryYear the year. To populate these controls, I handle the PreRender event on the page. In this method, I parse the date into its constituent parts and use that information to fill the Labels. Once done, you can hide the EntryDesc Literal if you no longer need it.
<script runat="server">
protected void Page_PreRender( object sender, EventArgs e )
{
DateTime entryDate = DateTime.Parse( EntryDesc.Text );
entryDay.Text = entryDate.Day.ToString();
entryMonth.Text = entryDate.ToString( "MMM" );
entryYear.Text = entryDate.Year.ToString();
}
</script>
Note: To be safe, you’d probably want to wrap the DateTime.Parse() in a try-catch block, or use DateTime.TryParse() if you are on ASP.NET 2.0. I was on ASP.NET 1.1 when I wrote this code and I have a fair amount of faith that CS will actually provide me with a valid date.
This works quite well for Skin-EntryView.ascx. Unfortunately, It’s not quite as simple for Skin-EntryList.ascx. Because you’re handling multiple date controls now, sorting out all of the dates in PreRender isn’t really a viable option. In this case, the simplest and most straightforward approach is to handle the ItemDataBound event of the Repeater control that generates the entries. We use the same markup as before, but this C# code instead:
<script runat="server">
protected void entryItems_MyItemDataBound( object sender, RepeaterItemEventArgs e )
{
WeblogPost entry = e.Item.DataItem as WeblogPost;
if ( entry != null )
{
Label entryDay = e.Item.FindControl( "entryDay" ) as Label;
if ( entryDay != null )
entryDay.Text = entry.UserTime.Day.ToString();
Label entryMonth = e.Item.FindControl( "entryMonth" ) as Label;
if ( entryMonth != null )
entryMonth.Text = entry.UserTime.ToString( "MMM" );
Label entryYear = e.Item.FindControl( "entryYear" ) as Label;
if ( entryYear != null )
entryYear.Text = entry.UserTime.Year.ToString();
}
}
</script>
The line to note here is WeblogPost entry = e.Item.DataItem as WeblogPost. This gets you the WeblogPost instance that corresponds to the entry currently being rendered. WeblogPost represents a single blog post and contains all of the relevant information that belongs to it. You can learn more about this class by perusing the CS SDK. The WeblogPost’s UserTime property contains the date and time at which the entry was posted, and we parse this DateTime in the same way that we did on Skin-EntryView.ascx. Also note that, because we are assigning values to controls inside of a Repeater, we must use the FindControl() method to get references to them instead of referencing them directly by ID.
More Posts