Out-Cleverring Yourself

Have you ever hacked a product to do something it wasn't intended to do in order to "simplify" things for your users and have that blow up in your face? This is an account of my experiences doing just that with MS Reporting Services.

If you've used Reporting Services at all, you'll know that there are two virtual directories that are created on IIS when you first install it to a server: ReportServer actually serves up the reports by passing the requested data to external applications via whatever protocol you have configured and Reports (aka ReportManager) which serves as a user interface for reports on the server.

ReportManager is, by far, the most visible of the two. Using ReportManager, you can organize your reports and data sources and set permissions on who can view and change them. Often, this is enough of a user interface that it is a viable deployment mechanism to simply point your users to the Url for a report under the ReportManager directory.

Ingenious people, like myself, will often designate a new Web instance on IIS just for Reporting Services, generally naming it something clever like "reports". When you do this, it is very tempting to point the root directory of that web instance to the ReportManager directory. This means that you can point your users to "http://reports/Invoice" instead of "http://reports/Reports/Invoice". You can see why you'd want to make this change (assuming you are similarly obsessive).

There is, however, an unintended side-effect to this change. Once you do this, your ReportServer will begin throwing errors if you ever decide you want to use direct Url access to display reports. Not a lot of people do this unless they're using a Reporting Services "Integration" that uses this functionality for showing reports. Personally, I ran into this situation when trying to tie our Reporting Services forms into Great Plains. Since Great Plains integrations (both native and third party) expect to use ReportServer for the report display, I was shocked and dismayed to find myself staring at this error:

The type Microsoft.ReportingServices.UI.WebControlConnection, ReportingServicesWebUserInterface, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 does not implement IReportServerConnection or could not be found

Hitting Google for this error is more than a little bit frustrating because the vast majority of issues where it shows up is in mis-configuring a ReportViewer object in an ASP.Net web application. This is manifestly not the case here.

This was a dome-scratcher for a lot longer than I like to admit. All of the advice pieces that I could find for this error are simply inapplicable. Fortunately, I ran across a Usenet post by Dave Green from last July. Dave reported that he could fix ReportServer if he removed a couple of settings from the <appSettings> section of the ReportManager. This is most odd because the two simply shouldn't be linked--at least, in that direction. If they were linked, I'd expect that link to go the other way (i.e. something in ReportServer breaking ReportManager). After all, ReportManager is essentially a UI on top of ReportServer.

I tested his findings and sure enough, removing the appSettings fixed ReportServer for me. Since removing those settings broke ReportManager, this was an unappetizing solution.

It was then that I remembered that IIS does an interesting little trick with web.config files and subdirectories. You see, each subdirectory on a website inherits the configuration from parent directories (also machine.config, but that's not relevant to our story). It doesn't matter how those directories are physically arranged, what is important is that IIS uses the Url to determine inheritance. So in a situation where "http://reports" is pointing at ReportManager and "http://reports/ReportServer" is pointing to ReportServer, that means that ReportServer is inheriting the web.config settings from ReportManager. Thus, the appSettings for ReportManager are being read by ReportServer and misapplied (I've no idea why the presence of an appSettings entry would break ReportServer)

Fortunately, the fix for this issue is pretty simple. The <appSettings> element includes a spec for a clear directive that will wipe out all inherited values. Adding an appropriate entry to the web.config in ReportServer cleared the problem right up. Here's the section for the curious (and/or lazy):

<appSettings>
    <clear />
</appSettings>

So there you go. I hoisted myself by my own petard. Fortunately, I was able to figure out how to get myself unhoisted. I now have both ReportManager and ReportServer humming along nicely without having to undo my clever directory mapping. Since I haven't been able to find this solution anywhere, I figured I'd post this for those in similar situations.

21. February 2007 21:47 by Jacob | Comments (0) | Permalink

Building Custom SubText Controls

In my response to Jeff Atwood, I mentioned the Technorati widget control that he is now using in lieu of Trackbacks. The Technorati widget is fairly simple to implement, but you have to be able to feed it the permalink for your post for it to be able to work. Since I already had a go at creating custom controls for use in SubText, I decided that now was a good time to see if I couldn't come up with a way to create one that has access to blog entry attributes like the permalink. For your edification, here's what I found.

First Things First

Before we walk this primrose path, lets make sure that we aren't setting ourselves up for hard times later. Create a custom Skin by copying the files from your favorite built-in Skin into a new folder in the Skins directory.

Once you do this, you'll need to let SubText know about your new skin. This is done in the Admin directory by creating a "Skins.User.config" file. The easiest way to do that is to copy the existing Skins.config file and rename your copy to "Skins.User.config". They have the same structure, but having the extra config for user skins allows the SubText team to maintain the built-in skins without messing up any customizations you might be using. Make sure that you delete all the entries for skins (except the one you copied from above). Then you can tweak the template Name and and TemplateFolder attributes (highlighted below).

Once this is done and uploaded to your SubText site, you'll see your skin listed at the bottom of the skin selection drop-down in the Admin pages (that dropdown is not sorted, so the user skins will be at the bottom). Now you can modify to your heart's content and not worry about a future upgrade nailing your customization. Also, if you really mess things up, just hit Admin and set your skin back to your old starting point.

Where Things Are

The controls you'll use to customize SubText are all in the Controls folder in your custom Skin directory.

The most daunting thing about creating custom controls is figuring out how SubText handles things. You can skip the rest of this section if you don't really care and just want to get on with creating your control.

There are a couple of quirks of SubText that can help you isolate any problems that you might encounter.

  1. SubText transfers some of the controls programmatically by grabbing subcontrols of one object and copying them to the controls collection of the parent object. This is most notable for the subcontrol brought in by the PageTemplate for the "contentregion". If you pick apart your PageTemplate.ascx, you'll see a control with an ID of "MPMain". That's where this happens. The reason for this is that SubText replaces MPMain with a different control depending on what kind of page it is trying to display. Which leads to the following problem.
  2. It isn't immediately clear which control is being pulled in by MPMain for different sections of your blog. The main page (default.aspx), for example, pulls the HomePage.ascx control. It took me a while to figure this out, but now that I have, I found that the control that functions as "MPMain" is determined by a group of <HttpHandler> elements in the web.config file (technically, MPMain can be replaced with multiple controls as you can see if you actually dig out your web.config). The HttpHandler elements in web.config describe a RegEx "pattern" attribute to apply to incoming URL requests and then a "controls" attribute that describes the control(s) used. I could have saved a lot of time if I had known this up front (say by asking members of the dev team who are actually quite helpful and would probably have responded pretty quickly, really)
  3. The classes that implement many of these controls use the specific ID of some controls to populate certain attributes--mostly Text. For example, Day.ascx can have a Literal control with an ID of "PostDesc" that is populated with the descriptive elements of a post at run time (e.g. when it was posted, if there are comments, that kind of thing). Some of these are checked for existence (like "PostDesc"), but some aren't (like "PostText"). Which are checked and which aren't makes pretty good sense, though, so I don't see that as a problem. I mean, I want to see an error if my blog is unable to display the PostText of a post.

Creating a New Control

The key feature that you need in order to leverage SubText entry information is a control class defined in SubText called CurrentEntryControl. This nifty little guy has no implementation in any of the default skins, so he's easy to overlook. The key feature he possesses is that if referenced from a control that has a blog entry associated to it, it'll let you use databinding syntax within that control to access the properties of an entry. I'm not going to say how long it took me to figure this out. Let's just be happy that I have...

The Technorati embed needs your fully qualified URL (i.e. the full-on web address to reach your post--the one you'd paste into a browser's address bar to reach just that post), so the best databind expression that I found for my purposes is:

<%# UrlEncode (Entry.FullyQualifiedUrl) %>

Believe it or not, this actually works. UrlEncode is defined in the CurrentEntryControl object along with the Entry property. Again, don't ask how long it took me to figure all this out. Just for kicks, here's the Entry class properties:

Since I'm going to want the Technorati embed to function in a couple of different page contexts, I think the easiest way to implement it is to put the script in its own control. This will be pretty simple for just the Technorati embed script:

<%@ Control Language="c#" Inherits="Subtext.Web.UI.Controls.CurrentEntryControl" %>
<script src="http://embed.technorati.com/linkcount" type="text/javascript"></script>
<a class="tr-linkcount" href="http://technorati.com/search/<%# UrlEncode (Entry.FullyQualifiedUrl) %>">View blog reactions</a>

That should be generic enough to function in any blog. I've put it together for you to download here if you want to save yourself some copy and pasting.

Placing The New Control

Now that we have the control we're going to use for Technorati's embed, you have to decide where you're going to use it. For me, I decided to use it both for posts on the blog's home page and for the individual posts themselves. After digging around, I decided that the best candidates for the new control were Day.ascx and ViewPost.ascx. Day is eventually referenced from the home page and ViewPost is referenced when displaying a post from archive (i.e. permalinked pages).

For the control to work, you'll need two things: a Register tag and the control tag. The Register tag lets the parent control know where to find your custom control. The control tag lets it know where to display it. My new ViewPost.ascx looks like this (my changes are highlighted):

Save those changes, upload to your server, and you're done. Just because I can, I included both of the modified controls in the download I referenced above. Pop all three into the Controls folder of your custom Skin and you should be good to go. Note that I pulled both of those controls out of the "Piyo" built-in skin, so it's very possible that they may differ from the controls in other skins.

22. December 2006 05:30 by Jacob | Comments (2) | Permalink

Hacking SubText

Okay, I've had SubText up and running for a week and some now, so naturally it is time to tinker. In poking around, I'm not as happy with SubText's integration model as I was with DasBlog's. DasBlog exposed a number of "macros" that you could use to insert certain internal values into your own stuff. I kind of liked their model because once you registered your own code, you could reference your macros from DasBlog stuff as well (bear in mind that I didn't actually implement DasBlog, so my impression could be off).

That said, as long as you don't need to access things like entry links or other internal blog workings, it's pretty easy to modify a "Skin" in SubText to add new stuff. SubText reads a main ASP.Net Web User Control called PageTemplate.ascx that holds essentially the format for the other Web User Controls that a SubText site uses. Which subcontrols are used are pretty much determined by this PageTemplate control and the controls it references. This structure made it pretty easy for me to add a new embed control I wanted to play with.

The Technorati Embed

There are a number of sites that offer badges, embeds, and other tools intended for their users to simultaneously spruce-up their own sites and provide free advertising for the service involved. The Technorati search feature on the right is an example and was my first foray into modifying my blog template. With Technorati, the tag I need to use to expose a Technorati search link is this:
<script src="http://embed.technorati.com/embed/xw5vkfrkzb.js" type="text/javascript"></script>

To pop it on there, I just added that code right in the PageTemplate.ascx file where I thought it'd do some good (using a text editor). Straightforward, but inelegant.

The Great Games Experiment Embed

 For my next trick, I wanted to see how SubText would handle it if I mimicked their own structure to create a new control. To do this, I created a couple of things that are way more complicated than they needed to be. I mean, technically, all I needed to do to pop the user embed onto my site is copy the badge tag from their site in the same place I put the Technorati stuff. The tag isn't all that complicated:
<iframe src="http://www.greatgamesexperiment.com/greatgamesbadge/user/Jacob" height="140" width="204" scrolling="no" frameBorder="0" />

Doing so would produce this handy "badge":

But I'm a geek and I wanted to test some stuff out, so instead of simply pasting a bunch of html, I created a .Net project and two custom web user controls. One of the controls creates a user badge. The other control creates a game badge. The result of this project is that you can drag one of these controls onto an ASP.Net page and set the "UserName" property (or "Game" property for a game badge) and it'll take care of the rest. The active html would look like this:
<cc1:GreatGamesUserEmbed ID="GreatGamesUserEmbed1" runat="server" UserName="Jacob" />

Somehow, I managed to get that to work. As long as you've registered my assembly for the right tag prefix, you're golden.

But that wasn't quite enough for me, oh, no. Simply adding that control to PageTemplate would be kind of hokey and out of place, so I went one further. Following the pattern of the other controls, I created a GreatGames.ascx file under the Controls directory and referenced that instead. It's a simple file, but then, most of those Control files are. It creates a section that fits in much better on the right side bar and groups the Great Games badges together more naturally.

And wonder of wonders, it worked first time. Love when that happens.

Wrap-up

SubText doesn't have any documentation that I could find detailing these steps, so making these modifications required mucking about in the code to follow their flow during page delivery. If I get the gumption later, I'll re-write this as a more generic how-to in creating very basic SubText add-ins because the community needs something like this. Also, I saw a couple of interesting objects that look like you can extend them for more complicated things (like controls that access the SubText data, I'll bet). I'm sure that Phil Haack and others could point those out. In fact, they're nice enough folk on their dev email list I'd bet they'd be happy to answer questions if I took the time to ask.

Also, if you want those Great Games controls, download the source code. It's nothing fancy, but does the job. Or, if you just want the binary, download that instead. The source code project includes my GreatGames.ascx file as well so you can see an example of implementing them. Let me know if you do anything interesting with them.

14. December 2006 18:34 by Jacob | Comments (1) | Permalink

Calendar

<<  May 2017  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar