Miguel A. Castro's Blog

# Wednesday, March 31, 2010

You know, it’s funny.  Most of the time when someone sees my home office set up, they’re pretty impressed.  But believe it or not, I still get the occasional idiot who attempts to mock or ridicule me for using so many screens (4 on one rig and 2 on another).  Well, I don’t usually even dignify idiots like that since any developer out there worth the code he (or she) writes understands the benefits of multiple screens, or multiple machines for that matter.

I have however been asked several times recently since my home office blog posting, what do I typically use this many screens for.  So for those that have actually asked nicely, here’s my typical configuration:


The primary rig has four screens, three 24” Dells and 1 20” Dell on top.  The center and right monitor normally hold Visual Studio with the center one being all code and the one the right having a nicely arranged and docked combination of the tool windows I use.


The monitor on the left is open for application execution, browser, or generally anything else I need.


The smaller one up on top usually has my GMail open, but occasionally is used to watch DNR-TV shows.


With Visual Studio 2010, multi-screens are better than ever because I can undock a code window and move it anywhere I want.

As far as the second machine is concerned (the one with two monitors), it’s set up as a gamer rig but I also have it loaded with all the same dev tools that the main rig has.  I do a lot WCF work so it’s great for testing true machine-to-machine messaging.  It also functions to watch DVDs with and to listen to music.


So now you know.  If it actually still bothers some of you or you think I’m excessive, perhaps you should find another line of work and leave development to the geeks.

Until next time…

Wednesday, March 31, 2010 3:57:41 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] - - Follow me on Twitter

# Monday, March 22, 2010

ATTENTION: The first few shots are “Before” images !

In the beginning…

P1010021No, I don’t mean Microsoft Office, I mean the third home office I’ve built from scratch.  The first office was built in the basement room with no modifications made to the room at all.  I can’t really say that it was an actual “build” since it was just my moving equipment into an existing room.  The setup consisted of two workstations and a couple of servers.  I’m almost embarrassed to publish pictures on it but my original intention was to get stuff in the room quickly after moving in then commencing on an actual rebuild.  Well, we all know how those things go.  The cool thing about the setup was that it was my first multi-monitor setup.

P1010022My bookshelf was completely overcrowded since at one time I thought it advantageous to keep ever single book I every bought – boy have things changed.

I don’t even remember the specs on the equipment I had at the time so I won’t bore you with those details.  I will say (as the pictures show) that this was pre-widescreen monitor days and even prior to having the ability to throw multiple video cards in a rig to achieve multi-monitor capability.  In this setup, I used a Matrox card that was specifically designed to handle three monitors.  The downside was that this was the only thing the card can do really well.  It was not meant for high-end graphics or even gaming.  This is why I initially set up two rigs.  The one on the left drive a 21” Samsung monitor which was at the time, the first large LCD that die-hard gamers approved over CRTs.

P1010023 This room had an open closet in it which is where I placed my two servers.  These rigs were hand-me-downs from older workstations.  In this closet I placed my hub and my cable modem.  The rest of the stuff in there is… well, let’s just say it’s a damn mess.  I used a KVM switch so I can control both servers with a single monitor, keyboard, and mouse which I mounted on a wall bracket.

In order for you to get a good image of this “before” scenario, let me describe the room.  It’s about 15 x 13 feet but not totally square.  Two of the walls offset out about a foot.  The carpet was fugly, but at least in good condition.  The room has a drop ceiling which later proved to be a great asset.  Also, the bookshelves were built into the wall and all the walls were standard off-white.


Accelerate to Office 2.0

DSC01260 The second office was basically a complete overhaul of the equipment in the office, but still in the same room with the same configuration.  In fact, this coincided with the building of Superrig, which I fully documented in my old blog

DSC01263This was a water-cooled rig which drive three, and later four monitors.  I also rebuild the second workstation and the servers and added a second monitor to the second workstation.  Under this same configuration, I later replaced all the Samsung 21” monitors with Dell 24” wide-screens.  The mess in the server closet still existed, it just moved to higher shelves.  The office looked like it was cleaning up a bit, but then something started to happen in our household.




Why I hate cats

I’ve never been a cat person, and now I down-right hate the damn things.  If they were edible, I would have chowed this one down years ago.  My cat decided, without any formal announcement to the family, that she no longer liked to pee in her litter box.  Now gentle reader, would you like to take a wild guess as to where she started to use the bathroom?  See the nice shade of light gray that my carpet has?  The corner when you first came into the office was no longer that shade.  It was several shades darker !!!  I’ll save you from having to see that picture.  And color alone doesn’t do it.  There’s a plethora of new fragrances that I enjoyed while I worked every day.  Needless to say it eventually got to the point where I couldn’t stand it.  Why did I wait so long you ask?  Well, it really wasn’t too long, only about a couple of months; during which time I sprayed and cleaned and did my best to get the carpet back to normal, but no joy.  I tried cleaners on the carpet, I tried cleaners on the cat.  I cut the piece of carpet out and came very close to doing to the same to the cat.  That’s it, it was finally time to do what I should have done to that room when I first moved in – start over !


Third time’s a charm

IMG_0374 Luckily, I had just cleaned out and reorganized the garage so I had a place to move all the furniture and equipment (my wife and I have an understanding, the garage and basement are mine and the rest of the house is hers).  I have a killer laptop so I was prepared for that to be my only means of working for a while.  I moved all the equipment and furniture into the garage and unloaded the bookshelf as well.  I tore up the carpet and to my enjoyment I found that there was a layer of plywood underneath.  Why enjoyment you ask?  Because whoever laid it down, did so before putting the drywall up.  And when they did put up the walls, they made sure they came down all the way to the edge of the plywood, which now was about an inch underneath all the sheetrock.  I had to take up all the plywood of course.  I can hear you now.  Why not just leave it so I can lay the next floor right on top of it.  I might have be able to do that except for the fact that cat piss is as potent as hydrochloric acid and it when it decides to start eating its way through something, we can have another China Syndrome on our hands.  Yes folks, that’s right, the cat corner was wet down to the plywood.  Oh, and by the way, I found two other corners that the cat claimed as hers.  I’m sure there are some of you who are now asking if I did anything to try to teach this animal a lesson.  Now, that would probably be illegal wouldn’t it?  Let’s just say that the cat bullets out of whatever room I come into now, so it seems we have an understanding.  Think of it as two magnets with same poles coming together.  As you move one, the other just moves aside and never touches.  She’s really quite an amazing animal.  She can go like five days without eating.

The task of tearing up the plywood was difficult to say the least, but after several hours, a possible hernia, and a slew of Cuban curses that would make my father proud, the floor was nothing but concrete.  That is with a bunch of plywood nails in it; a fact that a found out the hard way by barefooting in one day.

IMG_0392 I also cleaned up the walls after removing the monitor arms.  I had to spackle many holes and imperfections to get things smooth.  I now had the server closet staring at me in the face and this was a fight I was gonna win.  I wanted to open up the room by removing the closet and I did just that.  After tearing the sheetrock off the close walls, I cut up and demolished the beams that made up the closet’s frame.  In doing so, I tore some pretty big holes into the wall which of course I now had to patch.  Fixing sheetrock is not a big deal once you learn how to do it.  Luckily, I’ve had a lot of home improvement experience over the years so this was a task not new to me.  One piece of the wall was so torn up, I had to cut it out more so that I can replace it with a whole new piece of drywall (shown).  The rest of holes nicely accepted wall patches over them.

Let me tell you something about the hole in the wall you see in the picture to the left.  In order to patch a large hole in a wall, you need to cut it larger until it meets the edge of the beams, at least on one side.  I did that fine.  The other side can suffice with a makeshift beam with a piece of 2x4 that sits behind the wall and in the center of the seam.  Sheetrock comes in sheets measuring 4 x 8 feet.  They also have pieces at Home Depot that measure 24” x 24”.  This hole was about 18” high and needed to be cut to a larger width so, which I did.  I cut it to 25” accidentally.  So much for purchasing a small piece of sheetrock which would fit nicely in the trunk of my car.  I had to buy a whole 4’x8’ sheet and cut it down into thirds at Home Depot before trying to get it into my car.

IMG_0402 The bookshelves that were recessed into the wall on one side went down in a T-pattern all the way to the floor and I never liked it.  I didn’t really need all that extra space so I decided to removed those shelves and cover the whole up with sheetrock (shown).

Of course, you don’t build a new office and not pre-wire it up for everything you need.  I decided on a location for patch panel and started running Cat-6 wiring all around the office.  In fact, there are 17 drops in this room alone.  There’s an 18th drop into the patch panel that goes up the drop-ceiling and into a wall plate in my living room.  It worked out great that my entertainment center in the living room was against a wall that came down right into the ceiling area of my office.  I ran one cable up there and put another Linksys access point with a built-in four port hub.  My TiVo, BD-Live blu-ray player, and Logitech Squeezebox are connected to that hub.

IMG_0386 IMG_0382 The wiring took some time as I cut wall plate holes into locations all around the office and snaked wiring from the drop-ceiling area, which now was free of all tiles, down the walls in all the places where I wanted LAN lines.  Shown in one of those pictures is the wall where the closet used to be.  All those little holes are where the wire shelf brackets used to be screwed into.  Of course, I had to spackle all that to smooth it out.







We need more power captain

IMG_0390 OIMG_0388ne of the problems I used to have is blowing the office circuit breaker every time I used the laser printer.  Unless of course, I powered down the second workstation first.  This was no way to live, and hell I was already doing it all myself anyway, why not the electrical work too.  Nothing a couple of extra episodes of “Holmes on Homes” couldn’t solve.  My office is right next to the garage, and in fact shares a wall with it so I went to take apart the circuit panel and see what I had to deal with.  Believe it or not, this task was not difficult.  I was able to run electrical wire (romex) from the panel, up into the wall and out the same wall but at the top where it meets the garage ceiling.  Then I just ran the wire along the ceiling, promising myself that I would one day place a wire track there to hide it.  I ran it to the back of the garage where I made a whole in the top of the wall which came out in the office but inside the drop-ceiling – beautiful.  I ran it down the wall to a new outlet, then back up again and over to another wall where I ran it down into a second new outlet.  I made sure this was 12 gauge romex so I can handle up to 20amps on it.  I found the exact breakers I needed at Home Depot and wired them and clipped then in.  I did this not only with a 12 gauge cable for up to 20amps of power, but also with a 14 gauge cable connected to a new 15amp breaker and going into the office as well.  This one was for what was going to be a new strip of track lighting and I wanted a dedicated circuit.  Even though I was doing all the work myself, I do read up on the codes and follow them folks, this is my home we’re talking about.  The lighting of course, required me to run a line down to a new wall switch but that was a no-brainer.



Things are shaping up

IMG_0471This was a lot of work for one person, but let me tell you something: If you’ve ever done any home improvement by yourself, there’s a point you reach, a runner’s high if you will, where a room starts to take shape and you find yourself really psyched about the project.  The LAN lines were all crimped onto CAT 6 outlets and the new power outlets were in place.  Oh, and by the way, at this point we had just switched our Verizon home phone service to Cablevision, which also runs my internet line.  We also left DirecTV and went with Cablevision on TV as well.  So I readied the office for the phone lines and made sure they were able to access the cable model with all in-wall wiring as well.  I even have a TV cable outlet down here in the office.  My second workstation (the one on the left) was always used for entertainment as well, both gaming and movies.  This rig needed to have a killer sound system and the old one while OK just wouldn’t do.  I picked up the Logitech Z-5500 5.1 system with a sub so big it doubles as an ottoman.  This installation needed to be clean so rather than have speaker wires connect to the back of the sub and go to each satellite, I ran speaker wire in the walls for the fronts, center, and surround speakers.  The wires terminated down next to the LAN drops for that rig (shown – the picture is farther along than the story - sorry).  Now I only had to connect the sub to the RCA plugs that were to go on that outlet.  I found a place called DataPro that creates custom wall plates with literally anything you need on them.  They even have a great on-line wizard so you can design the plates yourself.

Everything was spackled, sanded, and primed; and now it was ready to be painted.

IMG_0454IMG_0456 I’ve never claimed to be an interior designer nor even to have any skills that lean toward that trade.  I proved this in my color selection, but I gotta say, it turned out very nice.  I wanted the place to look lively and not necessarily coordinated.  I succeeded so my heterosexuality suffered not an inkling.  The room has five, yes you heard right, five colors in it but looks awesome.  Yes, that is a shade of pink on the wall there and purple up where the skylights are; it’s the only places where those colors are used so it actually makes you do a double take when you come into the room. 

The first wall you see when you come down the stairs received a grayish-green color and looks great too.  It’s this wall where all the LAN drops come into and where I placed a wall rack that holds a CAT 6 patch panel as well as an awesome HP Powercurve Gigabit hub.  Why awesome you ask?  Because it’s totally fan-less and has pretty lights on it.  I also put a small shelf on that wall where I have the cable model, wireless access point, and a Microsoft Disco Light of course.  The phone and fax lines in the room, as does the Internet line drop down into this area so the jump to the cable model is only a few inches.IMG_0459




It might all have been for nothing

IMG_0469 The office was looking great but the floor was still concrete.  And by the way, I had the concrete professionally cleaned to get all the stain and smell out from my friggin feeline!

I didn’t want carpet again but I didn’t want the headache of installing a hardwood floor.  I decided to go with floor planks by Dupont with a dark cherry tone.  This was gonna be an easier installation… or was it?  I’m a computer guy, so I don’t read instructions for anything, including how to lay this flooring down.  And I’ve done this before, but it’s been a while.  Well, you’re supposed to start in a specific corner, and if you don’t, getting the planks to click in place and stay in place is nearly impossible.  No, let me correct that, it is friggin impossible.

I can’t tell you how close I came to kicking in the walls and taking a sledge hammer to the room.  That’s how frustrated I was getting with this floor until my beloved wife came down to try to make me feel better.  No, I don’t mean that! Get your minds out of the gutter!  My wife Elena picked up the instructions which clearly (in a larger font size in fact) said to start in the left corner and work yourself right.  You can guess I had stared on the right corner and was attempting to work myself left.  It took us about 3 hours to lay the whole floor down and it came out great – thanks honey!.  All I had left to do now was to buy the baseboard molding (and the wall molding for the recessed shelves), cut them, paint them, and install them.  All these steps were actually quite easy, all but the first one.  Well let me clarify that.  Buying the molding was easy.  In fact, I made it a point to measure out everything I would need so I pre-cut them at the Home Depot (with a little extra of course).  Then you whip out your trusty credit card and the transaction is done, right?  Wrong.  Now you gotta take all of it home.  We’re a two-car family and I didn’t think about the kind of cars we have when I went to Home Depot.  I had brought my black Mustang GT Convertible and could not for the life of me figure out a way to get this home.  No, taking the top down wasn’t gonna do it, besides it was the dead of winter.  Whatever I did, it had to be easier with my wife’s car.  I left the stuff at Home Depot and went home to exchange cars.  I drove our new Honda Accord to the front of the store and stared at the rather long pieces of molding with the same trouble as I had with my Mustang.  I really had no choice.  I rolled down all the windows and started putting the long pieces in so that they stuck out of a rear window and the opposite front window.  The shorter ones stuck out of only one window.  The problem was that the so-called “sticking out” was about 2 and half feet on both sides.  I drove home like this, careful not to get close to other cars.  The other cars were not so careful (typical Jersey) so it was not without its challenges but I made it.  The next day I cut the pieces to exact size, mitered them (that’s the angle cuts for you newbies), and painted them.  I own just about every piece of tool equipment out there so I had a good miter saw for all this and I even have an air compressor and finishing nail gun.  I was not about to hand hammer them all.


The furniture and equipment come back in

IMG_0472 It was finally time to start bringing the desk back in from the garage.  This was a task for which I had help to get it into the garage, but there was no one else around now.  So what the hell, let me give it a try.  I’ll make this part short.  I was out of commission for a week.  During which week I had two visits to my chiropractor.  No gym, no driving, and no home office project.

A week later, Elena came to my rescue once again and helped me get the desk pieces back in where I then proceeded to assemble them.  The old book cases wouldn’t do, specially since I pretty much destroyed them trying to get them into the garage so it’s off to Ikea I go to get new stuff.  The design I came up with warranted new furniture anyway.

IMG_0512 I also started to rebuild the equipment, and wouldn’t you know that this project coincided with the release of Windows 7- hurray!  I built the second workstation first but before I did that, I brought it up as-is and had my beautiful new HP Media Smart Windows Home Server back it up fully.  I can’t rave about WHS enough so I’ll leave that for another posting.  I fully reloaded that rig with Windows 7 and put everything in place.  The first workstation was my liquid-cooled one and before this project started, it had sprung a leak.  I had already isolated where the leak was and fixed it (replacing the reservoir).  I use non-conductive liquid so everything was cool, but I did replace the two 150gig 10K RPM Raptor drives with two 300gig 10K RPM Velociraptor drives.  These make up my boot partition and are Raided to mirror each other.  I also learned that draining a liquid cooled system is a bit of a pain so I installed a drain valve in that rig (shown).

Earlier, I had put up some shelves on the front and rear walls of the office for decor (that’s what you see in the pictures).  You’ll see later what I use one of them for.


IMG_0480  IMG_0477 I also wanted to keep all the monitors off the desk but I didn’t want any fixed to the walls.  Fixing so many monitors on the walls means you have to space them perfectly and since you’re limited to the wall beams, it’s not an easy task.  I found some great monitor arms by Ergotron and bought six of them.  I also bought a seventh for my laptop.  Yes, my laptop is on an arm now.  This is really quite cool cause that arm has four extension pieces on it so it can come out quite a bit, allowing me to be on the second workstation and extend the laptop all the way out so I can reach it.  Oh, by the way, these damn arms are the absolute best and strongest out there, but you bet your ass you pay for that; they’re about 120 bucks a piece.



As clean as I can get it

DSCF0001 DSCF0002A fter rebuilding all the equipment and hooking everything up, my project was nearly complete.  A new office merits a new office chair so I picked up a really nice brown leather high-back at Staples for about $300, not too bad.  I tried to get all the cables and wires on wire guides under the desk so while not perfect, it’s good enough.

I also built the new Ikea cases on top of which are a new Epson Artisan 810 printer and my Windows Home Server.  This is the only server I have, and need.  For years, I’ve had dedicated servers for things I never used.  The most my servers were used for were file sharing and the WHS totally rocks.  I upgraded it to 4 gig of RAM and it runs Virtual Server nicely if I need other server products.  The area where this furniture sits is where the closet used to be.  Can’t tell can you?!?  To the right of one of my stations is a drawer roller with a new Samsung CLP-610N Color Laser.  Both printers are networked and since each of my stations uses two LAN lines, now you know why I dropped so many lines.  I also bought a new cigar humidor (those of you who know me, expected this one) which is actually a piece of furniture and sits in the corner of the recessed book shelves.  The wall shelves in the back of the room had some decor on them, including a glass sword-shaped container of Belarussian Cognac given to me by my father-in-law.  The shelve on the front of the room holds an MVP plaque and my Samuel Adams bottle collection.  Say what you will, it looks pretty cool.

DSCF0003 DSCF0004 The primary workstation has four monitors attached to it (the photo shows the top one off – it has been replaced by a Dell 20”) and the secondary workstation has two monitors on it.  Both machines are setup for development and are used that way, though the second one gets some great entertainment use with its killer sounds system and all.

Also, note that every machine, including the server has a dedicated UPS system.  In fact, the box next to one of the rigs (under the laptop) contains a shelf-top UPS that now sits next to the Linksys and cable modem and protects them and the hub from power loss.  Also, I’m proud to say I no longer blow the circuit breaker since there are now more than one driving the room.


DSCF0005 DSCF0006 There are many things I would like to improve for a future office, but this one is done.  It’s even got a bar area in the middle of the recessed shelving.  All my other ideas would require a different room layout, meaning a different room, meaning a different house.





Oh, and in case you’re wondering what happened to the cat: she seems to have gone back to normal (kitty litter that is).  In any case, my office has a gate on it (it’s a sub-standard-sized door way) so she can’t come into it.  But believe me, I got my eye on her, or my foot…


Until next time.

Monday, March 22, 2010 8:08:57 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [5] - - Follow me on Twitter

# Tuesday, March 16, 2010

In the never-ending quest to keep my site up-to-date, I wanted to add a “latest tweet” area to the top of my blog.  All these tweakings I’ve done (the syntax highlighter, licensing, branding, etc) were actually inspired by a talk that Scott Hanselman made in Cairo Code Camp on making your blog suck less.  The talk is based on this posting he made some time ago.  I’ll post on all the tweakings I’ve done later; let’s get back to the Twitter feed.

The Twitter site self has a section where you can step through creating an embedded Twitter feed, either in Flash or HTML.  In fact if you Google or Bing the phrase “embed twitter feed on website”, you’ll see many different techniques.  The one I used is the one right off the Twitter site.

After walking through the wizard, the site gives you this code:

<div id="twitter_div">
    <h2 class="sidebar-title">Twitter Updates</h2>
    <ul id="twitter_update_list"></ul>
    <a href="http://twitter.com/miguelcastro67" id="twitter-link" style="display:block;text-align:right;">follow me on Twitter</a>
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/miguelcastro67.json?callback=twitterCallback2&count=5"></script>

I first inserted this code as-is, just to get things working (my usual approach to things).  The div tag was inserted into my homeTemplate.blogTemplate in the area where I wanted to display my Twitter feed.  In my case, this was just below the area that shows the admin bar (shown only when logged in).  The two script tags need to be placed at the bottom of the same file, just above the closing body tag.  This displayed my last five tweets in a unordered list.  The first script tag includes the function that the Twitter site will call back to after retrieving my feed information.  The second script tag makes the call to the Twitter API and defines the name of the callback function defined in the first script.  This worked fine but I wanted a different look.

The first thing I did to modify the display is to browse to the http://twitter.com/javascripts/blogger.js link and place the function in a javascript file in my site; then I replaced the src attribute in the first script tag to point at my function.  The results of this of course were no different than before – good.

Next I modified the div section for my display to look like this:

<div class="twitter-div">
    <a class="twitter-header" href="http://twitter.com/miguelcastro67" alt="Follow me on Twitter">Last Tweet:</a>&nbsp;
    <span id="twitter-post" class="twitter-post"><i>retrieving last tweet...</i></span>


As you can see, I eliminated the unordered list and replaced it with something more simple, that’s one line only, and that expects only one tweet.  Then I took the function I had obtained from the blogger.js file and modified to look like this:

function twitterCallback(twitters) 
    var statusHTML = [];
    var username = twitters[0].user.screen_name;
    var status = twitters[0].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
        return '<a href="' + url + '">' + url + '</a>';
    }).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
        return reply.charAt(0) + '<a href="http://twitter.com/' + reply.substring(1) + '">' + reply.substring(1) + '</a>';
    statusHTML.push('<span>' + status + '</span> - <a style="font-size:85%" href="http://twitter.com/' + username + '/statuses/' + twitters[0].id + '">' + relative_time(twitters[0].created_at) + '</a>');
    document.getElementById('twitter-post').innerHTML = statusHTML.join('');

function relative_time(time_value)
{ var values = time_value.split(" "); time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3]; var parsed_date = Date.parse(time_value); var relative_to = (arguments.length > 1) ? arguments[1] : new Date(); var delta = parseInt((relative_to.getTime() - parsed_date) / 1000); delta = delta + (relative_to.getTimezoneOffset() * 60); if (delta < 60) { return 'less than a minute ago'; } else if (delta < 120) { return 'about a minute ago'; } else if (delta < (60 * 60)) { return (parseInt(delta / 60)).toString() + ' minutes ago'; } else if (delta < (120 * 60)) { return 'about an hour ago'; } else if (delta < (24 * 60 * 60)) { return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago'; } else if (delta < (48 * 60 * 60)) { return '1 day ago'; } else { return (parseInt(delta / 86400)).toString() + ' days ago'; } }

Now I only worry about one tweet and display information about that tweet into the inner section of the span tag called twitter-post.  Incidentally, the original callback function looked like this:

function twitterCallback2(twitters) {
    var statusHTML = [];
    for (var i = 0; i < twitters.length; i++) {
        var username = twitters[i].user.screen_name;
        var status = twitters[i].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
            return '<a href="' + url + '">' + url + '</a>';
        }).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
            return reply.charAt(0) + '<a href="http://twitter.com/' + reply.substring(1) + '">' + reply.substring(1) + '</a>';
        statusHTML.push('<li><span>' + status + '</span> - <a style="font-size:85%" href="http://twitter.com/' + username + '/statuses/' + twitters[i].id + '">' + relative_time(twitters[i].created_at) + '</a></li>');
    document.getElementById('twitter_update_list').innerHTML = statusHTML.join('');


Obviously I changed the name in the call to the Twitter API to use the callback function twitterCallback instead of the original twitterCallback2, but of course this can be anything so long as the two match.

The last change was to change the count attribute in the call to the Twitter API to 1 instead of 5.  This wasn’t crucial since I was only dealing with array item 0 in my callback function, but in the interest of getting as much performance as possible I thought it was a good idea.

I added the style sheet tags to make the display look the way I wanted and voila.  Because of the callback-nature of this technique, the blog page will render first while the call to the Twitter API is made and in the meantime I display a “retrieving…” message.

Until next time.

Tuesday, March 16, 2010 4:54:16 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] - - Follow me on Twitter
Dev Stuff

This technorati tag – TKHAYZRNKMP3 – is for their verification purposes only.

This bloglines tag – <!-- ckey="4595A1E8" –> – is for their verification purposes only.

Tuesday, March 16, 2010 12:59:05 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] - - Follow me on Twitter

# Monday, March 15, 2010

One of the reasons I took my blog off GeeksWithBlogs.com, despite the fact that I liked running it there for as long as I did, is so I can have source control over the site and be able to customize it as much as I want.  As before, I want people to be able to visit this site to learn about speaking engagements I do and to download session material, but rather than spread out the session material on individual blog postings (though I’ll do that as well), I wanted a single page where folks can go and download any material I’ve made available; hence www.dotnetdude.com/downloads.

So here’s where I tell you what I did and how I did it.  Now as usual, I anticipate criticism on what I did wrong and how better I could have done it and blah blah blah.  So while I really don’t care what people say about the way I do certain things, I will tell you what my goal was so you understand the methods I undertook.  I wanted a very, very simple downloads page.  Let me repeat that s-word, “SIMPLE”.  I didn’t want to have a database driven page or something with layers of categories.  I simply wanted a single page where anyone can go to get session, or magazine material.  Now that being said, I did want some amount organization while having ease of maintenance.  I simply ended up writing a page that read off a folder structure and manifested that structure in a tree.  In fact, in the interest of keeping it as simple as possible, I used Microsoft’s TreeView control.

The code is pretty generic (as you’ll soon see) and simply looks for a folder starting point, which I store in the config file.  From there it recursively iterates through the folders and files and builds the nodes of the tree.  This design let me manage the folder structure in whatever manner I want and allows for as much or as little hierarchy as I want.  I thought this was a good mix of simplicity and flexibility.

This was the easy part.  The harder part was digging around the dasBlog source code to figure out what I had to do in order to add custom pages to the site.  The dasBlog engine is very nice but does things a bit different from what conventional web forms application developers may be used to.  The site uses template files to determine the ASP.NET code for each section of a page.  For example, the main layout of every page in your blog is determined by a file called homeTemplate.blogTemplate.  This file contains what looks like conventional HTML code, and in fact it is.  However there are some keywords surrounded by <% %> tags (note, no equal sign) which will get replaced later when the template is processed and rendered.  This design actually made it pretty easy to customize the theme I chose for my blog because it is in this particular template file where I went to make alterations to the page layout.  When I wanted to customize what the blog posting area looked like, I went to the itemTemplate.blogTemplate file.  Here I could make changes like put the permalink on top instead of the bottom, or add an image to the left of my post header, etc.  Back to the homeTemplate.blogTemplate file; it is here where I added additional link tags and meta tags I wanted every page to use.  I also added the Syntax Highlighter here that you’ll see in practice below on code.  Rather than tell you a lot about this must-have utility, just check out Scott Hanselman’s post here.  Another customization I’ll save for another post is the Bing search box on the site.

So now that I knew how the templates worked, I wanted to add a custom page that appeared within the context of the main layout defined in the homeTemplate.blogTemplate file.  It didn’t take much digging into the existing pages of the site to figure out the repeating pattern they all have.

The page I wanted was to be called Downloads.aspx so I started by creating that page right off the root of my site.  Every page in dasBlog contains an empty PlaceHolder control which is later filled with a corresponding user control.

<%@ Page Language="C#" AutoEventWireup="False" CodeBehind="Downloads.aspx.cs" Inherits="newtelligence.DasBlog.Web.Downloads" %>
<asp:placeholder id="contentPlaceHolder" runat="server"></asp:placeholder>


The code-behind for this page follows the same pattern as other pages in the site.  The main thing is to inherit from the SharedBasePage class and implement the IPageFormatInfo interface.  The only required implementation for this interface is the BodyText property which returns a type of Control.  Here is where I would return an instance of a user control that will be the actual heart of my new page.  By instance, I mean a usage of the LoadControl command.  Another standard on the page is to expose the PlaceHolder by providing a property for it. 

Here’s the code-behind for the Downloads.aspx page:

using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Core; 

namespace newtelligence.DasBlog.Web
    public partial class Downloads : SharedBasePage, IPageFormatInfo
        protected override PlaceHolder ContentPlaceHolder
            get { return contentPlaceHolder; }

        public Control Bodytext
            get { return LoadControl("DownloadsList.ascx"); }


Now I had to write the user control that this page was to load and display.  This user control was simply going to have an ASP.NET TreeView control on it.

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="DownloadsList.ascx.cs" Inherits="newtelligence.DasBlog.Web.DownloadsList" %>
<%@ Register Assembly="System.Web.Extensions, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    Namespace="System.Web.UI" TagPrefix="asp" %>
<div class="downloadHeading">File Download Section</div>
<div class="bodyContentStyle" id="content" runat="server">
    <div class="downloadArea">
        <asp:TreeView ID="tvwFiles" runat="server" NodeIndent="40">
            <NodeStyle CssClass="fileTree" />


The div-tag hierarchy is just standard div styling with which I’m sure most of you as more familiar with than I am.

The real meat of this page is in the building of the tree, but even this was not overly complex.  I simply loaded a folder starting point from my config file and started my recursive iteration.  Each time I went down a level, I sent the previously built tree node so that each subsequent child tree node can be added to its collection.  The ASP.NET TreeView control is a bit more limited than third party controls and only seems to allow node-searching through one level within the tree.  If I’m wrong on this, please let me know.

Before I show you the code for this user control, let me tell you about one more thing I came up with.  Even though a folder and file hierarchy is easy enough to follow for any of us, I wanted a tad more polishing to this page.  I wanted the option (not the requirement) to display friendly titles for both folders and files within the folders should I desire that (and I do).  To accomplish this, I knew I would need some kind of mappings file but I didn’t want one massive file to manage so what the code does is look for a file called index.xml in each folder during the recursive iteration.  This file, if it exists, will let me rename the folder itself as well as any of the contained files.

Here’s an example of the index.xml file which resides in a folder called Cairo2010:

<titles folder="Cairo Code Camp - Feb 2010">
    <title file="DynamicStateStorage.zip" name="Dynamic State Storage: An ASP.NET Provider-Based Feature" />
    <title file="Extensibility.zip" name="Extensibility: Software That Survives" />
    <title file="FunWithHandlers.zip" name="Fun With HTTP Handlers" />
    <title file="UnderstandingASPNET.zip" name="Understanding ASP.NET Under The Covers" />


As you can see, based on the information in this file, the folder will display as Cairo Code Camp – Feb 2010 and each of the contained files will display as their corresponding title as show in each name attribute.  Should I forget to create one of these files for a particular folder or forget to map a particular file within a folder, or simply decide to not do it by choice, the tree will display the original folder or file name instead.

    public partial class DownloadsList : System.Web.UI.UserControl
        protected void Page_Load(object sender, EventArgs e)
            if (!IsPostBack)
                string filesSubFolder = ConfigurationManager.AppSettings["files"];
                string filesFolder = Server.MapPath(filesSubFolder);
                string filesUrl = Request.ApplicationPath + "/" + filesSubFolder;

                string[] folders = ProcessFolder(filesFolder, null);

        private string[] ProcessFolder(string path, TreeNode parentNode)
            string[] folders = Directory.GetDirectories(path);
            if (folders.Length > 0)
                foreach (string folder in folders)
                    DirectoryInfo dirInfo = new DirectoryInfo(folder);
                    string folderName = dirInfo.Name;

                    XDocument xmlDoc = null;

                    string indexFile = folder + "\\index.xml";
                    if (File.Exists(indexFile))
                        xmlDoc = XDocument.Load(indexFile);
                        var attrFolder = from node in xmlDoc.Descendants("titles")
                                 where node.Attribute("folder") != null && node.Attribute("folder").Value != ""
                                 select node.Attribute("folder").Value;

                        if (attrFolder != null)
                            folderName = attrFolder.FirstOrDefault<string>();

                    TreeNode folderNode = new TreeNode("&nbsp;&nbsp;" + folderName, folder);
                    folderNode.SelectAction = TreeNodeSelectAction.None;
                    folderNode.ImageUrl = "Images/Folder24.png";
                    if (parentNode != null)

                    // retrieve files
                    string[] files = Directory.GetFiles(folder);
                    if (files.Length > 0)
                        string appPath = Request.PhysicalApplicationPath;

                        foreach (string file in files)
                            FileInfo fileInfo = new FileInfo(file);
                            string fileName = fileInfo.Name;

                            if (fileName.ToLower() != "index.xml")
                                if (xmlDoc != null)
                                    var attrName = from node in xmlDoc.Element("titles").Descendants("title")
                                                    where node.Attribute("file").Value == fileName
                                                    select node.Attribute("name").Value;

                                    if (attrName != null)
                                        fileName = attrName.FirstOrDefault<string>();

                                string fileUrl = file.Replace(appPath, "");
                                string fileAlt = fileInfo.Name;
                                string fileLink = string.Format("<a href=\"{0}\" alt=\"{1}\">{2}</a>", fileUrl, fileAlt, fileName);

                                TreeNode treeNode = new TreeNode(fileName, fileUrl);
                                treeNode.NavigateUrl = fileUrl;

                    string[] subFolders = ProcessFolder(folder, folderNode);

            return folders;


As you can see, the code uses Linq-to-XML to access the index.xml files.

The last thing I wanted to do is to give a user the easiest URL possible so although you can navigate to www.dotnetdude.com/downloads.aspx to get to this page, I want you to be able to simply type www.dotnetdude.com/downloads.

IE7 allows for extension-less URL, my current hosting server still uses IE6 so I needed a sure-fire technique to accomplish this.  The easiest thing to do was to create a sub-folder in my site called Downloads and place an empty Default.aspx page in it.  In fact, I eliminated the code-behind for this page so it is truly empty.  I eliminated the code-behind because I didn’t need to put a code-driven redirect to my actual Downloads.aspx page.  This is thanks to a URL mapper that comes with dasBlog.  To take advantage of this, I went to the web.config file and fouind the <newtelligence.DasBlog.UrlMapper> sesion.  Following the pattern I saw in there already, I added an entry that looked like this:

<add matchExpression="(?&lt;basedir&gt;.*?)/downloads" mapTo="{basedir}/Downloads.aspx"/>


This should be easy enough to follow.  Suffice it to say that navigating to www.dotnetdude.com/downloads now takes you to the actual Downloads.aspx page.  In fact, I added another couple of lines to shortcut to the email page on the site and to an bio posting as well.

<add matchExpression="(?&lt;basedir&gt;.*?)/aboutme" mapTo="{basedir}/2010/03/02/AboutMeShamelessBioPost.aspx"/>
<add matchExpression="(?&lt;basedir&gt;.*?)/contact" mapTo="{basedir}/Email.aspx"/>


Now you can simply type www.dotnetdude.com/email to contact me, or if you’re really bored you can type www.dotnedude.com/aboutme .

The last thing I want to point out here is that the TreeView control is not using JavaScript to expand and collapse itself.  I haven’t yet figured out why as this is supposed to be a built-in feature of the ASP.NET TreeView control.  I’m suspecting that it has to do with the way dasBlog turns templates into code and renders pages.  I need to put some time into it cause I don’t really like the fact that it posts each time you collapse or expand a node.  Clicking on a file doesn’t post, since the display text for each file is literally an HTML anchor tag to the actual file to download.  I tried simply putting the control in an ASP.NET Ajax UpdatePanel control and it was ignored.  This is what tells me it’s something within dasBlog rendering since the Ajax UpdatePanel functionality is also not working.  If anyone has any ideas on this, please let me know.

I hope this has been useful to anyone who uses dasBlog and wants to customize it to their liking.  Now I just gotta fill the download page with all my session material from past event.

Until next time.

Monday, March 15, 2010 7:48:06 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] - - Follow me on Twitter

Me & My Flair

Read all about me here.
Download my Resume here.

Check out where I am here.
Click on logos above for profiles.
<March 2010>
Total Posts: 40
This Year: 0
This Month: 0
This Week: 0
Comments: 93