13
Jan
2007
Without the miracle of trackbacks, I wouldn't know that I can still get my Community Server news fix at http://csnews.csmvps.com/blogs/dn/ now that the old news site has apparently stopped updating. Thank you, trackbacks!
Of course, I'm sure that someone mentioned this at some point. I should probably be more observant.
11
Jan
2007
A while back, I was provided with a solution to the problem of allowing HTML in my blog comments by Jayson Knight. This involved modifying the SafeFeedback method in the Community Server source code and rebuilding the module. While that works, I chose to implement it as a complete CS module of its own. Why do this? Because the fewer changes that I make to the CS source code, the easier things are to deal with when I have to upgrade. As long as the changes to the stock FeedbackHtmlFormatting module are minimal (or nonexistant, as they were with 2.1 SP1 and SP2), I can upgrade the product without having to reimplement my modifications.
So now, after much procrastination, I've packaged up a new version of NinjaFish.CommunityServer. The only change here is the addition of a new CSModule, FeedbackHtmlFormatting. This module shares a name with one of the stock CS modules because it is intended as a replacement.
The version of FeedbackHtmlFormatting that is included in this assembly provides the exact same functionality as the original version, but will allow limited HTML (the same HTML as in your forum post configuration) to pass through into the comment body. It provides the addition restriction that image tags are not allowed (they are converted into plain text links) and repeated links are not converted into HTML links.
I tried to make the link detection reasonably robust in order to keep the comments from becoming obnoxious, but I'll be the first to admit that I probably didn't think of everything. As always, the source code is included, so feel free to customize the SafeFeedback method for your own needs.
And, of course, the module is running on this blog, so feel free to play with it.
27
Dec
2006
I figure that I have no excuse for not updating this thing when I've been on vacation for almost two weeks. Something to keep in mind if you run Community Server and you've ignored your blog for a while: when you go to your blog dashboard and click on the number next to "Awaiting Approval" for comments, you are presented with the list of comments published on your blog, and not the list of comments awaiting approval. Make sure you are aware of this before you start nuking comments. Otherwise, you will look like me: stupid. Thankfully, I think I only killed one legit comment. Apologies to whomever just got bumped.
I have many things to catch up on. Apparently there is a new service pack for CS, which I will attempt to deploy over remote desktop (since I am not home from vacation yet). I've also got an update for NinjaFish.CommunityServer that I've been meaning to publish for, oh, a month, which includes a new CS module that (hopefully safely) allows HTML in blog comments.
[Update] Fifteen minutes later and we have another flawless service pack application. Yay for easy patches!
29
Nov
2006
Once again, this is what happens when I stop paying attention (to be fair, though, I was on vacation this time). As mentioned in various posts linked off of the Community Server Daily News, Snap (whom I had admittedly not heard of until today) has come up with a damn cool little tool called Snap Preview Anywhere that creates popup Javascript/HTML previews of the links on your site. Want to see it in action? Just roll over any link on this page! This is really handy for people like me who like to litter their blog posts with lots of pointless links.
While that's fancy enough on its own, what impressed me the most was how easy it is to implement on your site. You go through the sign up process and you add one Javascript reference to your site. That's it. Very cool stuff.
Well, it's almost that easy. Snap Preview Anywhere creates previews for every link on your page. This has the rather annoying side effect of previewing links back into your own site. Snap does provide a simple Javascript solution for disabling the previews on internal links, however:
<script type="text/javascript">
//<![CDATA[
//change sites internal links to class "snap_nopreview"
var links = document.getElementsByTagName('a'); for (var l = 0; l < links.length; l++)
{ if(links[l].href.match(/^http:\/\/blog\.ninjafish\.net/))
{ links[l].className += " snap_nopreview";
}
}
//]]>
</script>
Add this script to the bottom of your page, before the </body> tag (it has to run after the page has rendered). Obviously, you'll want to change the search string to match your site instead of mine. There's also no reason why you can't use this to filter out domains other than your own.
[Update] If you use Scott Watermasysk's Share It module, you'll probably want to update that script to filter out the links that it creates. Here is the regex I am using to do just that:
/^http:\/\/(blog\.ninjafish\.net)|(del\.icio\.us\/post)|(www\.digg\.com\/submit)|(reddit\.com\/submit)/
10
Nov
2006
So Telligent released something called BlogMailr this week -- a web service that allows you to post to your blog via email. This post is more or less content-free; I just wanted to test the silly thing and see what it does. I'm using my web mail client at ASPnix to enter the post, which fully supports HTML and all that good stuff.
OK, I guess I do have some content...
BlogMailr supports attachments via the MetaWeblog API. As you're probably aware, Community Server doesn't. Well, it didn't: it turns out that Telligent has also back-ported some of the API changes from CS 3.0 to CS 2.1. The update is available here, and is a simple, one-file change. The greatest improvement is support for newMediaObject, which allows BlogMailr (and Windows Live Writer) to attach files to your posts.
And now I press the "send" button and pray that this doesn't implode.
[Update] BlogMailr is a lying liar. I just got an email confirmation claiming that this post doesn't actually exist, so stop reading it before you permanently damage the fabric of reality.
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.
31
Oct
2006
Wow, talk about oblivious. I've been so heads-down on other stuff that I completely missed the Community Server service pack that was released on Monday. I only noticed that because I was headed over to the CS forums to find out how to enable HTML in comments, which doesn't seem to be working/enabled (still researching). As for the SP, I've already got it installed (the upgrade was absolutely painless and thankfully did not require a complete upload of CS), and it seems to not be exploderating in my face. Kudos to Telligent on the easy update!
[Update] I still haven't found any evidence of a feature to enable HTML in blog comments(the Magic 8-Ball says "not likely"), but I did run across Ken Robertson's ClearCommentCache module, which is quite handy. Ken sure has done a lot to enhance my comment functionality.
Good lord, when is the last time I looked at this stuff?
25
Sep
2006
This is my first blog, and I haven't had it for very long. I was surprised to find that it only took about a month for the comment spammers to show up (this was after I removed the CAPTCHA control from the comment form, which I deemed unnecessary due to the low amount of traffic).
I didn't want to bring back the CAPTCHA block, and I certainly wasn't about to disable anonymous comments (I hate having to sign up to a site just to leave a few words of feedback), so I decided to give the Community Server spam rules a shot instead. In my case, I was getting (past tense, as this particular spammer seems to have given up) persistent comment spam from one person. All of his comments had a similar theme and used a small selection of user names. Based on this information, I made some changes to the rules:
- All of the user names used by this spammer immediately went on the forbidden words list. This was a safe move, as this spammers user names were all short phrases and very unlikely to clash with a real user name. Forbidden words get five points each.
- I added a couple of choice words that were repeated many, many times in his comments to the bad words list. Bad words get two points each, and are allowed only once before they start to count towards the spam score.
- While this user did not post any links, I decided to take the advice posted on several other blogs and enable the link count rule. I allow three links, with five points added for each link beyond three.
- Similarly, I enabled the IP count rule, allowing up to three comments to be posted from the same IP within sixty seconds. Every post beyond three earns five points. I'm still wondering if this rule might be too loose, as I can't imagine anyone posting three legitimate comments in a minute, but I see no need to change it for now.
- Since the bad word count and link count rules are fairly loose now, I set the score to mark a comment for moderation at five and increased the score to mark a comment as spam to fifteen.
- Finally, I set comments to be disabled after a post had been up for thirty days.
And then I just sat back and watched. It is now about three months later, and the spam blocker has caught pretty much everything that's been thrown at it. More importantly (to me, anyway), it hasn't incorrectly flagged any legitimate comments as spam. I am very, very impressed. I've even loosened the post date restriction to ninety days, because I really don't care how much spam shows up, so long as it doesn't make it to the actual page.
I've also since installed Ken Robertson's AllCommentRss CSModule, which creates an RSS feed of all comments posted to your blog, and apparently filters out spam comments as of last week. I no longer have CS email me when I get comments; instead, I use the RSS feed to watch them while CS sends an email when I have comments that need moderation (spam).
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.
13
Aug
2006
I caught wind of the Windows Live Writer beta from Major Nelson's blog. He wasn't kidding when he said that BlogJet users should check it out — it feels almost like a BlogJet clone. There are still some things that BlogJet does better, though. For example, I like how BlogJet automatically substitutes certain HTML entites, such as — and curly quotes, as you type, as well as its text substitution features (similar to Community Server's Text Parts, but built into the editor).
CS support in Live Writer is decent enough. It goes through the MetaWeblog API, much like every other WYSIWYG editor on the market. The application did make an attempt to download my style sheets from the web site, which would have been really cool if it had actually worked. Perhaps they could add a feature to allow you to add these manually. Live Writer also makes any pings after posting via the editor itself. BlogJet appears to go through its posting API, which CS does not support, so I have to ping manually.
Windows Live Writer appears to be shaping up really nicely. If they can catch up with BlogJet's feature set and add a few more perks, I might just make the switch.
This post was made (and updated) with Live Writer, of course.
[Update] After seeing Dave Burke's post on Live Writer and that his blog is apparently much less uppity than mine and downloaded his styles without a problem, I decided to give it another go. Worked wonderfully the second time, but unfortunately it appears to have some issues pulling images out of the CSS file. See for yourself:
I'm impressed with how well the preview works in terms of showing you how your layout will work in its final form. However, the lack of CSS image support (which may stem from the fact that my styles use relative paths) makes it too annoying to work in this mode all the time. Still very cool stuff. I look forward to seeing what else they have in store for us.
11
Aug
2006
The final release of Community Server 2.1 is upon us! I’ve already gone through the motions and upgraded the site. I skipped the RC1 release because…well…I’ve only had this thing up for a couple of months and I’ve already upgraded it four times.
CSS for the Custom Post Dates
Vern at Subtext asked if I could go over the CSS for the custom post dates I use for the site. First off, here’s the markup that ends up on the page after ASP.NET is done rendering it:
<span class="entry-date">
<span class="entry-day">19</span>
<span class="entry-month">Sept</span>
<span class="entry-year">2006</span>
</span>
Nothing terribly complex here. I only need three spans because I’m applying different styles to each part of the date. For the CSS, the vast majority of the formatting is handled by the styles for the container span:
.entry-date
{
float: left;
margin: 3px 8px 0 0;
padding: 4px 2px 0 0;
width: 50px;
height: 58px;
background: url( ../Images/EntryDateBG.gif ) no-repeat top left;
text-align: center;
}
Floating the span also has the benefit of turning it into a block-level element, so we can apply a height to the container. The background image actually reveals most of the trickery:

Since all of the flair is handled by a single image, the only thing that remains is to get the text into the right positions. This will, of course, require some hand tweaking, and the actual numbers will depend on the background image being used, font sizes, the phase of the moon, etc. Here’s the CSS for my dates:
.entry-date .entry-day
{
display: block;
font-size: 1.2em;
font-weight: bold;
color: #0f42a4;
}
.entry-date .entry-month
{
display: block;
font-size: 1.0em;
font-weight: bold;
font-variant: small-caps;
line-height: 1.2em;
color: #0f42a4;
}
.entry-date .entry-year
{
display: block;
margin-top: 4px;
font-size: 1.0em;
font-weight: bold;
color: #fff;
}
I convert each span to a block-level element so that each section of the date gets its own line in the box. For spacing, I mostly fiddle with the line-height on the month, as it’s in the middle. Everything else in there is pretty basic.
3
Aug
2006
One of my favorite things about Community Server is the SDK, which is really nothing more than the CS source code. I guess they call it an “SDK” because it really does help with developing new CS stuff. If you’ve been developing with ASP.NET for a while and you take a look at the CS 2.0 SDK, you can almost immediately tell that they knew what was coming with ASP.NET 2.0 and implemented ASP.NET 1.1 versions of much of that functionality. It’s almost forward-thinking, but I suspect they were cheating. 
So I was bored one day and was trying to shoehorn Atlas into Community Server, much like Reese’s shoehorns peanut butter in their chocolate, when I ran into a rather silly issue. In order to enable partial rendering on the Atlas ScriptManager (and let’s face it: what fun is Atlas without partial rendering?), you need a head tag with runat="server" on it. Easy enough: I’ll just add runat="server" to…wait a sec! What’s this <CS:Head> thing?!
It turns out that <CS:Head> is like an ASP.NET 1.1 version of <head runat="server">. It allows you to programmatically access the contents of the <head> tag at any point during page rendering, just like you can do in ASP.NET 2.0. The problem is that it renders the <head> markup, so if I just wrap it in <head runat="server">…well, I’d have two <head> tags, and we can’t have that, can we?
The solution I came up with was to replace <CS:Head> itself. I still need its functionality so that CS can provide me with the correct scripts and stylesheets. I just don’t need its errant <head> tag.
using System.IO;
using System.Text;
using System.Web.UI;
using CSControls = CommunityServer.Controls;
namespace NinjaFish.CommunityServer.Controls
{
/// <summary>
/// Renders the contents of the Community Server Head tag without rendering the
/// HTML head tags themselves.
/// </summary>
public class HeadContent : CSControls.Head
{
protected override void Render( HtmlTextWriter writer )
{
// Intercept the output of the base class and store it in a StringBuilder.
StringBuilder baseOutput = new StringBuilder();
HtmlTextWriter intercept = new HtmlTextWriter( new StringWriter( baseOutput ) );
base.Render( intercept );
// Remove the <head> tag from the output.
baseOutput.Replace( "<head>", string.Empty );
baseOutput.Replace( "</head>", string.Empty );
// Dump the new output out of the control.
writer.WriteLine( baseOutput.ToString() );
}
}
}
To use this, I do something like the following:
<head runat="server">
<nf:HeadContent runat="server">
<CS:AllTheCSHeadGuts runat="server" />
</nf:HeadContent>
</head>
The output is exactly the same as it was before I started messing around, but now the ScriptManager no longer complains about its missing head.
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.
28
Jul
2006
At this rate, I'm going to be upgrading this thing every day!
Telligent has posted beta 2 of Community Server 2.1. I was expecting a bug fix release, but there appears to be a few new features as well. Most of them are for tag support in forums, which I'm not using here (and haven't really played with). The four new blog skins also make their debut with this release, courtesy of Jaxon Rice.
Seeing as this wasn't a major upgrade, I went ahead and performed the update today. Based on the diffs I ran using Beyond Compare (which is a fantastic, easy to use diff utility), it would appear that most of the changes are in the UI and the bin directory, and aren't going to cause any headaches.
On the other hand, I've already seen some people having problems out on the CS forums due to the changes in Web.config. In particular, two of the entries (and their corresponding assemblies) have been removed from the <httpModules> section of the Web.config file: CSProfile and CSRoleManager. If you try to upgrade while retaining your old Web.config file and these entries are not removed, the application will exploderate in your face, and that is a bad thing. Some assembly references were also removed from communityserver.config, but I couldn't say whether or not these will break the application if they are present.
Based on my admittedly very limited experience with upgrading CS, I can tell you that performing an upgrade without replacing the three main configuration files (Web.config, communityserver.config, and SiteUrls.config) is going to be potentially tricky. I find that it is much easier to keep track of the changes you've made and make those same changes to the new configuration files.
If, on the other hand, you've made so many changes that your config files are now horrible mutant chimeric derivations of their former selves (as my Web.config now is), then it may be easier to run a diff between the two versions and make those changes manually. When doing this, you should use the original, unmodified configuration files that came with the installation package. This will provide a much more meaningful set of changes than trying to compare your modified config files to the installer's version.
Beta 2 also comes with a database schema update (even if you're running beta 1). Keep that in mind if database changes give you the heebie-jeebies.
More Posts
Next page »