comments suggest edit

Silver Bullet: From In a recent post I talked about how good design attempts to minimize the impact of changes to a system, often through Design Patterns.

When used appropriately, Design Patterns are a great tool for building a great design, but there is an important caveat to keep in mind anytime you apply a pattern. A Design Pattern might minimize the impact of one kind of change at the expense of amplifying another type of change.

What do I mean by this? One common pattern is the Abstract Factory pattern which is often manifested in .NET code via the Provider Model pattern. The Provider Model abstracts access to an underlying resource by providing a fixed API to the resource. This does a bang up job of insulating the consumer of the provider when changing the underlying resource.

The MembershipProvider is one such implementation of the provider model pattern. The consumer of the MembershipProvider API doesn’t need to change if the SqlMembershipProvider is swapped in favor of the ActiveDirectoryMembershipProvider. This is one way that the provider pattern attempts to minimize the impact of changes. It insulates against changes to the underlying data store.

However there is a hidden tradeoff with this pattern. Suppose the API itself changes often. Then, the impact of a single API change is multiplied across every concrete implementation of the provider. In the case of the MembershipProvider, this is pretty much a non-issue because the likelihood of changing the API is very small.

But the same cannot be said of the data access layer for software such as a blog (or similar software). A common approach is to implement a BlogDataProvider to encapsulate all data access so that the blog software can make use of multiple databases. The basic line of thought is that we can simply implement a concrete provider for each database we wish to support. So we might implement a SqlBlogDataProvider, a MySqlBlogDataProvider, a FireBirdBlogDataProvider, and so on.

This sounds great in theory, but it breaks down in practice because unlike the API to the MembershipProvider, the API for a BlogDatabaseProvider is going to change quite often. Pretty much every new feature one can think of often needs a backing data store.

Everytime we add a new column to a table to support a feature, the impact of that change is multiplied by the number of providers we support. I discussed this in the past in my post entitled Where the Provider Model Falls Short.

Every Design Pattern comes with inherent tradeoffs that we must be aware of. There is no silver bullet.

The key here when looking to apply patterns is to not follow a script for applying patterns blindly. Look at what changes often (in this case the database schema) and figure out how to minimize the impact of that change. In the above scenario, one option is to simply punt the work of supporting multiple databases to someone else in a more generic fashion.

For example, using something like NHibernate or Subsonic in this situation might mean that a schema change only requires changing one bit of code. Then NHibernate or Subsonic is responsible for making sure that the code works against its list of supported databases.

One might object to these approaches because they feel these approaches cannot possibly query every database they support as efficiently as database specific SQL as one would do in a database specific provider. But I think the 80/20 rule applies here. Let the dynamic query engine get you 80% of the way, and use a provider just for the areas that need it.

So again, this is not an indictment of the provider model. The provider model is extremely useful when used appropriately. As I mentioned, the Membership Provider is a great example. But if you really need to support multiple databases AND your database schema is succeptible to a lot of churn, then another pattern may be in order.

comments suggest edit

This one is probably old news to many of you, but I just recently ran across it. Every time I want to add a new control to a new page, I get annoyed because I have to remember that annoying syntax for registering a control.

Let’s see…how does it go again? Do I have to add a TagName attribute? No, that’s for user controls. Hmmm, forget it, I’ll just dynamically add it! Well in the interest of reducing future angst, here are two examples of the syntax, one for a custom control and one for a user control.

<%@ Register TagPrefix="st" Namespace="Subtext.Web.Controls" 
  Assembly="Subtext.Web.Controls" %>
<%@ Register TagName="SomeControl" TagPrefix="st" 
  Src="~/Controls/SomeControl.ascx" %>

The first one registers the tag prefix st with the Subtext.Web.Controls namespace in the Subtext.Web.Controls assembly. The second one registers the tag name SomeControl with the user control SomeControl.ascx

Add this to the top of your page or user control and you can reference a control from this assembly like so:

<st:HelpToolTip id="blah" runat="server" HelpText="Blah!" />
<st:SomeControl id="foo" runat="server" />

A most helpful tooltip!

Fortunately, starting with ASP.NET 2.0, we can register a tag prefix within the Web.config file. This basically makes all the controls within that namespace and assembly available to all pages without having to add that ugly Register declaration.

        <add assembly="Subtext.Web.Controls"
                tagPrefix="st" />
        <add src="~/Controls/SomeControl.ascx"
                tagPrefix="st" />

Thanks to the ASP.NET 2.0 MVP Hacks book for this one.

comments suggest edit

Blue Skies from
Stock.xchng We’ve all been there.  Your project stakeholder stands in your doorway with a coffee mug in hand and asks for one more teeny tiny change.

Yeeeaaah. It’d be great if you could just change the display to include the user’s middle name.  That’s pretty easy, right?

No problem!  Let’s see.  I’ll just need to modify the database schema to add the column, update several stored procedures to reflect the schema change, add a new property to the User class, update the data access code to reflect the new property, and finally update the various user controls that render or take in input for this information.

That’s quite a number of changes to the codebase for one measly little change.

The goal of good software design is to minimize the impact of changes in the code.  Many of you might be having the same reaction to this that you would if I just told you the sky is blue.  Well no duh!  Even so, I think this bears repeating again and again, because this principle is violated in subtle ways, which I will discuss in a follow-on post.

This is one reason that duplicate code is considered such an odoriferous code smell.  When a snippet of code is repeated, a change to the code affects every location in which that snippet is located.

Many Design Patterns focus on minimizing the impact of changes by attempting to look at what varies in a system and encapsulate it

For example, suppose you develop a class that monitors the power level of your Universal Power Supply (UPS) device.  When a power level change occurs, several UI widgets need to be updated.

A naïve implementation might have the UPS class keep a reference to each widget that needs to be updated and directly makes a call to various methods or properties of each widget to update the widget’s state.

The downside of this approach becomes apparent when you need to add a new widget or change a widget.  You now need to update the UPS class because of changes to the UI.  The UPS class is not insulated to changes in the UI

The Observer pattern addresses this issue by changing the direction of the dependency so that the UPS class (the observed) does not have direct knowledge of the UI widgets (the observers).  The widgets all implement a comment observer interface and the UPS class only needs to know about that one interface.  Add a new widget and the code for the class does not need to be updated.  Now the UPS class is insulated from changes to the UI.

Another example of code that is not resilient to change is a class with several methods that contain a similar switch statement.  Going back to the example of the UPS class, suppose the class has several operations it must do every few seconds.  But how it implements each operation is dependent on the current power state.

A naïve implementation might have a switch statement in each method that contains a case for each possible power state.  The problem with this approach is that when we need to add a new power state or edit how an existing state behaves, we have to update multiple existing methods.  The State pattern addresses this problem by encapsulating the behavior of a state in a class.  Thus each power state would be encapsulated in a class and the UPS class would simply delegate calls to its member state instance.

So where is the downside in all this? Seems like these patterns provide a win-win situation for us.  Well in these contrived examples it sure does, but not in every situation.  When used improperly, a pattern in one scenario can actually increase the impact of change in another.  Stay tuned.

comments suggest edit

Subtext Logo I put up a short three question survey on the Subtext website.  If you are a subtext user or thinking about using Subtext, please take a moment and fill it out. It’ll only take a second.

For the first question, if you choose Other, please be kind and comment on this post what that other hosting provider is. Thanks!

The third question bears a bit of explanation.  If you use the web admin to enter a blog post, the Advanced Options allow you to enter a short excerpt for the blog post.  My hunch is that nobody uses it.

This excerpt is displayed when listing posts within a category.  I think we might be better served by having some means to automatically generate an excerpt OR to allow users to mark up their blog posts when using a 3rd party tool so that we can extract an excerpt.

After I get in some results to this survey, I’ll talk about that some more.  Over time I may add some more questions.  So please, if you have a moment, take the survey.

comments suggest edit

Telligent, the hyper-caffeinated people behind Community Server, have just released their latest online service, blogmailr.  This service allows you to post to your blog via email.

Leading up to the unveiling, the Telligenti have been a bit coy about keeping this product shrouded in secrecy.  But with a name like blogmailr, removing the last “e” isn’t quite obfuscating enough to prevent anyone from guessing. ;)

We’ve been working on a mail-to-weblog feature for Subtext, but if you can’t wait for that, you can always give blogmailr a try.  I gave it a quick try and it does indeed work with Subtext. One nice feature of the service is that they support Really Simple Discovery (RSD), popularized by Windows Live Writer, so it is easy to configure.

If you are a fanatic about clean valid semantic markup, you should consider sending your email using plain text.  All the markup gunk that Outlook puts in for rich text emails will make your validator’s head spin.  After posting this very blog post with BlogMailr, I went in and cleaned up the markup.  I’m just anal that way.

If this service takes off, beware of spammers attempting to email random blocks of * email addresses.  That would be a particularly effective SPAM attack if they were able to post to your blog via email.

Fortunately, the service generates a random email address used to post to your blog.  It also allows you to specify which email addresses it will accept as a from address.  So a spammer would have to spoof your allowed address and guess your blogmailr address.  Not easily accomplished.

One downside of this approach is it may be difficult for users to remember the generated blogmailr email address. 

Published with BlogMailr

comments suggest edit

Rob Conery just announced the 2.0 release of the Commerce Starter Kit. If you are planning to build a commerce site, it is well worth checking out as it is a full featured modular application built on top of ASP.NET 2.0 and Subsonic.

I know Rob and his team worked hard to get this release ready and Rob especially has poured his heart and soul into the project, building a thriving community around it in that time.  Congratulations to everyone involved!

This good news is tempered by the news that Rob’s father passed away recently.  The announcement of the CSK release includes a heartfelt dedication to his father. I wish you and your family well Rob.

comments suggest edit

Source: Total world domination has always been a small goal of mine. It’s not that I have an unquenchable thirst for power, it just sounds like a challenging goal and I love challenges.

It’s why I got into software development really. I figure he who controls the computers, can control the world.

But lately, all this involvement in running a company and an open source project leaves me very little time for plotting.  I realized I need help.  I need a minion. Someone to help me carry on my work.  Another Haacker.  So my wife and I hatched a plan and the incubation process has begun.

Sometime in June, the incubation period ends and phase 2 will begin as we add another member to our domination plans.  A baby Haack.

Initially, this new Haacker will actually slow our progress in achieving world domination as we put him or her through an orientation period.  It will take some time to get up to speed on essential skills such as speech, motor coordination, and control over bowel movements - which we’ve dubbed milestone 1.

But soon enough, our progress will increase as the newest member of our plot infiltrates the school system sowing the seeds of revolution. 

Yep, good times are ahead.

code, open source comments suggest edit

I have a few great openings available that I have to share with you.  These are really great positions working with really interesting and smart people all over the world. 

Some of the outstanding benefits include:

  • Work from home.
  • Work for a great project lead (me!).
  • Work with an international team of really great developers.
  • Work on a product used by thousands of people and seen by many more.
  • Work tasks are pretty much self-directed.  Nobody is looking over your shoulder.
  • Set your own schedule and work at your own pace.
  • Interesting cutting-edge ASP.NET work. 
  • Gain great experience working on a product and increase your marketable skills.

Some of the interesting projects include:

  • Building a next-gen plugin architecture.
  • Localization and Internationalization.
  • Advanced Skinning architecture.
  • General application architecture and API improvement.
  • Streamlined UI design using AJAX.
  • Advanced Bayesian statistical analysis coding.
  • Windows CardSpaces and Infocard.

For such a great opportunity, I must warn you about a few of the downsides.

  • No health care
  • No Pay

I have a gut feeling that second downside is going to be a bit of a deal breaker for a lot of people.  But did I mention you can work as many or as few hours as you wish?

As you probably figured out already, I am attempting to recruit people to contribute to the Subtext Project.  I have to cut down the number of hours I put into the project for the rest of the year and into the first couple months of the next year for a couple reasons I will mention later.  I’ll still be heavily involved, but won’t be able to contribute as much code as I have been.

Why Accept No Pay For Work?

I’ve written on this a bit before.

In truth, there are many reasons people work on open source software, and they are not all the same. Many just find it fun to work on something more interesting than the boring data-in data-out systems they build at work. Some want to have a hand in building a better mousetrap. Many enjoy participating in a community and perhaps gaining a bit of recognition among their peers.

However there is another angle I want to promote.  It can help you get a better job.  On one hand, it helps you get experience in skillsets you might not otherwise exercise at your current job.

37signals, that über hot company right now, see Open Source contributions as a great way to judge a potential candidate.

Open source is a gift to those who need to hire technical people. With open source, you can track someone’s work and contributions — good and bad — over a lengthy period of time.

That means you can judge people by their actions instead of just their words. You can make a decision based on the things that really matter:

In fact, they only hire people they know through open source. Most companies aren’t that extreme, but trust me, it’s a serious turn on to a potential employer.

Hmmm… I’m Intrigued. What Do You Need?

Great!  Glad you asked!

We have a great team as it stands, but we can always use more help in any and every area.  Everyone is welcome to contribute to anything. Even so, I would like to have a few people step up and become responsible for a few areas.  That person doesn’t necessarily have to do the work in the respective area, but just take ownership of getting people to contribute and get it down.  Basically I want to decompose Subtext into multiple smaller projects. Here are some of our needs: 

Documentation Guru:We need someone to be in charge of documentation.  This would include making sure our project website is up to date.  It can also include generating NDoc documentation and posting it on our site, etc…  In fact, it’d be nice to have our project website redesigned to be a better resource for Subtext users.

Build and Deployment Master: Right now, this has been handled by a combination of Me, Steve Harman, and Simone (Simo) Chiaretta.  Unless Simo wants this title, I think it’d be nice to have one person be responsible for maintaining our Continuous Integration and built scripts as well as our final deployments.

QA Manager and Testers: We need more people to help out with QA before we release builds, but one person to manage this process. I have someone in mind for this, but I thought I’d put it out there since we could use more help in this area.  This would include helping us increase our unit test coverage and start getting WATIR tests going.  After the last error plagued release (my fault), I want to get more serious in this area.

Developers, Developers, Developers: And of course, we need more developers!  We have several ongoing mini-projects we could use help in.  Simo is chugging away on our new plugin framework, but I know he’d appreciate some help as well as someone to start writing some initial plugins to deploy with Subtext.

I have a new skinning architecture I want to get in place, but I won’t have the time to implement it, though I can describe it to anyone who will listen in great detail.

Robb Allen is working on our new membership provider.  I’m sure he’d love to have some help finishing the integration with Subtext.

And there are many more minor tasks that we’d like to get done such as general bug fixes, feature requests, code refactorings.  I’d like to clean up some of our data access architecture to streamline it a bit. Maybe even evaluate using Subsonic.

Wrap Up

To be clear, any and all contributions are worthy and helpful.  I may be ambitious asking for volunteers to take on these specific management roles, but it’s worth a try.

However, if you have some time to contribute, but don’t want to take on a management role, don’t let this dissuade you.  For example, if you don’t have time to help manage the QA process, but can do testing for a bit here and there when we are preparing a release, please jump on in! 

If you are interested in joining in the efforts, start by subscribing to thesubtext-devs mailing list and we’ll get you going.

comments suggest edit

A while ago I mentioned that my company, in collaboration with Shepard Associates built two websites for a medical device company using DotNetNuke (aka DNN).

Well we just learned that the client’s websites we built won three WebAwards from the Web Marketing Association. Cool!

I will admit, I don’t know much about the association or their awards.  It just feels good to see our hard work (by our I mean everyone involved including the client and Shepard) recognized.  Especially considering the torturous work it took to coerce, fight, and bend DNN to obey our will in order to implement these sites.

comments suggest edit

I love Halloween because it makes for great parties. What makes a party great? Costumes! This past weekend I drove up from Los Angeles to San Jose to stay with my friend Kyle and his wife Cara. That evening we drove up to San Francisco to attend a house party in a large four story house in the hills. It was about the perfect size for the 100+ people in attendance, and they hade a few top-notch local DJs spinning.

Unfortunately, I didn’t bring my camera so I had to rely on “donated pics”. I usually am a total shutterfly. Of course, not having my camera gave me more time to enjoy the party. Click on a pic for a larger version.

Hunter S. Thompson and Noam
Chomsky Group of people outside the

My buddy Kyle went as Noam Chomsky, which was a hit of a costume, as simple as it was. It’s rather freaky to see a black and white 2-D face on a normal body. The face looked almost disembodied. The first pic with Hunter S. Thompson just cracks me up. What a literary powerhouse! I went as the thoroughly unimaginative “Kung-Fu guy”. The necklace I’m wearing is made up of miniature skulls of my enemies. Yes, I fight those crazy Lilliputians.

The DEA agent was probably one of my favorite costumes, just for the chill he sent down the spine of some partygoers.

Steve Irwin, the Crocodile

As expected, someone with showed up in a Crocodile Hunter costume, though I don’t understand the red wig. We also had Cartman and his favorite snack in attendance.

Inside the
party The

Finally, the governator showed up, but he was inappropriately dressed. I had to black out that portion of his costume. Let’s just say, it took a pretty big circle to black that portion out. Good going Guv’nor!

Pimpula and

The beaker costume above was one of my favorites.  She and her boyfriend both were beakers. Apparently the sight of them making out was disturbing.  That’s my friend Jason aka Count Pimpula on the right with his girl Rocel.

comments suggest edit

Jeff Atwood points out several problems with using blacklists (specifically Akismet) to prevent comment spam.  He makes the following point:

The core problem is relying on a single method of defense against spam.

Absolutely.  Subtext employs several measures against comment spam, mostly of a heuristic nature.  The latest release adds Akismet support as well as Visible and Invisible CAPTCHA.

The funny thing about CAPTCHA and especially Invisible CAPTCHA is the number of people who claim it won’t work and is broken. As Jeff points out, this may be true among researchers, but it is not the case in the wild.  However let me back up his post with some numbers.

For the past four days, I have not emptied my blog spam folder, just to see what gets put in there.  So far, in that time, my blog has received 1441 comments, trackbacks, etc…  Of those, 1407 have been flagged as spam by Akismet or marked as spam by me.  Of those, only one was a comment.  The rest were trackbacks/pingbacks.

So as far as I am concerned, Invisible CAPTCHA is working well so far.  And it has the benefit of being usable, assuming you can do simple math.

So assuming that CAPTCHA, for now, is the best approach to fighting comment spam, we need to deal with its critical weakness.

The real problem is how do we enable CAPTCHA for trackbacks?

I wrote about this problem before when I discussed my qualms about CAPTCHA.

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.

I mentioned this to Atwood who pointed out that trackbacks and pingbacks are indeed automated, but they are left on behalf of a user.  This is true.  When I write a blog post, Subtext will look at all the links in my post and attempt to trackback each one for me.

Unfortunately, the trackback and pingback APIs have no facility for dealing with CAPTCHA. Unless there were a community effort to revise these specs (I would be happy to join in), CAPTCHA for trackbacks and pingbacks are not gonna happen.

Even with such a community effort, implementing CAPTCHA for trackbacks is going to be a lot of work for blog implementers.  In part, this is indicative of a usability issue with CAPTCHA based approaches.  CAPTCHA requires human intervention.  This makes integrating CAPTCHA with something like trackbacks hard work, whereas if someone comes up with a better automated filter, integrating that is easy.

So for the time being, we have two choices.

  1. Abandon Trackbacks/Pingbacks
  2. Find better ways to filter trackbacks and pingbacks.

I know many have decided to simply abandon trackbacks.  I understand this choice, but I personally am not ready to throw in the towel just yet.  Trackbacks can and do add a lot of value to discussions that occur via blogs.  So far, Akismet has allowed me to reclaim trackbacks.

What is the next step? Well I agree with Jeff:

Akismet is a fine addition to our anti-spamming toolkit. But that doesn’t mean it’s a good idea to outsource your entire anti-spam effort to a single website, either. Anti-spam security starts at home. For best results, use defense in depth and combine local anti-spam measures, such as CAPTCHA, with Akismet as a backup.

Though I think we need to start working on some better non-CAPTCHA filters to combine with Akismet.

comments suggest edit

Steve Harman points out another bug in the upgrade process.  I feel really terrible that this one slipped through, though as far as I can tell so far, it may be mostly cosmetic in its effect.  It apparently converts some comments to trackbacks.  While this is not desirable behavior, since we show both comments and trackbacks in the comment section, it might not have a huge negative effect.

Unfortunately, I am in San Jose right now, so I can’t update the release with a fix yet. Hopefully Steve can handle it. Smile

comments suggest edit

In the essay entitled Hold the Mayo, 37signals points out the obvious fact that most surveys ask users what features they want added to a product.  They rarely ask what features they want removed.

I have in the past asked users for permission to remove features, but I’ve never taken the extra step of asking users, which features would they like removed.  So here I go. 

Which feature(s) would you like to see removed from Subtext?

I think a natural response I will receive is the question, Why would you ever want to remove a feature?

Features have many hidden costs.  Even relatively simple features.  I am going to tell a short story about one such occurrence that happened in Subtext.  I won’t name names and this is not meant to call anyone out for public embarrasment or chastisement.  I have probably been more guilty of this than anyone.

Not too long ago, someone checked in a control into Subtext that displays recent comments, just as we were close to preparing a release.  I was a little miffed at the time because I was not expecting anyone to add features at the time.

However this was a pet feature and some of the devs really wanted it in the system.  Besides, it’s such a small feature, what could go wrong?  So I let it in not wanting to be a hard-ass about it.

The recent comments control is a pretty simple control in concept.  When added to the main skin template, it simply displays recent comments left on various blog posts.  Sounds simple, no?

However, as with any feature, the devil is in the details.  Several problems immediately became apparent as we tested the release, and one problem affected me in a later release.

  1. The control let you truncate comments after a certain number of characters.  However it didn’t strip HTML out, so if it truncated a comment in the middle of a tag, it would completely mess up the whole page.
  2. The control bypassed our provider model and made stored proc calls directly.
  3. The control displayed all feedback for a blog, even ones that were left via the Contact Page, thus potentially displaying private messages.
  4. Recently, I tried to append a little HTML comment after messages that had been processed by Akismet to make it easy to see that Akismet was indeed working by viewing source.  Comments aren’t being stripped by the Recent Comments control, so the site was broken in a way that I had not anticipated. It took a long time before I realized what was happening.

So for a relatively small feature, a lot of development time and effort was used up in supporting it.  I am glad we have the feature now, I really like it and plan to add it to my own blog at some point.

But the main point still stands, every feature is like an iceberg. When scoping it out in your head, you typically only think of the top part that sticks out above the water.  However, the real effort is in the part under water that supports the whole thing.

Iceberg Photo from

So if there is a feature in your product that provides very little bang for the support buck, consider getting rid of it.

So again, Which feature(s) would you like to see removed from Subtext?

comments suggest edit

Real Now that 37signals have put their book Getting Real online for free, I’ve finally gotten around to start reading it. And so far, I **love it. I think there are a lot of great lessons, reminders, ideas in here that will help me make products I work on that much better.

I have a premonition, even before writing this, that a lot of people will tell me that the book is crap because they don’t believe in functional specifications and even Joel believes in functional specifications. As the authors point out in their Caveats, disclaimers, and other preemptive strikes page, their techniques don’t apply to everyone (though probably to more people than one would think). Also, their ideas are not an all or nothing affair. Gotta have your functional specs, then by all means keep doing it. Don’t throw out the baby with the bathwater.

Open Source projects are probably in the best position to make use of their guidelines, though I am sure many more projects would be made much better by getting real. Subtext will most certainly gain from many of these approaches. My hope is to use Subtext as a testing ground for the principles in Getting Real, and then using its success to show my clients that they don’t need to be so rigid about product development. We’ll see.

As you might guess, many of my upcoming blog posts will focus on some of these topics.

comments suggest edit

Wallet Well if you’re the punk malcontent who found my wallet yesterday, you go on a shopping spree…at Rite Aid, Vons, and Ralphs (a convenience store and two grocery stores). 

Really, if you’re going to try and spend my hard-earned credit, at least go buy something decent like a stereo system.  There’s a Best Buy just down the street that isn’t much further away than the local Vons.

I mean who spends $103 at a Rite-Aid?!  Who!?  I really mean it. Who? I’d like to know because I want my wallet back.  And to the store clerks there, doesn’t it even slightly ring the suspicion bell in your head when someone tries to buy $103 worth of anything at Rite-Aid?  I didn’t even know you could buy that much stuff at a Rite-Aid. I figured buying out everything in the store would lighten your wallet by about $70, tops.  Maybe he bought the cash register too.

Did you even look at the guy and compare his picture to the Id, my Id, he presented?  There’s not a lot of hapas running around L.A. so I doubt he looked that much like me, assuming it was a “he”.

Anyways, yesterday on a short walk, I dropped my wallet.  A really comical mistake like you see in the cartoons. I put my wallet in my side cargo-short pocket, which unbeknownst to me, had a huge gaping hole in it.  I returned back to the area literally 10 to 15 minutes later, and it was gone.  And then the charges started showing up in my account as I frantically called to close my cards.

Anyways, that’s why I rushed out a new release of Subtext last night.  I figure follow something bad with something good.  Oh, and I bought a lottery ticket.  I know, the chances are pretty much nill. But what were the chances I’d put a wallet in a pocket with no bottom?  This is a chance for the world to get back to even terms with me. Wink

comments suggest edit

As I mentioned in my last post, someone reported a bug with deleting posts in the admin tool.  That posts also describes a quick and dirty workaround.

However, I fixed the bug and updated our current 1.9.2 release at SourceForge.  The existing URL to download the release is still valid.  The full version number for this release is

To find out which version of Subtext you are running, just log into your admin and look in the bottom left corner.  You should see the version there.


If you downloaded Subtext 1.9.2 before I applied the update, then you will probably see version in there.  If so, you have two options.  You can either apply the quick and dirty patch mentioned in my last post, or you can download the latest build and update the Subtext.Framework.dll file in the bin directory.

However, this latest update also includes some CSS fixes for the KeyWest skin in IE 7 designed (and fixed) by Robb Allen.  So if you are using that skin, you should download and apply this update.

comments suggest edit

Someone reported that they cannot delete posts in the just released Subtext 1.9.2. I am mortified that we do do not have a unit test for this function!  To our defense, we did start with 0% code coverage in unit tests and have now reached 37.9% and rising!

I have a quick fix if this problem affects you. I am also currently building a more permanent fix which I will release soon.

Run the following query in Query Analyzer (don’t forget to hit CTRL+SHIFT+M to replace the template parameters before executing this).

    @ID int
    , @BlogID int = NULL

DELETE FROM [<dbUser,varchar,dbo>].[subtext_Links] 

DELETE FROM [<dbUser,varchar,dbo>].[subtext_EntryViewCount] 

DELETE FROM [<dbUser,varchar,dbo>].[subtext_Referrals] 

DELETE FROM [<dbUser,varchar,dbo>].[subtext_Feedback] 
WHERE EntryId = @ID

DELETE FROM [<dbUser,varchar,dbo>].[subtext_Content] 


TO [public]

Sorry for those that this affects. Like I said, we’ll have a bug fix out soon.

comments suggest edit

Making the world safe for trackbacks again!

UPDATE: A bug was reported that blog posts could not be deleted. We have updated the release files with a fixed version. There’s also a quick and dirty workaround.  You only need to apply the fix if you downloaded and installed Subtext before this update message was posted.  See here for details.

Well it took me a little longer than anticipated, but I finally teased out the remaining show stopper bugs and put the finishing touches on Subtext 1.9.2. If you plan on upgrading to Subtext 1.9.2, please consider reading this entire post carefully. If not, at least remember to backup your database and site first before upgrading!

What happened to 1.9.1? Long story for another time. We are skipping from 1.9 to 1.9.2. Here is a list of releases so far just in case you were curious. UPDATE: I had only listed the versions that required an automatic upgrade. Here is the complete list with the ones in bold required an automatic database update:

  • Subtext 1.0
  • Subtext 1.5
  • Subtext 1.5.1
  • Subtext 1.5.2 
  • Subtext 1.9
  • Subtext 1.9.2

As you can see, our version numbering has been a bit less than consistent. But starting with 2.0, we should stay a bit more consistent. With the launch of IE 7, don’t be surprised if we come out with a 1.9.3 version that just includes fixes to the skins. Volunteers for that effort are welcome.

New Features

The reason we call this the “Shields Up” edition is that the focus has been on dealing with Comment Spam. I had few interesting ideas I could not implement in time, but I did implement three key features that have really made a huge difference in regards to comment spam, and may not necessitate my other ideas.

Akismet Support

Subtext now has full integrated support for Akismet spam filtering. Not too long ago, I released an Akismet API so that others can use it for spam filtering. Scott Hanselman Members of the DasBlog team implemented it for DasBlog (in their source control tree. Not yet released.) and has nothing but praise for it, saying it has completely eliminated comment spam for him.

Now let me explain what I mean by full integrated support. When you enable Akismet and are looking at approved comments in the feedback section, if you notice something that should have been filtered as spam, you can check it and click the Spam button. That will then report a false negative to Akismet, indicating that they failed to mark this item as spam and then move the item to the new Trash folder.

Likewise, if you notice an item that is flagged as spam, but should not have been, you can check the item and click Approve which reports a false positive to Akismet. By doing so, you will be training Akismet to become more adept at filtering spam.

Note, to enable Akismet, you must sign up for anAkismet API keyand supply that key to Subtext via the admin section. In order to get this key, you have to register for a user account, whether or not you plan to use a Wordpress blog.

Another note, Akismet may not work in Medium Trust scenarios. So if you host your site with a hosting provider such as GoDaddy, WebHost4Life, etc… who run their sites in Medium trust, Akismet might not work for you. I wrote about the problem here.

In the comments to the aforementioned post, Scott Watermasysk points out a promising approach. Have your hosting provider set up a proxy which can be used to make requests. To that end, I did add some Web.config appSettings for enabling proxy support: ProxyHost, ProxyPort, ProxyUsername, ProxyPassword.

If specified, all web requests for subtext will use this proxy.

Invisible Captcha

Not too long ago, I released a lightweight invisible CAPTCHA validator control. As far as I can tell, it has worked pretty well at keeping out automated comment spam. But as I also warned, it did nothing to stop Trackback/Pingback spam. Hence the need for Akismet support.

Invisible CAPTCHA is probably not necessary given the Akismet support, but since Akismet is not enabled by default and Invisible CAPTCHA is, it will provide some relief until you get your Akismet API key, if you so choose.

If Invisible CAPTCHA causes problems for you for some reason, you can turn it off for the entire site (no per-blog setting) via Web.config.


Some of you twisted my arm, so I am complying. I love my users so I gotta keep them happy. We now have support for the standard visible CAPTCHA you know and love.

Time Zone Fix

This topic gets its own special treatment because it is both a bug fix and a new feature. Not too long ago I mentioned some work I did for DasBlog regarding daylight savings time.

Long story short, there was no way in the .NET framework to convert from one arbitrary timezone to another. The way this manifested in Subtext is that users who had their blog hosted in another timezone always had incorrect timestamps on their blog posts.  This fix resolves that issue.

Previously, Subtext only stored a timezone offset for each timezone. For example, the offset for Pacific Standard Time (PST) is -8. However that is not accurate enough to be correct. For example, at the time of this blog post, the offset is actually -7 because of Daylight Savings Time.

Likewise, choosing -7 for a timezone is not accurate because Arizona does not observe daylight savings, but Mountain Time does. The only way to really get this accurate is to store the actual timezone and not just the offset. So that is what Subtext now does.

We now list every timezone (at least every one I could extract from the Windows XP registry). There is no natural integer identifier for a timezone, so based on a suggestion by someone at Microsoft, I used the hash code of the timezone’s Standard Name. This is the only guaranteed consistent unique identifier for a timezone. So when you upgrade to 1.9.2, we try and update your offset to the most likely timezone id. Obviously, we can’t be perfect about this, so after you upgrade, you probably want to login and configure your blog with the correct timezone if we chose unwisely.

Other Release Note Items

  • Feature [1565237] Support referencing External CSS
  • Feature [1577073] Flag All, Destroy All options
  • Fixed [1584075] Disabling trackbacks didn’t disable trackbacks properly.

Upgrade Instructions and Warnings

If you are upgrading from Subtext 1.5 or below, then please read this important note on upgrading. Subtext 1.9.2 runs on ASP.NET 2.0, so upgrading from 1.5 and below(which ran on ASP.NET 1.1) takes a few additional steps than normal.  As always, don’t forget to merge your web.config customizations into the new web.config file.

Also, for all people upgrading to 1.9.2. The Subtext 1.9.2 upgrade process performs a major database schema change, moving all comments and trackbacks into a new subtext_Feedback table. We’ve tested this over and over again working out all the kinks we could find, but we can’t guarantee that it will be 100% perfect. Thus backup your database first before upgrading!

Also, as I mentioned in the previous section, after you upgrade, please check the timezone setting in the admin section.

Lastly, in order to improve the commenting experience, we’ve added a tiny dash of Ajax using the MagicAjax control for leaving comments on a blog. However, this does not work well with the excellent ReverseDOS because ReverseDOS acts before the request is passed on the our code. If you plan on using Akismet, it is recommended that you turn off ReverseDOS support in the Web.config. In fact, the web.config file that comes with 1.9.2 disables ReverseDOS. I’ve been in contact with the ReverseDOS creator, Michael Campbell, about these issues and he has plans to work together to address them. But like all things, life comes first.

Even though Akismet does a great job with comment spam, I still think ReverseDOS is worthwhile and a nice complement to Akismet. However, we need tighter control of when ReverseDOS is triggered in the request pipeline in order to integrate it into Subtext’s existing spam filtering functionality.


Ok, enough talk already, where do I download this sucker?  The download is hosted by SourceForge here: [DOWNLOAD]

comments suggest edit

Dan Appleman takes the .NET focused custom search engine idea one step further by grabbing a great domain name for his search engine.  Now why didn’t I think of that!  Sometimes that’s all it takes between a search engine that will get used alot (his) and one that won’t (mine).  I’d be happy to throw my support over to his, though I’m keeping my baby.  The one key difference is I plan to leave my search engine open for others to contribute sites.  Perhaps the two will complement each other.

And no, the domain name is not the only reason. This guy *is* Dan Appleman after all! He wrote the Visual Basic Programmer’s Guide to the Win32 API which allowed me to feel like a real programmer despite using VB back in the day.  For this, I owe him a debt of gratitude as I was thus equipped to stand up to the C++ snobs.

Now we just need someone to help spruce up the logo.

Tags: Custom Search, Search, .NET, Dan Appleman