August 2006 Blog Posts

Subtext Skins Gallery at SubtextSkins.com

As a follow-up to the recent release of Subtext 1.9, I would also like to introduce a little something I put together at http://subtextskins.com/.

Since we have yet to incorporate a skin preview or gallery in Subtext, I setup a site that features each of the default skins.  Additionaly, the site also has a Skin Showcase where users can submit skins to share with the community.  User submissions are moderated.

Subtext Skins Gallery

Although we plan to overhaul skinning in the next version, it is still worthwhile to share skins here as my hope is to help port any user submitted skin (unless it gets too ridiculous in there).

I started off the sharing with the Marvin 3 Column skin which used to be included with Subtext.  If you have a skin you wish to share, please do share!  I recommend that your zip file include a Skins.User.config file rather than a Skins.config file.

What About CAPTCHA?

I mentioned several heuristic approachs to blocking spam in my recent post on blocking comment spam, but commenters note that I failed to mention CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart).  At the moment, CAPTCHA is quite effective, both at blocking spam and annoying users.

But I don’t have any real beef with CAPTCHA, apart from the accessibility issues.  If I met CAPTCHA in a bar, I’d buy it a beer! No hard feelings despite the trash talking in the past, right?

There is successful code out there that can break CAPTCHA, but that is pretty much true of every other method of blocking spam I’ve mentioned.

The reason I didn’t mention CAPTCHA is that it would be ineffective for me.  Much of my spam comes in via automated means such as a trackback/pingback .  The whole point of a trackback is to allow another computer to post a comment on my site.  So telling a computer apart from a human in that situation is pointless.

And at the moment, the Comment API has no support for CAPTCHA.  If comment spam isn’t coming in via the comment api now, it is only a matter of time before it is the primary source of comments.

So while I believe CAPTCHA is effective and may well be for a good while until comment spammers catch up, I would like to look one step ahead and focus on heuristics that can salvage the use of trackbacks and the Comment API. 

Important Note On Upgrading to Subtext 1.9

Let me start off by noting that Subtext 1.9 requires ASP.NET 2.0!  Thus the upgrade process from a prior version of Subtext (all which run on ASP.NET 1.1) will not be quite as simple as before, but should hopefully not be overly complicated as is the spirit of Subtext.

For Users Who Have Control Of Their Hosting Server

Users who host on their own server, or have Remote Desktop access to their hosting server will have an easier time with the upgrade.  My recommendation is to simply setup a new folder with the new version of Subtext, copy in your modifications, and then switch IIS over to ASP.NET 2.0 to point to the new directory.  The following is a step-by-step detailed procedure.

  1. Backup your Database. This should go without saying.
  2. While you are in Enterprise Manager, make sure the database user that your blog uses to access the database has DBO permissions temporarily.  This is required so that the web-based upgrade procedure will work.
  3. Make sure you can login to the HostAdmin section.  On most blogs this would be the /HostAdmin/ directory of the site. For example, on my blog the HostAdmin is located at http://haacked.com/HostAdmin/.  If you forget your HostAdmin password, there is a query you can run in Query Analyzer to reset your password at the bottom of this page.
  4. Download and unzip the Subtext 1.9 binaries into a new directory parallel to your current installation.  For example, on my server I host my blog in the d:\Websites\Haacked.com\ directory.  When upgrading to Subtext 1.9, I unzipped the distribution to the following path d:\Websites\Haacked.com.1.9\.
  5. Merge any customizations from your old web.config file into the new web.config file.  Be sure to note that some settings have moved. For example, the connection string has been moved into the <ConnectionStrings> section.  Also take a look at any new settings you may be interested in.
  6. Copy all your images, videos, audio files and any other non-Subtext files and customizations into the appropriate place in the new directory. For example, I copied the images folder as well as my own Demos folder which contained some demo code on my site into the Haacked.com.1.9 folder.
  7. Now in IIS Manager, configure your existing site to use ASP.NET 2.0 and point it to your new directory.  For details, see the section at the bottom of this post.  You may need to change the Application Pool your site runs in if you are running Windows 2003.
  8. Visit your website and follow the instructions.  At this point, the normal web-based upgrade wizard should kick in, asking you to login to the HostAdmin tool and click the upgrade button.  This will upgrade your database schema and stored procedures.
  9. Make sure to reverse the change you made in step 2!  Subtext does not require DBO permissions for normal operations. The user that Subtext uses to connect to your database should just be in the public group.

For Users With Hosted Solutions Such as WebHost4Life

Unfortunately I am not familiar with the procedure that the various hosting providers use to upgrade a site from ASP.NET 1.1 to ASP.NET 2.0.  If the upgrade happens on the same machine that your site is currently hosted in, the upgrade may bring down your site for a short bit.  You may have to coordinate the above steps with a technician at your hosting company, except for the following changes.

  • Step 4: Download and unzip the Subtext 1.9 binaries to your local machine.
  • Step 6: Have your hosting support technician upgrade your site to ASP.NET 2.0
  • Step 7:  Copy your local files over to your hosting provider.

Configuring IIS for ASP.NET 2.0

To configure a website in IIS for ASP.NET 2.0, right click on the website in the IIS Manager tool and select properties. Click on the ASP.NET tab in the dialog box. It should look something like this...

ASP.NET Version Dialog

Makes sure to select 2.0.50727 in the ASP.NET version dropdown.

On Windows 2003, I created a separate Application Pool for my ASP.NET 2.0 websites.  To select the Application Pool for a website, cilck on the Virtual Directory tab and select the Application Pool in the dropdown at the bottom of the dialog as in the following screenshot.

App Pool Dialog

Good luck and smooth sailing!

Subtext 1.9 "Daedelus" Released!

Subtext Logo Well my friends, it took a bit longer than expected to get Subtext 1.9 out the door, but we did it.  When we released Subtext 1.5 back in June I said,

The next version of Subtext is code named Daedelus. It will simply be a straight upgrade to ASP.NET 2.0. We hope for a quick turnaround as we don’t plan to add a lot of features in this iteration. We just want to get up and running in ASP.NET 2.0.

Well that was then and this is now and I was wrong about the quick turnaround.

We realized that a straight port to ASP.NET 2.0 wasn’t much fun if we couldn’t take advantage of some of the new goodies that ASP.NET 2.0 has.  So we spent a significant amount of time cleaning up code and refactoring some functionality to take advantage of what ASP.NET 2.0 offers.  A lot of the changes are under the hood, but there are still a few surface level treats for everybody.

Before you upgrade to 1.9, please check out my notes on upgrading.

So what is new in Subtext 1.9 besides that it is now an ASP.NET 2.0 application?

Under The Hood

Let’s not kid ourselves. 99.999% (Five nines baby!) of my readers are geeks and we want to pop open the hood and take a look around.

  1. Subtext Providers have been refactored to use the Microsoft base Provider class, System.Configuration.Provider.ProviderBase.
  2. Used Generics where appropriate.  As you know, there is a lot of temptation when given a new hammer to start looking at everything as a nail.  We tried to avoid that temptation and make judicious use of Generics.  I think we did a bang up job.  Most of our collection classes are now generic collections and there’s that CollectionBook class I wrote about recently.
  3. Improved our Continuous Integration and build process using CruiseControl.NET.  We now have a nice dashboard that provides a lot of visibility into our development progress.
  4. Improved our unit test code coverage to 36.4% and counting. (When I started it was pretty much 0)
  5. Subtext now runs under Medium Trust without problems except for the Trackback/Pingback issue.
  6. Converted the Subtext.Web project into a Web Application Project.
  7. Added a _System folder to the Skins directory. This contains some CSS files that any skin may reference which provide some common CSS layout and styles.  For example, by referencing commonstyle.css, you can use the pullout css class to pullout some text.  Custom skins can reference these files and override specific settings, putting the Cascading back in CSS.
Try out the new “pullout” or “pullout.alt” CSS classes.

New Features

Some new features we added.

  1. Sometimes removing code is as much a feature as adding code.  As I announced earlier, we removed some old skins and added some snazzy new ones.  We also implemented a way for custom skin definitions to not get overwritten when upgrading code.
  2. Improvements to the packaged skins.  We added the Previous/Next control to nearly every skin as well as Gravatar support among other minor improvements.
  3. Comment Moderation!  This high demand feature was fast-tracked when my company was hired to implement it for a client who wished to start a blog. The client agreed to contribute the source back to the project!
  4. Not exactly a new feature, but we changed the default Html Editor to use the FCKEditor.
  5. Implemented RSD (Really Simple Discovery) and the MetaWeblog API’s newMediaObject method so that Subtext works quite well with Windows Live Writer.

Bug Fixes

There are probably too many to list, but I'll point out a few that people may have noticed.

  1. [1524172] The Username is being saved as the title.
  2. [1524371] Non-English comments do not appear correctly in mail message.
  3. [1521317] Installation check code fails in locked down scenarios.
  4. [1519764] Skin selection not retained.

Important Note

Subtext ships with ReverseDOS spam blocker enabled out of the box. Please check the ReverseDOS.config file to make sure that it is not filtering any terms that would be relevant to comments in your blog. You can also disable ReverseDOS by removing any reference to it from Web.config should you so desire.

Plans For The Future

We are now gearing up for Subtext 2.0 “Poseidon”, our next major release, which will feature a plugin framework.  Our hope is to foster a community of plugin contributions.  Other features in the works include a Membership provider which will allow multiple authors for a single blog and a new skinning framework. I will update the Roadmap soon to reflect our current plans for 2.0.

Also, with the recent deluge in comment spam, I am considering having an interim release (1.9.1) that would include Akismet as well as semi-moderation (1.9 does include full moderation now). Ideally we would save these features until we have a plugin framework, as they seem like great candidates for plugins. However the communal benefit of blocking spam may be too great to wait.

Many thanks to the growing numbers of Subtext contributors who helped shape and test this release.  All your efforts, whether it is coding, submitting patches, testing, reporting bugs, requesting features, or just giving us a piece of your mind are appreciated!

And before I forget, as I tend to do, the link to the latest release is here.

MakeMeAdmin And Console MatchMaker

I am still continuing my experiment in running as a LUA (aka Non-Admin).  Let me tell you, it has been a total pain in the ass and now I totally understand why more developers do not do this, which feeds into the vicious cycle in which apps are developed that do not run well under least user privileges.  When I have some time, I will write up my experiences.

One tool that has been invaluable in this experiment is the MakeMeAdmin batch file used to temporarily elevate your privilegs in a command window.  This has worked nicely for me for a while.

Console Screenshot
Then Scott Hanselman points out Console that takes cmd.exe and adds transparency and tabs.  Just pure geek hotness that I gotta have.

However, the only command shell I normally keep open is my MakeMeAdmin shell.  It’d be a shame to install Console and never see its sleek hotness.  So I decided to play matchmaker and see if I could marry these two wonderful utilities.

I modified the MakeMeAdmin.bat file to use Console instead.  It was a one line change (note file paths should be changed to fit your setup and the line break in there is for formatting purposes. There shouldn’t be a line break.).

set _Prog_="console.exe c:\console_admin.xml 
    -t """*** %* As Admin ***""""

I also created a new admin config file named console_admin.xml that specifies transparency and a red tint which lets me know that this console window is not like the others. It will run commands as an admin.

I’ve uploaded my modified MakeMeAdmin.bat file as well as the console configuration file to my company’s tool site here.  Hopefully all five of you out there also running as a non-admin will find this useful.

tags: , ,

Subtext On Mobile Devices

Scott writes about making DasBlog work on Mobile Devices.  The approach he takes is to programmatically detect that the device is a mobile device and then present an optimized TinyHTML (his term) theme.

Ideally though, wouldn’t it be nice to have mobile versions of every theme?  In fact, I thought this could be handled without any code at all via CSS media types.

Unfortunately (or is that fortunately) I don’t own a BlackBerry or any such mobile device with a web browser, so I can’t test this, but in theory, another approach would be to declare a CSS file specifically for mobile devices like so:

<link rel="stylesheet" href="mobile.css" type="text/css" 
    media="handheld" />

The mobile browser should use this CSS to render its screen while a regular browser would ignore this.  Should being the operative word here.  Unfortunately, at least for Scott’s Blackberry, it doesn’t.  He told me he does include a mobile stylesheet declaration and the BlackBerry doesn’t pick it up.  Does anyone know which devices, if any, do support this attribute?

For those devices that do, a skin in subtext can be made mobile ready by specifing the media attribute in the Style element of Skin.config like so (note this feature is available in Subtext 1.5).

<Style href="mobile.css" media="handheld" />

Refer to my recent overview of Subtext skinning to see the media attribute in play for printable views, which does seem to work for IE and Firefox.

Ultimate Tool

Scott Hanselman sets the geek-o-sphere abuzz with his latest (and apparently now annual) Ultimate Developer and Power Users Tool List for Windows.  The publishing of this list usually coincides with a productivity drop for me as I find many new toys to play with.  Unfortunately, many tools don’t work so well when running as a non-admin.

This year I was pleased to find my name and my humble little blog on his list.  Quite pleased in fact until it struck me. 

Wait one doggone minute!

Is Scott calling me a tool?  An ultimate tool no less.  We’ll see about that!

Getting Jiggy With ASP.NET On Rails

Update: Rob renamed his project to Subsonic.

Rob Conery just released ASP.NET ActionPack 1.0.1 on his blog today.  This project is definitely one to watch!  He is essentially taking some of the principles of developing web apps with Ruby on Rails and porting those ideas to ASP.NET.  Just watch this great screencast to get a taste of the progress he has made in a short time.

So far I am very impressed with this guy.  Yesterday evening I sent the link to the screencast to Jon Galloway who then wondered why he was using strings for table names.  I told him to quit bothering me about it and post something in the Codeplex forum.  But Jon, being the simultaneous type of guy he is, had already posted a comment on Rob’s blog before I could finish my sentence.  This all happened last night.  This morning I notice the sixth bullet point in Rob's announcement states that he added struct in classes for column names.  Apparently he had received the comment, made the change, and sent a reply to Jon in two hours.

Now that is a quick turnaround and good customer service! ;)

Not only that, but this guy lives in Kuaui, Hawaii! I don’t know how he gets anything done unless it’s the rainy season right now. Subtext would definitely languish if I lived in Kuaui.

Removing The Comment Spam Incentive

The other tactic I neglected to mention in my previous post on combatting comment spam is more big picture.  How do we remove the incentive for spammers to comment spam in the first place?

Apparently the rel="nofollow" approach has done little to curb comment spammers despite many predictions (including my own).  I still think it is an important step in removing one incentive, but what else can be done to remove this incentive?

With the lack of results from the rel="nofollow" approach, the lesson we learn is that either the incentive for comment spam isn’t necessarily Google rankings or that there are enough unpatched blogs out in the wild that it still does help the google rank to post comments indiscriminately.  Or both.

If a spam commenter can put a link in the comments of several thousand blogs, then certainly that translates to tens to hundreds of thousands of eyeballs on that link, and maybe a few hundred clickthroughs (yes, I’m pulling these numbers out of my rear).  When someone clicks through, the spammer gets paid a small amount from the owner of the site.

Warning, here is where I go off the deep end in brainstorming solutions.  Forgive my naivete.

What if the marketers who pay for these links to be spread around found out that comment spammers were creating negative feelings for their products by posting comments on sites that were vehemently against having these advertisements.  Would they care?  Would they be interested in not paying for click throughs from sites who have specifically opted-out of such links? 

I’m probably dreaming here, but stay with me for a moment as I flesh out a quick thought experiment.  Suppose these sites did care.  One option is for them to not pay for links that originate from sites that specifically opt-out of comment advertising.  For example, by registering with some central opt-out site.

Another approach would be for sites that receive click-throughs to initiate a trackback like mechanism in which they request a comment spam policy from the blog.  If the blog does not explicitely endorse their product, the link does not get paid.

Of course the big flaw in this experiment is that these sites probably do not care and wouldn't go to the trouble to implement these approaches to being a good citizen.  They just want the links to come in.  Even negative publicity is good publicity.  So what can we do? Is there a way to make them care? Is there a way to make comment spam less lucrative?

Comment Spam Heuristics

SpamLately my blog has been hit with a torrential downpour of comment spam.  I’ve been able to fight much of it off with some creative regular

expressions in my ReverseDos configuration file.  Of course keyword filtering, even Bayesian filtering, can only go so far.  We need to supplement these approaches with something else.

But first, in order to combat SPAM, we need to identify the enemy.  Are we fighting against automated bots relentlessly crawling the web and posting comments?  Or are these low paid humans behind the keywords?  Are they attacking via the Comment API or posting to an HTML form?

My assumption has been that these are bots, but I plan to add some diagnostics to my blog to test that assumption someday soon.  Lets run with the assumption that the bulk of comment spam is generated by bots.  In this case, we need to examine the behavioral differences between bots and humans for clues in how to combat spam.

For example, an automated script can pretty much post a spam comment instantaneously.  What if your blog engine timed the interval between sending out the content and receiving a comment? If the comment came back too quickly, then we have high confidence that it is spam.

Certainly this is easily defeated by a spammer by adding a delay, but an artificial delay is costly to an automated script trying to hit the most blogs possible in the shortest amount of time.  Anything to slow down the spammers is worthwhile.

Another potential approach is to require javascript to comment.  Perhaps your comment form doesn’t even exist without some javascript to insert it in there.  The theory behind this approach is that most automated scripts won't evaluate javascript. They simply want to post to some form fields.  Unfortunately this hinders the accessibility of your site for users who turn off javascript, but it may be worth the price.  Spammers will eventually figure this one out too, but it does add a nice computation cost to implement javascript handling in an automated spambot.

Ultimately, these approaches are more about the behavior of the spammer than the content.  For example, when I first started working on Subtext, I added two features that at the time blocked a significant amount of spam for me.  The first was to not allow duplicate comments.  I found that a lot of comment spam simply posted the same thing over and over.

The second feature was to require a delay between comment spam originating from the same IP address.  Using a sliding timeout of only two minutes seemed to defuse spam bombs which would try to post a large number of comments in a short period of time.

Later, I added ReverseDOS to help catch the spam that made it through these approaches.  Over time, I've noticed that comment spam starts to look more and more like legitimate messages, like the current crop of “Nice Site!” spam. 

The one thing that every comment spam has in common is a link.  Ultimately, the only way to stop content spam via a content-based approach is to simply not allow any comment that contains a link in any way shape or form.  But how awful would that be for the many legitimate commenters who wish to share a link?

No, we must do something better. I currently don’t think we’ll ever win the battle, but we can work to stay one step ahead.

The Real Shaolin Soccer

Unbelievable!

Another blog linked to this post and mentioned watching the video up to the slow motion practice session.  Be sure to keep watching past it to see the woman juggling a soccer ball, while playing double dutch, with a flaming soccer ball and jump rope.  Ronaldinho never did that!

Developing Custom Skins

This is my third post about Skinning in Subtext. Previously I talked about some breaking changes.  Then I gave a high level overview of skinning in Subtext.  In this post I want to mention one new feature for those who use custom skins.

Subtext 1.9 actually reduces the the number of pre-packaged skins that come with it out of the box.  That’s right, we got rid of the skins that screamed, "Hey! I was designed by a developer who wears plaid pants with flannel shirts!".  Over time, we hope to add more polished designs.

Of course we don’t want to leave developers with custom developer designed skins out in a lurch.  Taking an informal poll I found that a majority of Subtext users deploy a custom skin typically based on one of the out-of-the-box skins. 

As I described in the overview, skins are configured via a file named Skins.config.  One problem with having all the skin definitions in this file is that any customizations a user might make are overwritten when upgrading to a new version of Subtext.

It is incumbent upon the user to merge new changes in.  We thought we could make this better so we have introduced the new Skins.User.config file.

The format for this file is exactly the same as the format for Skins.config.  The only difference is that we do not include such a file in the Subtext distribution.  Thus you can place your custom skin definitions in this file and it will not get overwritten when upgrading.

From now on, it is recommended that if you customize an existing skin, you should rename the folder and place your skin definition in Skins.User.config.

tags: , , ,

Mile High Overview Of Subtext Skinning

In my previous post, I outlined some minor changes to the skinning model for Subtext. In this post, I will give a high level overview of how skinning works in Subtext.

Subtext renders a Skin by combining a set of CSS stylesheets with a set of .ascx controls located in a specific skin folder.  If you look in the Skins directory for example, you might see a set of folders like this.

Subtext Skin Folders.

Skin Template

A common misperception is that each folder represents a Skin.  In fact, each folder represents something we call a Skin Template, and can be used to render multiple skins.  One way to think of it is that each folder contains a family of skins.

Each folder contains a series of .ascx controls used to render each skin in that skin family as well as some CSS stylesheets and images used for individual skins or for the entire family.

For example, the Redbook template folder contains three skins, Redbook, BlueBook, and GreenBook.  In the screenshot below, we can see that there are three CSS stylesheets that specifically correspond to these three skins.  How does Subtext know that these three stylsheets define three different skins while the other stylesheets in this folder do not?

Redbook Folder

The answer is that this is defined within the Skins.config file.

Skins.config

The Skins.config file is located in the Admin directory and contains an XML definition for every skin.  Here is a snippet of the file containing the definition for the Redbook family of skins. This snippet shows the definition of the BlueBook skin.

<?xml version="1.0"?>
<SkinTemplates  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Skins>
    <SkinTemplate Name="BlueBook" 
                  TemplateFolder="RedBook" 
                  StyleSheet="Blue.css">
      <Scripts>
        <Script Src="~/Admin/Resources/Scripts/niceForms.js" />
      </Scripts>
      <Styles>
        <Style href="~/skins/_System/csharp.css" />
        <Style href="~/skins/_System/commonstyle.css" />
        <Style href="~/skins/_System/commonlayout.css" />
        <Style href="niceforms-default.css" />
        <Style href="print.css" media="print" />
        </Styles>
    </SkinTemplate>
  </Skins>
</SkinTemplates>

There is a SkinTemplate node for each Skin within the system (I know, not quite consistent now that I think of it. Should probably be named Skin). 

The Name attribute defines the name of this particular skin. 

The TemplateFolder attribute specifies the physical skin template folder in which the all the ascx controls and images are located in. 

The StyleSheet attribute specifies which stylesheet defines the primary CSS stylesheet for this skin. 

For example, the GreenBook skin definition looks just like the BlueBook skin definition except that the StyleSheet attribute references Green.css instead of Blue.css.

Within the SkinTemplate node is a collection of Script nodes and a collection of Style nodes.  These specify any client scripts (such as Javascript) and other CSS files that should be included when rendering this skin.  As you can see, the tilde (~) syntax works for specifying a path to a file and a developer can specify a media and a conditional for each CSS stylesheet.

Controls

I keep mentioning that Subtext depends on a collection of .ascx user controls when it renders a family of skins. Let’s talk about them for a moment. 

In the second screenshot above, you may have noticed a directory named Controls.  This contains the bulk of the .ascx controls used to render a specific skin.  There was also a control named PageTemplate.ascx in the parent directory.

The Controls

Each skin in a family of skins is rendered by the same set of UserControl instances.  The only difference between two skins within the same family is the CSS stylesheet used (which can account for quite a difference as we learn from CSS Zen Garden).

The PageTemplate.ascx control defines the overall template for a skin, and then each of these user controls in the Controls directory is responsible for archiving a specific portion of the blog.

Select a different skin from another skin family, and Subtext will load in a completely different set of UserControl files, that all follow the same naming convention.

Drawbacks and Future Direction

This is one of the drawbacks to the current implementation.  For example, rather than using data binding syntax, each .ascx file is required to define certain WebControl instances with specific IDs.  The underlying Subtext code then performs a FindControl searching for these specific controls and sets their values in order to populate them.  This naming convention is often the most confusing part of implementing a new skin for developers.

It used to be that if a WebControl was removed from an .ascx file (perhaps you didn’t want to display a particular piece of information), this would cause an exception as Subtext could not find that control. We’ve tried to remedy that as much as possible.

In the future, we hope to implement a more flexible MasterPage based approach in which the underlying code provides a rich data source and each skin can bind to whichever data it wishes to display via data binding syntax.

From a software perspective, this changes the dependency arrow in the opposite direction.  Rather than the underlying code having to know exactly which controls a skin must provide, it will simply provide data and it is up to the individual skin to pick and choose which data it wishes to bind to.

Conclusion

We provided the Naked skin so that developers creating custom skins could play around with an absolutely bare-bone skin and see just how each of the controls participates in rendering a blog.

tags: , , ,

Subtext Skinning Changes

With the Subtext 1.9 release just around the corner, this is probably a good time to highlight some minor, but important, changes to skinning in Subtext.

We made some breaking changes to Skins.config file format to make the naming more consistent with the purpose.  There was a lot of confusion before.  The following is a snippet from a pre-Subtext 1.9 Skins.config file.

<?xml version="1.0"?>
<SkinTemplates xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Skins>
    <SkinTemplate SkinID="RedBook" 
                    Skin="RedBook" 
                    SecondaryCss="Red.css">
      <Scripts>
        <Script Src="~/Admin/Resources/Scripts/niceForms.js" />
      </Scripts>
      <Styles>
        <Style href="niceforms-default.css" />
        <Style href="print.css" media="print" />
      </Styles>
    </SkinTemplate>
  </Skins>
</SkinTemplates>

And here is how that snippet will change in Subtext 1.9.

<?xml version="1.0"?>
<SkinTemplates xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Skins>
    <SkinTemplate Name="RedBook" 
                  TemplateFolder="RedBook" 
                  StyleSheet="Red.css">
      <Scripts>
        <Script Src="~/Admin/Resources/Scripts/niceForms.js" />
      </Scripts>
      <Styles>
        <Style href="niceforms-default.css" />
        <Style href="print.css" media="print" />
      </Styles>
    </SkinTemplate>
  </Skins>
</SkinTemplates>

The key differences are in the SkinTemplate element. The following attributes have been renamed:

  • SkinID was changed to Name
  • Skin was changed to TemplateFolder
  • SecondaryCss was changed to StyleSheet

Another new change is that the Style element now supports a new attribute named conditional. If specified, Subtext will wrap the stylesheet declaration with an IE specific conditional comment. This is commonly used for stylesheets that contain IE specific CSS hacks. For example...

<Style href="IEHacks.css" conditional="if ie" />

Gets rendered as...

<!--[if ie]>
<Style href="IEHacks.css" conditional="if IE" />
<![endif]-->

Thus only IE will see that style declaration.

tags: , , ,

Coder's Bill of Rights

Gratuitious nature pic for no good reason other than I love Windows Live WriterJust sticking my head above water long enough to take a breath and to link to some rubbish called the Programmer’s Bill of Rights that Jeff Atwood declares on his blog.

I don't understand this guy.  You let this sort of dangerous propaganda spread and software departments will become much more efficient and be able to build better systems with less money. 

You realize what that means, don't you?  Companies will be able to get more done with less people

For those who lose their jobs because of this, blame Atwood.  Then again, if you’re reading his blog, you're probably not the target audience that would get laid off due to increased efficiency.  Readers of my blog on the other hand ...

String.IsNullOrEmpty

This post is an ode to one of my favorite, albeit extremely minor, additions to .NET 2.0.  This is the method that I am sure we have all written in some sort of StringHelper library of some sort, but are now glad it is included in the framework as it makes our code a tad bit cleaner and shuts up that pesky FxCop warning about using the length of the string to test for empty strings.

And under the hood, it does the right thing.

public static bool IsNullOrEmpty(string value)
{
      if (value != null)
      {
            return (value.Length == 0);
      }
      return true;
}

If you haven’t met this method, do get well acquainted.

Soccer Beat Down

Tonight we had our first game of the new season against Hollywood United who now feature Alexi Lalas and Frank Le Boeuf, among several other former pro and national team players.  Let’s just say the result wasn’t pretty.  At least not pretty for us as they dismantled us 12 to 0.

As bad a beating as it was, it was exciting to be playing against such a high caliber team.  And we managed to put together some good plays, but pretty much looked erratic and rushed with the ball.

I asked one of my teammate’s wife to take some photos, but being a night game, none of them turned out too well.  Here is the best I got showing both these players.

Frank and Alexi on the Pitch

I had one good play in which I dummied the ball, faking Alexi and taking him out of the play as he was hot on my back (I know, I’m reaching here).  Of course later in the game, he embarrassed me with a nutmeg.

After the game we were chatting with a player from their team who is going to be in a new TV show called Heros.  He told us his superpower in the show was that if he touches you he paralyzes you.  Now we know what happened on the field.

Next game we hope to hold them to single digits.  We’re in need of a keeper if you know any former standout keepers.

Colbert on Dungeons and Dragons

Stephen Colbert of Comedy Central’s The Colbert Report is in my opinion the funniest comedian on television right now. As Mugatu (played by Will Ferrell, the funniest comedian in movies) would say, “It’s that damn Stephen Colbert! He's so hot right now! Stephen Colbert.”

This clip had me rolling on the floor laughing for its spot on parody of Dungeons and Dragons. It is probably funnier to those of us who geeked out with D&D back in the day than for the unitiated.

After watching it, one gets the sense that he really was a player. It turns out he is a true fan, based on this Gamespy report.

Related Posts:

Resistance Is Not Futile

Interesting post on the 37Signals blog regarding competing against Google.  Harb references a post by Paul Graham about Kiko’s founders putting their site up on eBay. 

Long story short, it sounds like Kiko came out with a web-based calendar, but when Google came out with theirs, Kiko’s growth stopped, users defected, and they threw in the towel.

Paul’s solution is to stay out of Google’s way.

Kind of sounds like what could happen to Blogjet with the advent of Windows Live Writer.  However Harb offers a more optimistic take on the events.

No. Don’t run, don’t hide. Be different. You can’t outdo Google by trying to match them point-by-point, but you don’t have to. There are other, better ways to fight. Compete differently.

Perhaps the same can be said for the creator of BlogJet vs Microsoft.  I was chatting with Jon Galloway about this and I suggested this would really hurt BlogJet, perhaps even spell its demise in the long run.  How does a tiny company challenge Microsoft and compete against a free product?  Jon was more optimistic and pointed to SourceGear as a counterexample.

I was not convinced.  Writing software to post to a blog has a much lower bar of entry compared to a source control system.  But so does writing a Calendar app, no?

It will be interesting to see if Dmitry can competes differently and come out with some creative means to keep Blogjet viable.  Good luck.

Subtext 1.9 Update

Work on Subtext 1.9 is progressing well with more and more contributors chipping in. No firm release date yet, but hopefully soon.  The latest builds are pretty stable, but there a few more minor bug fixes to get in there. Also we plan to implement the MetaWeblog API newMediaObject method in order to better support Windows Live Writer.

If you like to live dangerously, you can download one of the latest builds and give it a twirl.  Please note that link is running off of a VMWare Server within a Shuttle box in my office/guest room. So if we have guests, that link is going down for the night.

Of course, should you find a bug, you know the drill.

In the meanwhile, you can track our status by checking out our CruiseControl.NET statistics page.  As you can see, our code coverage from unit tests is steadily climbing, currently at 34%.  There are 23584 lines of code with 1367 FxCop warnings.

Atwood Is Preaching And I'm In The Choir

ChoirIn Jeff Atwood’s latest post entitled Source Control: Anything But SourceSafe he is preaching the gospel message to choose something other than Visual Source Safe and I am screaming amen in the choir section.

There are three common reasons I hear for sticking with Visual Source Crap (I sometimes swap that last word with one that doesn’t break the acronym).

1. It is free!

UPDATE: As a lot of people pointed out, VSS isn't free. What I meant was that it comes with the MSDN Universal Subscription, so many companies already have a copy of VSS.

So is Subversion.  I was on a project recently in which VSS corrupted the code twice!  The time spent administering to it and time lost was a lot more costly than a license to SourceGear Vault.

2. We know how to use it and don’t want to learn a new system.

When I hear this, what I am really hearing is we like our bad habits and don't want to spend the time to learn good habits.  Besides, Eric Sink already wrote a wonderful tutorial.

3. We have so much invested in VSS.

Well you had a lot invested in classic ASP (or other such technology) and that didn't stop you from switching over to ASP.NET (Ruby on Rails, Java, etc...), did it?

The reason I spend time and energy trying to convince clients to switch is that it saves them money and saves me headaches.  It really is worth the effort.

For Open Source projects (or any project that receives user code contributions), Subversion and CVS have the nice benefit of a patching feature making it easy to contribute without having write access.

tags:

Pictures Of The Day

Since my blog has been getting a bit geek heavy for my wife’s taste (see comment - though she now has some greek cred for having a gravatar) I thought I would try out Tim Heuer’s Flickr plugin and post a couple of random pics.

Blue Skies

The above pic was taken by my brother-in-law when the in-laws visited this past spring.

Lazy Sea Lions

On that same trip, we took a boat ride in Oceanside and took a pic of these orgyastic sea lions. In public no less! Shameful.

What Is Really Simple Discovery (RSD)?

That is a fine question in need of a good answer.  The answer for implementors is easily found in the spec.  For the rest of us there is the exceedingly sparse entry (at the time of this writing) in Wikipedia.

That entry is somewhat pathetic at the moment. I mean who wrote that crap?! Hmmm.. Well taking a look at the history... I see that the perpetrator was...oh! me.

Well I am sure one of you can do a much better job than I did of elaborating on the topic.

Windows Live Writer First Impression

There is a lot of buzz around Windows Live Writer.  I Might as well throw my two cents into the fray.  Keep in mind that I do understand this is a beta.

The Good

  • It uses the paragraph tag <p> around your paragraphs rather than <br /> tags.
  • Keyboard shortcuts for switching views.
  • Integrates your blog’s style when previewing.  This is an impressive feature.  When I preview the post I am working on, it displays the post as if it was posted on my site.  I have seen a temporary blog post with the message This is a temporary post that was not deleted. Please delete this manually. (c3eaf4ab-0941-4881-9d17-d4f62bde069e)  in a few blogs related to obtaining a blog’s style. My guess is that Windows Live Writer posts to the blog, then deletes the post.  It takes a diff to figure out what changed.  That’s just my guess.
  • When adding a link, it has a field for the rel tag.  Nice!
  • It handles my unusual FTP setup using an FTP virtual directory. Not every tool deals with that correctly.
  • Inserting images allows you to apply a few simple effects. Very neat!
  • Support for Really Simple Discovery (RSD).  I just added RSD to Subtext.
  • Plugins!

The Needs Improvement

Since this is a beta, I didn’t feel right calling it “The Bad”.

  • Doesn’t let you specify the title attribute of a tag.
  • Inserts &nbsp; all over the place as you edit text.
  • What key do I hit to Find Next?
  • No Find and Replace? Really?
  • Be nice to have HTML highlighting in the HTML View.
  • If I markup a word with the <code> tag, then cut and paste that word elsewhere in the Normal view, it marks up the pasted word with the font tag rather than using the underlying tag.

The Wish

  • Using typographic characters for singe quotes and double quotes ala MS Word.
  • Integration with CSS styles from the blog. Be nice to have a pulldown for various CSS classes.
  • Configurable shortcuts. I use the <acronym /> and <code /> tag all the time and have shortcuts for these configured in w.bloggar.

All in all, I really like it so far.  It has a nice look and feel to it and if they iron out a lot of these kinks, this may be the one to beat.

Unfortunately, this may cause some delays in Subtext 1.9 unless I can fight off the vicious urge to write some Microformat plugins. Why must these people introduce such interesting toys into my life! Where will I find the time? ;)

Speaking of which, Tim Heuer already has written a couple of plugins

[Download Windows Live Writer]

Fun Iterating PagedCollections With Generics and Iterators

Book Oh boy are you in for a roller coaster ride now!

Let me start with a question, How do you iterate through a large collection of data without loading the entire collection into memory?

The following scenario probably sounds quite familiar to you. You have a lot of data to present to the user. Rather than slapping all of the data onto a page, you display one page of data at a time.

One technique for this approach is to define an interface for paged collections like so...

/// <summary>
/// Base interface for paged collections.
/// </summary>
public interface IPagedCollection
{
    /// <summary>
    /// The Total number of items being paged through.
    /// </summary>
    int MaxItems
    {
        get;
        set;
    }
}

/// <summary>
/// Base interface for generic paged collections.
/// </summary>
public interface IPagedCollection<T> 
    : IList<T>, IPagedCollection
{ 
}

The concrete implementation of a generic paged collection is really really simple.

/// <summary>
/// Concrete generic base class for paged collections.
/// </summary>
/// <typeparam name="T"></typeparam>
public class PagedCollection<T> : List<T>, IPagedCollection<T>
{
    private int maxItems;

    /// <summary>
    /// Returns the max number of items to display on a page.
    /// </summary>
    public int MaxItems
    {
        get { return this.maxItems; }
        set { this.maxItems = value; }
    }
}

A method that returns such a collection will typically have a signature like so:

public IPagedCollection<DateTime> GetDates(int pageIndex
    , int pageSize)
{
    //Some code to pull the data from database 
    //for this page index and size.
    return new PagedCollection<DateTime>();
}

A PagedCollection represents one page of data from the data source (typically a database). As you can see from the above method, the consumer of the PagedCollection handles tracking the current page to display. This logic is not encapsulated by the PagedCollection at all. This makes a lot of sense in a web application since you will only show one page at a time.

But there are times when you might wish to iterate over every page as in a streaming situation.

For example, suppose you need to perform some batch transformation of a large number of objects stored in the database, such as serializing every object into a file.

Rather than pulling every object into memory and then iterating over the huge collection ending up with a really big call to Flush() at the end (or calling flush after each iteration, ending up in too much flushing), a better approach might be to page through the objects calling the Flush() method after each page of objects.

The CollectionBook class is useful just for that purpose. It is a class that makes use of iterators to iterate over every page in a set of data without having to load every record into memory.

You instantiate the CollectionBook with a PagedCollectionSource delegate. This delegate is used to populate the individual pages of the data we are iterating over.

public delegate IPagedCollection<T> 
    PagedCollectionSource<T>(int pageIndex, int pageSize);

When iterating over the pages of a CollectionBook instance, each iteration will call the delegate to retrieve the next page (an instance of IPagedCollection<T>) of data. This uses the new iterators feature of C# 2.0.

Here is the code for the enumerator.

///<summary>
///Iterates through each page one at a time, calling the 
/// PagedCollectionSource delegate to retrieve the next page.
///</summary>
public IEnumerator<IPagedCollection<T>> GetEnumerator()
{
  if (this.pageSize <= 0)
    throw new InvalidOperationException
      ("Cannot iterate a page of size zero or less");

  int pageIndex = 0;
  int pageCount = 0;

  if (pageCount == 0)
  {
    IPagedCollection<T> page 
      = pageSource(pageIndex, this.pageSize);
    pageCount = (int)Math.Ceiling((double)page.MaxItems / 
      this.pageSize);
    yield return page;
  }

  //We've already yielded page 0, so start at 1
  while (++pageIndex < pageCount)
  {
    yield return pageSource(pageIndex, this.pageSize);
  }
}

The following is an example of instantiating a CollectionBook using an anonymous delegate.

CollectionBook<string> book = new CollectionBook<string>(
    delegate(int pageIndex, int pageSize)
    {
        return pages[pageIndex];
    }, 3);

I wrote some source code and a unit test you can download that demonstrates this technique. I am including a C# project library that contains these classes and one unit test. To get the unit test to work, simply reference your unit testing assembly of choice and uncomment a few lines.

Technorati Tags: , , , ,

Subtext Breaks Into The Top 15 Active Projects In SourceForge

Subtext Logo No, this is not a case of breaking and entering. With preparations for the next release of Subtext, apparently our SourceForge ranking has climbed up into the top 15. Now keep in mind this is a ranking of the most active projects, and is not a ranking of the success or value of a project. It is an amalgamation of source control checkins, discussion forum activity, and other statistical factors.

Even so, it is satisfying to see our project listed as #13 in the most active projects last week. It is an indication that more and more people are getting involved and having fun building this puppy.

I hadn’t even noticed until several people pointed it out.

I imagine that number will dip after we release when we all take a collective sigh of relief.

I Swear I Didn’t Commission This

Or did I?

JackAce, a former coworker of mine and total poker addict, happened upon this fine piece of artwork and astutely snapped this picture for posterity with his T-Mobile (click through to go to the original flickr pic).

Picture of my last name graffiti'd on a park bench

Soon the entire city of Angels will know the name of Haack!!! BWAHAHA!

Unfortunately the idiot taggers didn’t follow my instructions. I told them to tag my URL, not my name.

Eeeeediots!

Good taggers are hard to find. You might promote your blog by writing good content, posting comments in other blogs, and other such nonsense, and hey that’s cool for you. But this is how we roll in L.A.

Westside!

How To Get The Calling Method And Type

Here are a couple of useful methods for getting information about the caller of a method. The first returns the calling method of the current method. The second returns the type of the caller. Both of these methods require declaring the System.Diagnostics namespace.

private static MethodBase GetCallingMethod()
{
  return new StackFrame(2, false).GetMethod();
}

private static Type GetCallingType()
{
  return new StackFrame(2, false).GetMethod().DeclaringType;
}

Pop Quiz! Why didn’t I apply the principle of code re-use and implement the second method like so?

public static Type GetCallingType()
{
    return GetCallingMethod().DeclaringType;
}

A virtual cigar and the admiration of your peers to the first person to answer correctly.

Technorati Tags: ,

The Using Statement And Disposable Value Types

A while ago Ian Griffiths wrote about an improvement to his TimedLock class in which he changed it from a class to a struct. This change resulted in a value type that implements IDisposable. I had a nagging question in the back of my mind at the time that I quickly forgot about. The question is wouldn’t instances of that type get boxed when calling Dispose?

So why would I wonder that? Well let’s take a look at some code and go spelunking in IL. The following humble struct is the star of this investigation.

struct MyStruct : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing");
    }
}

Let’s write an application that will instantiate this struct and call its Dispose method via the interface.

public class App
{
    public void DemoDisposable()
    {
        IDisposable disposable = new MyStruct();
        DisoseIt(disposable);
    }
    
    public void DisoseIt(IDisposable disposable)
    {
        disposable.Dispose();
    }
}

Finally we will take our trusty Reflector out and examine the IL (I will leave out the method header).

.maxstack 2
.locals init (
    [0] [mscorlib]System.IDisposable disposable1,
    [1] NeverLockThis.MyStruct struct1)
L_0000: ldloca.s struct1
L_0002: initobj NeverLockThis.MyStruct
L_0008: ldloc.1 
L_0009: box NeverLockThis.MyStruct
L_000e: stloc.0 
L_000f: ldarg.0 
L_0010: ldloc.0 
L_0011: call instance void 
    NeverLockThis.App::DisoseIt([mscorlib]System.IDisposable)
L_0016: ret 

Notice the bolded line has a boxing instruction. As we can see, our struct gets boxed before the Dispose method is called.

The using statement requires that the object provided to it implements IDisposable. Here is a snippet from the MSDN2 docs on the subject.

The using statement allows the programmer to specify when objects that use resources should release them. The object provided to the using statement must implement the IDisposable interface. This interface provides the Dispose method, which should release the object's resources.

I wondered if the using statement enforced the IDisposable constraint in the same way a method would. Let’s find out. We will add the following new method to the App class.

public void UseMyStruct()
{
    MyStruct structure = new MyStruct();
    using (structure)
    {
        Console.WriteLine(structure.ToString());
    }
}

This code now implicitely calls the Dispose method via the using block. Cracking it open with Reflector reveals...

.maxstack 1
.locals init (
    [0] NeverLockThis.MyStruct struct1,
    [1] NeverLockThis.MyStruct struct2)
L_0000: ldloca.s struct1
L_0002: initobj NeverLockThis.MyStruct
L_0008: ldloc.0 
L_0009: stloc.1 
L_000a: ldloca.s struct1
L_000c: constrained NeverLockThis.MyStruct
L_0012: callvirt instance string object::ToString()
L_0017: call void [mscorlib]System.Console::WriteLine(string)
L_001c: leave.s L_002c
L_001e: ldloca.s struct2
L_0020: constrained NeverLockThis.MyStruct
L_0026: callvirt instance void 
    [mscorlib]System.IDisposable::Dispose()
L_002b: endfinally 
L_002c: ret 
.try L_000a to L_001e finally handler L_001e to L_002c

As you can see, there is no sign of a box statement anywhere to be seen. Forgive me for ever doubting you .NET team. As expected, it does the right thing. I just had to be sure. But do realize that if you pass in a value type that implements IDisposable to a method that takes in IDisposable, a box instruction will occur.

Praise For a Good Company

Today I was thinking about how much I enjoy doing business with a particular company and realized my natural inclination was not to blog about it. Yet when a company really drops the ball, I have no problem airing my criticisms. I don’t know about you, but I don’t like to be around people who are only critical and never offer praise. It gets to be tiresome.

So today I would like to give a shout out to Paychex (I am not in any way affiliated with them other than being a customer). We signed up with them to handle our payroll and everything has been extremely smooth. We had a few problems (not related to them) when we initially started and they went way above and beyond to help us straighten our books related to payroll.

Not only that, the payroll specialist who handles our account is extremely courteous and helpful on the phone. Just an all around cool guy. It is very telling when you are looking forward to calling in payroll because you like talking to the rep. Paychex just seems to understand customer service and gets it right.

So tell me, who deserves your shout out today?

Tying MVP To the ASP.NET Event Model

I knew this question would come up, so I figure I would address it in its own blog post. Mike asks a great question about my MVP implementation (actually he asks two).

One observation...don't you seem to be tying the presenter to the ASP.NET event model? If not, can you use the same presenter for a WinForms app?

The answer is that I am absolutely tying my presenter to ASP.NET.

Why?

Well when I first working on the article, I planned on creating an abstracted IView and presenter that would work for both ASP.NET and Windows Forms, but ran into a few problems. The biggest problem is that I rarely have to write a Windows Forms applications. In fact, I almost never do. So why spend all this time on something I won’t need? I had to call YAGNI on my efforts.

Premature Generalization

Besides, I didn’t want to run afoul of Eric Gunnerson’s #1 deadly sin of programming, premature generalization. There is no point in writing an IView and Presenter to work with both winforms and ASP.NET unless I am also implementing concrete instances of both at the same time. Otherwise I will write it for one platform and hope it will work for the other. If I ever do implement it for the other, I will probably have to rewrite it anyways.

Parity is a rarity

Secondly, even if I did need it, there are some other issues to deal with. First, trying to write a single presenter for both ASP.NET and a WinForms app assumes the user interaction with the application and the view is going to be roughly the same. That is rarely the case. If I have to go to the trouble to write a Winforms app, I will certainly take advantage of its UI benefits.

Leaky Abstractions Rear Their Head

Thirdly, despite all the hoops that ASP.NET jumps through to abstract the fact that it is a web application and present an API that feels like a desktop platform, it is still a web application platform. The abstraction is leaky and trying to abstract it even more causes problems.

For example, in a Winforms view, you only need to call the Initialize method once because the data is persistent in memory. With an ASP.NET view by default, you have to essentially repopulate every data field every time a user clicks a button. Can you imagine a Winforms app written like that?

Of course you could more closely simulate the Winforms view in ASP.NET view by storing these fields in ViewState or, shudder, Session, but this then becomes a constraint on your ASP.NET view in order to support this pattern, forcing you to take a Winforms approach to a web based app. Ideally a presenter for an ASP.NET application should not have to assume that the ASP.NET view is going to store fields in a persistent manner.

Conclusion

So that is a long-winded answer to a short question. I believe if I had to, I could get the same Presenter to work for both a Winforms App and an ASP.NET app. These problems I mention are not insurmountable. However, I would need to be properly motivated to do so, i.e., have a real hard requirement to do so.

ASP.NET Supervising Controller (Model View Presenter) From Schematic To Unit Tests to Code

UPDATE: For a more full featured implementation of this pattern for ASP.NET Web Forms, check out the WebForms MVP project! It’s available as a NuGet package! Install-Package WebFormsMVP

Martin Fowler recently split the Model-View-Presenter pattern into two new patterns, Supervising Controller and Passive View. They are pretty much two different flavors of the MVP pattern differentiated by how much application logic is placed within the view.

The goal of this post is to demonstrate an end-to-end walk-through of the process of implementing the Supervising Controller pattern starting with a rough schematic. My goal is not explain the Supervising Controller pattern in detail but to help others take it out of the sounds nice in theory bucket and move it into I can and will use this in a real project, I promise bucket. There are many ways to implement the pattern for any given scenario, so keep in mind that this is not the one true way, just one way.

The Schematic

For this fictional scenario, I might as well pick an interface that I am familiar with, a blogging engine. In particular I will create a very very simple page to edit the body of a blog post and add and remove tags associated with the post. In trying to keep the example simple, I leave out all minor and extraneous details such as a title for the blog post. How important is a title, really?

Tag UI Schematic

The chicken scrawl above is a hand-drawn quick and dirty schematic for the user interface. No surprises here. There is a text area for entering the body of the blog post. There is a list of existing tags for the current post on the right. At the bottom of the list of tags is a text box. In order to add a new tag, the user can simply type in the name of the tag and click the add button. This will add a new tag to the list and associate it to the blog post. Note that when adding tags, any changes to the blog post should not be lost. The user can also remove tags by clicking the [x] next to the tag name.

When the user is finally ready to save the blog entry, the user clicks the Save button.

Defining the View

The next step is to analyze the schematic and define a view interface that can appropriately represent this interface. For the sake of this discussion, I will implement a single view that will represent this entire page. An alternative approach would be to break the page into two user controls and implement each user control independently with its own view and presenter.

Examining the schematic reveals what properties we need to populate the view interface. Obviously the view should have a getter and setter for the body text. Probably good to have a property that returns a collection of tags as well. We will want a getter and setter for the tag textbox, and a an event each for the buttons.

The entire process of defining a view interface may take multiple iterations. I am going to skip that long process (mainly because I cannot bear to type that much) and show you what I ended up with. Before I created the specific view interface, I defined a more generic base interface, IView. This is the base interface I will use for all my ASP.NET views.

public interface IView
{
    event EventHandler Init;

    event EventHandler Load;

    bool IsPostBack { get; }

    void DataBind();

    bool IsValid { get;}
}

One thing to notice is this interface defines two events, Init and Load. Most implementations of MVP that I’ve seen place an Initialize method on the Presenter/Controller class that every concrete view implementation must remember to call at the right time.

But when you have an ASP.NET Control or Page implement this interface, you don’t have to remember to have every concrete view call anything and you don’t have to implement these interfaces. It is already done for you by the ASP.NET runtime. You get the initialization call for free. Less code is better code I always say.

One common complaint with such an approach is that events on an interface are hard to test. I thought the same until I discovered Rhino Mocks. Now testing events is quite easy.

Here is the final view interface for the blog post edit page.

public interface IPostEditView : IView
{
    string BlogPostBody { get;set;}

    ICollection<Tag> Tags { get; set; }

    string NewTag { get; set;}

    int BlogPostId {get;}

    event EventHandler PostSaved;

    event EventHandler TagCreated;

    event EventHandler<TagRemovedEventArgs> TagRemoved;
}

Again, no surprises. One thing to note is the BlogPostId property. In this scenario, it is the view that is responsible for figuring out which blog post to edit. This makes sense in a lot of scenarios as the id may be coming in from a query string or via some other means. However, in other scenarios, you might want the controller to be responsible for figuring out which post to edit.

Writing a Unit Test

Now our next major task is to implement the presenter. But before we do that, we should start off with a basic unit test. The first thing I want to test is that the presenter properly attaches to the events on the view. The following is the test I wrote. Notice that there is some code in the SetUp method that I am not presenting here. You can see that code later.

[Test]
public void VerifyAttachesToViewEvents()
{
    viewMock.Load += null;
    LastCall.IgnoreArguments();
    viewMock.PostSaved += null;
    LastCall.IgnoreArguments();
    mocks.ReplayAll();
    new PostEditController(viewMock, 
      this.dataServiceMock);
    mocks.VerifyAll();
}

Defining the presenter

With the test in place, I can move forward and start implementing the controller. In practice, I only implement enough to get my test to pass, at which point I write another test and the cycle continues. In order not to bore you, I will skip ahead and show you the entire Controller implementation.

public class PostEditController
{
    BlogPost blogPost;
    IPostEditView view;
    IBlogDataService dataService;
    
    //Attaches this presenter to the view’s events.
    public PostEditController(IPostEditView view, 
      IBlogDataService dataService)
    {
        this.view = view;
        this.dataService = dataService;
        SubscribeViewToEvents();
    }
    
    void SubscribeViewToEvents()
    {
        view.Load += OnViewLoad;
        view.PostSaved += OnPostSaved;
        view.TagCreated += OnTagCreated;
        view.TagRemoved += OnTagRemoved;
    }

    void OnTagRemoved(object sender, TagRemovedEventArgs e)
    {
        this.dataService.RemoveTag(e.Title);
        this.blogPost = this.dataService.GetById(view.BlogPostId);
        view.Tags = blogPost.Tags;
        view.DataBind();
    }

    void OnPostSaved(object sender, EventArgs e)
    {
        Save();
    }

    void OnTagCreated(object sender, EventArgs e)
    {
        CreateAndAddTag();
    }

    void OnViewLoad(object sender, EventArgs e)
    {
        if (!view.IsPostBack)
        {
            LoadViewFromModel();
            view.DataBind();
        }
    }
    
    public Tag GetTagById(int id)
    {
        //Normally we’d probably just have a method 
        //of the service just return this.
        foreach (Tag tag in this.blogPost.Tags)
        {
            if(tag.Id == id)
                return tag;
        }
        return null;
    }

    void LoadViewFromModel()
    {
        this.blogPost = this.dataService.GetById(view.BlogPostId);
        view.Tags = blogPost.Tags;
        view.BlogPostBody = blogPost.Description;
    }
       
    void Save()
    {
        this.dataService.Save(view.BlogPostId, view.BlogPostBody);
        LoadViewFromModel();
        view.DataBind();
    }
    
    void CreateAndAddTag()
    {
        this.dataService.AddTag(view.NewTag);
        //Need to rebind the tags. retrieve tags from db.
        this.blogPost = this.dataService.GetById(view.BlogPostId);
        view.Tags = blogPost.Tags;
        view.NewTag = string.Empty;
        view.DataBind();
    }
}

Implementing the View

I have yet to implement the ASP.NET page that will implement the view, yet I am able to write a bunch of unit tests (which I will provide) against the presenter to make sure it behaves appropriately. This is the benefit of this pattern in that much more of the UI logic is now testable.

Implementing the ASP.NET page is pretty straight forward. I drop a few controls on a page, wire up the controls declaratively to their data sources, and then implement the IPostEditView interface. As much as possible, I want to leverage ASP.NET declarative data binding. The point isn’t to force developers to write more code. Here is the code behind for the page. I apologize for the code heaviness of this article.

public partial class _Default : System.Web.UI.Page, IPostEditView
{
    PostEditController controller;
    ICollection<Tag> tags;
    
    public _Default()
    {
         this.controller = 
             new PostEditController(this, new BlogDataService());
    }
    
    protected void Page_Load(object sender, EventArgs e)
    {
    }
    
    public void Update()
    {
        DataBind();
    }

    public int BlogPostId
    {
        get { return GetBlogId(); }
    }
   
    private int GetBlogId()
    {
        string idText = Request.QueryString["id"];
        int result;
        if(int.TryParse(idText, out result))
        {
            return result;
        }
        return 1;
    }

    public string BlogPostBody
    {
        get { return this.txtDescription.Text; }
        set { this.txtDescription.Text = value; }
    }

    public ICollection<Tag> Tags
    {
        get { return this.tags; }
        set { this.tags = value; }
    }

    public string NewTag
    {
        get { return this.txtNewTag.Text; }
        set { this.txtNewTag.Text = value; }
    }
    
    protected void OnSaveClick(object sender, EventArgs e)
    {
        EventHandler postSaved = this.PostSaved;
        if (postSaved != null)
            postSaved(this, EventArgs.Empty);
    }
    
    protected void OnAddTagClick(object sender, EventArgs e)
    {
        EventHandler tagCreated = this.TagCreated;
        if (tagCreated != null)
            tagCreated(this, EventArgs.Empty);
    }

    void OnTagDeleteClick(object source, 
                          RepeaterCommandEventArgs e)
    {
        EventHandler<TagRemovedEventArgs> tagRemoved 
        = this.TagRemoved;
        if(tagRemoved != null)
        {
            string tagTitle 
          = ((Literal)e.Item.FindControl("ltlTag")).Text;
            tagRemoved(this, new TagRemovedEventArgs(tagTitle));
        }
    }

    public event EventHandler TagCreated;

    public event EventHandler PostSaved;

    public event EventHandler<TagRemovedEventArgs> TagRemoved;
    
    protected override void OnInit(EventArgs args)
    {
        this.btnSave.Click += OnSaveClick;
        this.btnNewTag.Click += OnAddTagClick;
        this.rptTags.ItemCommand += OnTagDeleteClick;
    }
}

When I finally compile all this and run it, here is the view I see in the browser.

Blog Post Editor Page

The benefits of this exercise become clear when you find bugs in the UI logic. Even while going through this exercise, I would find minor little bugs that I could expose by writing a failing unit test. I would then fix the bug which would cause the test to pass. That is a great way to work!

As an exercise for the reader, I left in a bug. When you leave the tag textbox blank, but click the Add button, it adds a blank tag. We should just ignore a blank tag. Try writing a unit test that fails because it assumes that a tag will not be added when the NewTag field is left blank. Then make the test pass by fixing the code in the Controller class. Finally, verify that by fixing the unit test, that the actual UI works by trying it out in the browser.

I apologize for glossing over so many details, but I did not want to turn this post into a book. If you want to read more on MVP, check out Bill McCafferty’s detailed treatment at the CodeProject. Also worth checking out is Jeremy D. Miller’s post on Model View Presenter.

Finally, to really understand what I am trying to present here, I encourage you to download the source code and try it out. I have included extensive comments and unit tests. This requires the free Web Application Project Model from Microsoft to compile and run.

The code doesn’t require a database connection, instead simulating a database via some static members. Please understand, that code is just a simulation and is not meant to be emulated in production code.

Log4Net And External Configuration File In ASP.NET 2.0

Recently I wrote that I could not seem to get Log4Net to work with an external configuration file while running ASP.NET 2.0 in Medium Trust. It turns out that I should have been more explicit. I could not get Subtext to work with Log4Net in Medium Trust, but it had nothing to do with Medium Trust. Mea culpa!

My best guess is that there was a small breaking code change in Log4Net that led to this issue since we hadn’t changed the logging code. Here is a breakdown of what happened just in case you run into this problem.

In Subtext, we wrap the Log4Net classes in our own Log class which is in the Subtext.Framework assembly. This is how we declare a logger within a class.

private readonly static ILog log 
    = new Subtext.Framework.Logging.Log();

In the Subtext.Web project, we have the following attribute in AssemblyInfo.cs which specifies the location of the log4net configuration file.

[assembly: log4net.Config.XmlConfigurator(ConfigFile 
    = "Log4Net.config", Watch = true)]

This worked fine and dandy up until ASP.NET 2.0. When you use the attribute approach, you have to make a log4net call early to jump start the engine so to speak. An attribute just sits there until somebody is told to look at it and do something about it. In our case, the line of code I showed above does the trick within Global.asax.cs.

I started digging into the Log4Net code to figure out how it uses the attribute to find the configuration file. I finally ended up at this code.

public static ILog GetLogger(Type type) 
{
    return GetLogger(Assembly.GetCallingAssembly(), 
        type.FullName);
}

GetLogger searches the attributes on the calling assembly to find out which configuration file to use. Since the calling assembly in our case is always Subtext.Framework (since we wrap all calls to Log4Net), Log4Net searches the Subtext.Framework assembly for the XmlConfiguratorAttribute. Well that won’t work because we have the attribute declared on the Subtext.Web assembly.

My initial fix was to move the attribute declaration to AssemblyInfo.cs within Subtext.Framework. That worked, but I felt like the proper place was to have it within the web project, since that is the natural place to look when you are trying to figure out where the config file is specified. So I updated the code to call log4net directly within Global.asax.cs like so.

//This call is to kickstart log4net.
//log4net Configuration Attribute is in AssemblyInfo
private readonly static ILog log 
    = LogManager.GetLogger(typeof(Global));

static Global()
{
    //Wrap the logger with our own.
    log = new Subtext.Framework.Logging.Log(log);
}

I only point this out to show there are two ways to solve it, both with their plusses and minuses. If you run into this problem, hopefully this guide will help you.

Is The Null Coalescing Operator Thread Safe?

In response to my blog post on ViewState backed properties and the Null Coalescing operator, Scott Watermasysk expresses a worry that the null coalescing operator opens one up to a race condition in the comments of his blog post.

He provides a code example of a thread safe means of reading the ViewState that copies the value from the ViewState into a local variable before performing the null check.

That got me worried as well. Not so much about the ViewState but about applying the null coalescing operator against the Cache or Session. These are classes where you are more likely to run into thread contention. Take a look at this method.

public void Demo(ref object obj)
{
Console.WriteLine((int)(obj ?? "null"));
}

The worry is that it might be possible for another thread to set value of the reference to obj to null in between the null coalescing operator’s check for null and returning it to the cast operation, potentially causing a NullReferenceException.

However, looking at the generated IL (with my comments interspersed), it seems to me (and I am no IL expert so correct me if I am wrong) that everything is just fine. It seems to copy the value before it performs the null check. So it looks like the null coalescing operator is roughly equivalent to the code Scott uses.

.method public hidebysig instance void Demo(object& obj) 
cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldarg.1
L_0002: ldind.ref
L_0003: dup //copy value to stack
L_0004: brtrue.s L_000c //jump to L_000c if value isn’t null
L_0006: pop
L_0007: ldstr "null"
L_000c: unbox.any int32
L_0011: call void [mscorlib]System.Console::WriteLine(int32)
L_0016: nop
L_0017: ret
}

So it looks like this is a thread safe operation to me. I look forward to any IL experts informing me if I happen to be missing something.

Threading - Never Lock This Redux

A while ago I wrote that you should never lock a value type and never lock this. I presented a code snippet to illustrate the point but I violated the cardinal rule for code examples: compile and test it in context. Mea Culpa! Today in my comments, someone named Jack rightly pointed out that my example doesn’t demonstrate a deadlock due to locking this. As he points out, if the code were in a Finalizer, then my example would be believable.

To my defense, I was just testing to see if you were paying attention. ;) Nice find Jack!

My example was loosely based on Richter’s example in his article on Safe Thread Synchronization. Instead of rewriting his example, I will just link to it.

His example properly demonstrates the problem with a Finalizer thread attempting to lock on the object. However Jack goes on to say that locking on this in an ordinary method is fine. I still beg to differ, and have a better code example to prove it.

Again, suppose you carefully craft a class to handle threading internally. You have certain methods that carefully protect against reentrancy by locking on the this keyword. Sounds great in theory, no? However now you pass an instance of that class to some method of another class. That class should not have a way to use the same SyncBlock for thread synchronization that your methods do internally, right?

But it does!

In .NET, an object’s SyncBlock is not private. Because of the way locking is implemented in the .NET framework, an object’s SyncBlock is not private. Thus if you lock this, you are using to the current object’s SyncBlock for thread synchronization, which is also available to external classes.

Richter’s article explains this well. But enough theory you say, show me the code! I will demonstrate this with a simple console app that has a somewhat realistic scenario. Here is the application code. It simply creates a WorkDispatcher that dispatches a Worker to do some work. Simple, eh?

class Program
{
    static void Main()
    {
        WorkDispatcher dispatcher = new WorkDispatcher();
        dispatcher.Dispatch(new Worker());
    }
}

Next we have the carefully crafted WorkDispatcher. It has a single method Dispatch that takes a lock on this (for some very good reason, I am sure) and then dispatches an instance of IWorker to do something by calling its DoWork method.

public class WorkDispatcher
{
    int dispatchCount = 0;
    
    public void Dispatch(IWorker worker)
    {
        Console.WriteLine("Locking this");
        lock(this)
        {
            Thread thread = new Thread(worker.DoWork);
            Console.WriteLine("Starting a thread to do work.");
            dispatchCount++;
            Console.WriteLine("Dispatched " + dispatchCount);
            thread.Start(this);
            
            Console.WriteLine("Wait for the thread to join.");
            thread.Join();
        }
        Console.WriteLine("Never get here.");
    }
}

From the look of it, there should be no reason for this class to deadlock in and of itself. But now let us suppose this is part of a plugin architecture in which the user can plug in various implementations of the IWorker interface. The user downloads a really swell plugin from the internet and plugs it in there. Unfortunately, this worker was written by a malicious eeeeevil developer.

public class Worker : IWorker
{        
    public void DoWork(object dispatcher)
    {
        Console.WriteLine("Cause Deadlock.");
        lock (dispatcher)
        {
            Console.WriteLine("Simulating some work");
        }
    }
}

The evil worker disrupts the carefully constructed synchronization plans of the WorkDispatcher class. This is a somewhat contrived example, but in real world multi-threaded application, this type of scenario can quite easly surface.

If the WorkDispatcher was really concerned about thread safety and protecting its synchronization code, it would lock on something private that no external class could lock on. Here is a corrected example of the WorkDispatcher.

public class WorkDispatcher
{
    object syncBlock = new object();
    int dispatchCount = 0;
    
    public void Dispatch(IWorker worker)
    {
        Console.WriteLine("Locking this");
        lock (this.syncBlock)
        {
            Thread thread = new Thread(worker.DoWork);
            Console.WriteLine("Starting a thread to do work.");
            dispatchCount++;
            Console.WriteLine("Dispatched " + dispatchCount);
            thread.Start(this);
            
            Console.WriteLine("Wait for the thread to join.");
            thread.Join();
        }
        Console.WriteLine("Now we DO get here.");
    }
}

So Jack, if you are reading this, I hope it convinces you (and everyone else) that locking on this, even in a normal method, is a pretty bad idea. It won’t always lead to problems, but why risk it?

Technorati Tags:

Log4Net Patch For ConnectionStrings

I submitted my first patch to the Log4Net project today. The patch enables the AdoNetAppender to get its connection string from the ConnectionStrings section of a web.config file. Hopefully it gets accepted and applied.

Los Angeles User Group Meeting

Tonight I attended our local Los Angeles .NET Developers Group meeting for the first time in years. I pretty much never go to these meetings because I just haven’t found them worth dealing with the congestion of rush hour traffic in the UCLA area, which is really bad. Of course I should probably view user group meetings in the same way Jeff Atwood views conferences - I am not there for the talks, I am there to meet you.

However the local group does bring in some great speakers via INETA. Tonight’s meeting featured Rob Howard, the CEO of Telligent. I first met Rob at Mix06 and it was good to see him again at this meeting. He gave a great talk on ASP.NET tips and tricks. The one trick that stood out to me had nothing to do with his talk. I noticed at one point he had SQL code with expandable regions much like code regions via the #region directive. Instead of the pound sign, they used --region. I just tried that with a .sql file and it didn’t work for me. Probably requires a database project. I’ll have to ask him about that.

I recognized one guy in attendance who happened to be Michael Washington, a member of the DotNetNuke core team. He patiently listened to my constructive criticism and we discussed ideas for improvements to module development. One thing I hope to help him with is incorporating more unit tests into DNN code he is working on. Andrew Stopford will be pleased that I am trying to steer Michael towards MbUnit.

The challenge will be how to integrate unit tests into the ASP.NET Web Site model, since VS.NET Web Developer Express does not support class library projects. This may be a no brainer, but I have never tried it. The tests will probably just be dropped in the App_Code folder, but will TD.NET run all tests by right clicking on App_Code and selecting Run Tests. I assume so, but we’ll see.

Log4Net Breaking Change in 1.2.9

I am a little late in reporting this, but I hadn’t realized the problem until I had to maintain an older project that used Log4Net 1.2.8. I upgraded it to log4net 1.2.10 and noticed it stopped working. I then found this comment in the log4net mailing list archives.

There were a number of breaking changes in 1.2.9

http://logging.apache.org/log4net/release/release-notes.html#1.2.9

In your config file "log4net.spi.LevelEvaluator" needs to be updated to "log4net.Core.LevelEvaluator".

I hope changes that would break existing config files are far and few between.

Tiny Trick For ViewState Backed Properties

This might be almost too obvious for many of you, but I thought I’d share it anyways. Back in the day, this was the typical code I would write for a value type property of an ASP.NET Control that was backed by the ViewState.

public bool WillSucceed
{
get
{
if (ViewState["WillSucceed"] == null)
return false;
return (bool)ViewState["WillSucceed"];
}
set
{
ViewState["WillSucceed"] = value;
}
}

I have seen code that tried to avoid the null check in the getter by initializing the property in the constructor. But since the getters and setters for the ViewState are virtual, this violates the warning against calling virtual methods in the constructor. You also can’t initialize it in the OnInit method because the property might be set declaratively which happens before Init.

With C# 2.0 out, I figured I could use the null coalescing operator to produce cleaner code. Here is what I naively tried.

public bool WillSucceed
{
get
{
return (bool)ViewState["WillSucceed"] ?? false;
}
set
{
ViewState["WillSucceed"] = value;
}
}

Well of course that won’t compile. It doesn’t make sense to apply the null coalescing operator on a value type that is not nullable. Now if I had stopped to think about it for a second, I would have realized how simple the fix would be, but I was in a hurry and quickly moved on and dropped the issue. What an eeediot! All I had to do was move the cast outside of the expression.

public bool WillSucceed 
{
get
{
return (bool)(ViewState["WillSucceed"] ?? false);
}
set
{
ViewState["WillSucceed"] = value;
}
}

I am probably the last one to realize this improvement and everyone reading this is thinking, “well duh!”. But in case there is someone out there even slower than me, here you go!

And if I spend this much time trying to write a property, you gotta wonder how I get anything done. ;)

Blog Upgrade

If you read this blog outside of an aggregator, you might notice a few minor new tweaks. I am dogfooding Subtext 1.9 which runs on ASP.NET 2.0. We are very close to preparing a release, so I figured I would beta test this one on my own blog and see if everything works well.

A couple of new things you might notice is that there is now a simple search field on the left hand sidebar that displays its results in an overlaying div. Also, when you view an individual post, there are links to the next and previous post. I have also added gravatar support to the comments.

It took me a while to warm up to the idea, but I really like the gravatars. I have participated in many various message boards and sites (such as flickr) in which users choose an avatar to represent themselves. It is a small thing, but adds to the fun and identity for the visually focused. However in most cases, you have to set up a separate avatar for each site.

With gravatars, you register an avatar with their site and in any system that supports it, your avatar is displayed when you supply your email address to the software. Subtext takes your email address, creates a one-way MD5 hash of it, and then requests your gravatar from gravatar.com. If none is found, then a default placeholder is displayed. I will post a comment to this post as a demonstration.

See! The Monkeys Are Trying To Take Over. But Luckily There Is Help.

The Evil Monkey In Chris's Closet This story from Boing Boing just cracks me up. Apparently monkeys have been harassing passengers of India’s Delhi Metro. This has become such a problem that they have had to hire some langurs (a fierce looking primate) along with a langur wrangler to scare away the monkeys. Here is a description of one such incident.

In that incident, a monkey boarded a train at the underground Chawri Bazaar station and reportedly scared passengers by scowling at them for three stops. It then disembarked at Civil Lines station. Passengers had to be moved to another car while staff chased the dexterous creature, causing delays.

Bad monkey! Bad monkey!

As the Simpsons showed us, the monkeys are trying to take over the world. The image above is from Family Guy.

Open Source Is Free Like A Flower

Orchid Jeff “The CodingHorror” Atwood takes issue with the idea that software developers have any moral obligation to contribute to Open Source projects. And you know what? I agree. However I do take issue with his conclusion as well as some of the points that he makes in an attempt to bolster his argument.

The whole point of open source-- the reason any open source project exists-- is to save us time. To keep us from rewriting the same software over and over.

snip...

It's exactly what open source is about: maximum benefit, minimum effort.

Well no. Not really. Back in the day, software was just an afterthought provided by hardware companies to complement and run their hardware. This software was widely shared freely, until companies began to impose restrictions with licensing agreements and started protecting their code by hiding the source.

Many developers grew frustrated as they lost control over their working environment and were unable to modify the programs to fit their needs. Hence Richard Stallman started the Free Software movement, hoping to give users the right to:

  1. run the program, for any purpose.
  2. modify the program to suit their needs. (To make this freedom effective in practice, they must have access to the source code, since making changes in a program without having the source code is exceedingly difficult.) They must have the freedom to redistribute copies, either gratis or for a fee.
  3. redistribute copies, either gratis or free.
  4. distribute modified versions of the program, so that the community can benefit from your improvements.

Thus originally, the whole point of open source was to provide users the freedom to regain control over software in order to get their jobs done.

Of course later, the movement split into the Free Software movement which was absolutist about the freedom issue, and the Open Source movement which sought to be more pragmatic and provide a business case for open source software. Open Source focuses on the intrinsic benefit of having communities of developers improving a software codebase.

Enough with the history lesson and please, I do know that I glossed over that with a big hand wave. That is called a rough history.

The highest compliment you can pay any piece of open source software is to simply use it, because it's worth using. The more you use it, the more that open source project becomes a part of the fabric of your life, your organization, and ultimately the world.

Isn’t that the greatest contribution of all?

If I read a book you wrote because I found it worth reading, have I made a contribution? Hardly! Apart from the contribution I paid at Amazon.com for the pleasure of reading the book, I am merely using something worth using. That is not a contribution, it is just good sense.

Now if I post a positive review on my blog, that would be a contribution.

The way I see it, Jeff’s right, there is no moral obligation to contribute to an Open Source project. However I am not quite sure the puppy analogy he mentions suggests that. At the risk of yet another bad analogy, allow me to propose one that works for me. I think of Open Source software as being like a nice set of flowers in a common space such as a courtyard. Nobody that lives around the common space owns the flowers, yet they all enjoy the presense of the flowers.

Fortunately, one or two volunteers tend to the flowers and the flowers thrive. Some flowers seem to do fine with very little care, others require lots of care. But everybody benefits and there is no cost, nor moral obligation to others to contribute to the upkeep of the flowers.

However, should the caretakers decide to stop tending to the flowers, the others certainly have no right to complain to the caretakers. And while it is true that in some cases, others may take up the slack and tend to the flowers, or other flowers might grow as replacements, if nobody takes up the cause, or if the new flowers are simply weeds, then everybody loses out. Including those who had the time, means, and even desire to help with the flowers, but just never thought to because someone else was taking care of it.

So while I do not think contributing to Open Source software is a moral obligation, I do think it is a worthy practical consideration. No offense to my homie, but I think it is Jeff who misunderstands the economics of open source. Minimum effort does not equate to free in cost.

For example, should you begin to rely more and more on some piece of Open Source software, consider the effort to replace the software (which might tack on a monetary cost for a proprietary replacement) and learn a new toolkit if development of the software dies out. Hardly a minimal effort.

True, a contribution in this situation is no guarantee that a project will succeed. But doesn’t the same risk apply to purchasing proprietary software? It certainly applied to contributing to a political party (if you were a Democrat in the last couple of elections).

And while it may be infeasible for a large portion of the audience (Jeff pulls the figure 99% out of his rear, which is probably close to the truth) to contribute the extravagance of a bug fix, for the remainder for whom it is feasible, it is certainly worth considering. Every contribution not only helps in a small way to keep the project viable, but it improves the software for your own purposes. It is an entirely selfish benefit.

So again, please do not confuse exhortations to contribute to Open Source as a claim for moral obligation. I honestly believe that those who contribute will benefit from the contribution as much as the recipients of the contribution, which is why I am such a big proponent for contributing.

If you have the means, why not water the flowers for a bit? There are many forms of contributing to an open source project. I list several on the Subtext contribution web page.

Related Posts

HttpModule For Controlling Custom Headers

In a triumphant return after about three months of not blogging, Barry “idunno.org” Dorrans has published an HttpModule for modifying custom HTTP headers in response to a throw away comment by Scott Hanselman in his post on P3P requests.

The nice thing about Barry’s module is that it is much widely general and applicable than just for P3P headers.

This is a configurable HttpModule, allowing you to use web.config to specify what headers and values you wish added to requests.

So if you need tight control over your HTTP headers, this is the module for you.

Soccer Statistics Can Be Painful

Soccer BallOne thing I love about my soccer league is that they have a kick butt website. The design isn’t much to look at, but the site is chock full of useful information both current and historical.

For example, you can see how my team (Westside Rovers) did last season. Tied for fourth isn’t such a bad showing for our first season in the league, but check out the GF and GA column.

Ouch! That is 24 goals we scored while there were 54 goals scored against us in fourteen games. That means we scored an average of 1.7 goals per game, while being scored on an average of 3.9 goals a game. When we got beat, we really got beat. Notice that LA Blues, the lead leaders scored 19 goals on us. We played them twice. Double ouch!

There’s no lesson here other than sometimes, you really are better off not knowing the detailed stats. At least we have an easy target to beat for the upcoming season.

The reason for the disparity becomes apparent when you read the following blurb on the front page.

FORMER PROFESSIONAL PLAYERS ARE PLAYING ON SOME OF OUR TEAMS: Some of the teams in our league have former professional players from the Major League Soccer (MLS) in the United States and from Professional Teams from around the world. One team even has a former World Cup Champion from France 1998 on its squad. Thus the level of play can be very high at times. Come, watch and enjoy the competitive level of play.

None of those players are on our team.

In really bad news, we had a pre-season friendly this past Saturday against another team in the league. They had a new player who was trying out for the team who made a vicious sliding tackle (no slide tackles allowed in our league due to the fields policies) on my teammate, breaking his tibia and fibia. The sound of the contact made a sickeningly loud crack! and bent his leg in an unsightly manner. It may take a while, but I hope he has a speedy recovery. Meanwhile, I am looking into better health insurance.