# Thursday, August 19, 2010

Dinking around with the Facebook JavaScript SDK

With some spare time on my hands, I took at look at the improved JavaScript SDK from Facebook.

In the past, I really disliked Facebook coding because the developer experience was really hard. The documentation was wrong or obsolete, the sample code libraries rarely worked and it was really frustrating.

Now with the push towards the JavaScript SDK, things are smooth sailing! Here’s the reference: http://developers.facebook.com/docs/reference/javascript/

It’s super easy to sign in to Facebook from my external site and remove the need to have my own authentication system. I’m always a fan of having one less username and password to remember. The following sample page can be used to connect to your facebook application and do a couple of simple tasks like retrieving your name. This Facebook code comes right out of the JavaScript SDK reference.

The 5 steps to set up this code are embedded in comments of this HTML file:

   1: <!DOCTYPE html>
   2: <html lang="en">
   3: <head>
   4:     <title>Sample Facebook JavaScript SDK</title>
   5:  
   6:     <!--
   7:         How to get started:
   8:  
   9:             1. Register an app on http://www.facebook.com/developers/
  10:             2. Helpful table of facebook app settings can be found here: http://msdn.microsoft.com/en-us/windows/ee395718.aspx
  11:             3. Create a Web Forms or MVC project in Visual Studio
  12:             4. Inside Visual Studio, set the specific PORT NUMBER to match the Facebook app registration
  13:             5. Copy your app id from Facebook registration page to the JavaScript below
  14:     -->
  15:  
  16:     <style>
  17:         input {
  18:             width:130px;
  19:         }
  20:         table {
  21:             width:100%;
  22:         }
  23:         .left {
  24:             width:200px;
  25:             vertical-align:top;
  26:             text-align:left;
  27:         }
  28:         .right {
  29:             vertical-align:top;
  30:             text-align:left;
  31:         }
  32:     </style>
  33: </head>
  34: <body>
  35:  
  36:     <table>
  37:         <tr>
  38:             <td class="left">
  39:                 <ol>
  40:                     <li>
  41:                         <input id="getLoginStatusButton" type=button value="Get Login Status" />
  42:                     </li>
  43:                     <li>
  44:                         <input id="loginButton" type=button value="Login" />
  45:                     </li>
  46:                     <li>
  47:                         <input id="getName" type="button" value="Get Name" />
  48:                     </li>
  49:                     <li>
  50:                         <input id="logoutButton" type="button" value="Logout" />
  51:                     </li>
  52:                 </ol>
  53:             </td>
  54:             <td class="right">
  55:                 <h2>Messages</h2>
  56:                 <ol id="messages"></ol>
  57:             </td>
  58:         </tr>
  59:     </table>
  60:     
  61:     <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
  62:     <script type="text/javascript">
  63:  
  64:         window.fbAsyncInit = function () {
  65:             FB.init({
  66:                 appId: '[YOUR APP ID GOES HERE]',
  67:                 status: true, 
  68:                 cookie: true,
  69:                 xfbml: true
  70:             });
  71:         };
  72:  
  73:         $(function () {
  74:  
  75:             $('#getLoginStatusButton').click(function () {
  76:                 console.log('inside button click event');
  77:                 FB.getLoginStatus(function (response) {
  78:                     if (response.session) {
  79:                         // logged in and connected user, someone you know
  80:                         $('#messages').append('<li>logged in; access token is ' + response.session.access_token + '</li>');
  81:                     } else {
  82:                         // no user session available, someone you dont know
  83:                         $('#messages').append('<li>not logged in</li>');
  84:                     }
  85:                 });
  86:             });
  87:  
  88:             $('#loginButton').click(function () {
  89:  
  90:                 FB.login(function (response) {
  91:                     if (response.session) {
  92:                         // user successfully logged in
  93:                         $('#messages').append('<li>successful log in; access token is ' + response.session.access_token + '</li>');
  94:                     } else {
  95:                         // user cancelled login
  96:                         $('#messages').append('<li>cancelled logged in</li>');
  97:                     }
  98:                 });
  99:  
 100:             });
 101:  
 102:             $('#getName').click(function () {
 103:  
 104:                 FB.api('/me', function (response) {
 105:                     $('#messages').append('<li>' + response.name + '</li>');
 106:                 });
 107:  
 108:             });
 109:  
 110:             $('#logoutButton').click(function () {
 111:  
 112:                 FB.logout(function (response) {
 113:                     $('#messages').append('<li>logged out</li>');
 114:                 });
 115:  
 116:             });
 117:  
 118:         });
 119:  
 120:     </script>
 121:     
 122:     <div id="fb-root">
 123:         <script src="http://connect.facebook.net/en_US/all.js" type="text/javascript"></script>
 124:     </div>
 125:  
 126: </body>
 127: </html>
#    Comments [0] |
# Monday, August 16, 2010

How to Edit Meta Data Inside JPG Files with C#

I do (change your EXIF data)We just got back from a great weekend at the beach; my sister-in-law got married on the sands near Haystack Rock in Cannon Beach, OR. Really a great trip from start to finish.

My wife accepted the photographer duties and snapped nearly 2K photos with our Nikon D40 and a Nikon D60 that we borrowed from a friend. She had a zoom lens on one and a short lens on the other to avoid the need to change lenses in mid-moment. That part worked great.

The part that we overlooked was the current date/time on each camera; they were not synchronized. This will make sorting photos by time more difficult. No problem, I say, on the drive back home. I’m a developer and JPG files have meta info that I can manipulate, right?

It turns out that its not all that easy. Its actually a remarkable pain in the ass, hence this blog post (that’s the remark-able part).

I started with an online search and found Hanselman’s post about combining PowerShell with Omar Shahine’s library, ImageLibrary.dll that is evidently no longer accessible. Big phat 404 while trying to get that assembly.

Next, I did some searches specific to StackOverflow.com and saw some posts about how difficult reading/writing this meta data can be. At this point, I was beginning to wonder if my wife was going to hear a broken promise or not. It wasn’t looking good.

I found out that JPG files store meta data using EXIF, or Exchangeable Image File Format, a standard used by the digital still camera industry. It looked like C# 4.0 could crack this open, but I might have to do get my hands dirty with some bit shuffling. No worries, I was up for it. It didn’t look like there was a library out there that was going to fall into my lap, so I cracked open Visual Studio 2010 and saw what kind of trouble I could get into with Intellisense and a gin and tonic.

Let’s Take It Out for a Loop

I created a new project, loaded a Bitmap class with a sample JPG and iterated over the properties:

   1: using System;
   2: using System.Drawing;
   3:  
   4: namespace ReadExifInJPG
   5: {
   6:     class Program
   7:     {
   8:         static void Main()
   9:         {
  10:             var bitmap = new Bitmap("c:\\temp\\somephoto.jpg");
  11:  
  12:             foreach (var item in bitmap.PropertyItems)
  13:             {
  14:                 Console.WriteLine("Id: {0}, Type: {1}, Value: {2}", 
  15:                     item.Id, item.Type, item.Value);
  16:             }
  17:         }
  18:     }
  19: }

I had lots of properties in my sample photo:

output of simple iteration

I was thinking that this was going to be smooth sailing until I saw the values listed as byte arrays. Ugh. Then I read a EXIF specification that said the values can be stored in multiple formats and there are a ton of properties. I had over fifty on a single JPG image. I needed to find the property that told me if the given photo was taken by a Nikon D60 or a D40, and then change the date on the D60 images. Here’s the EXIF specifications: http://www.exif.org/specifications.html, and what a great document to read! Ugh.

I Just Want My Property

I found a list of property tags in numerical order on MSDN, note the id values are listed in hex, so 0x0132 (hex) equals 306 (decimal): http://msdn.microsoft.com/en-us/library/ms534418(VS.85).aspx

When you go about reading or changing a given property, there are three parts to consider:

  1. the id number that identifies the given property
  2. the type number which describes the data format
  3. the actual data, encoded in the least accessible format you could want

Here’s a list of numeric types that identify the corresponding data format:

  1. A Byte
  2. An array of Byte objects encoded as ASCII
  3. A 16-bit integer
  4. A 32-bit integer
  5. An array of two Byte objects that represent a rational number
  6. Not used
  7. Undefined
  8. Not used
  9. SLong
  10. SRational

Fortunately, everything I was interested in was of type 2, a byte array. This is a great MSDN article that gave me a bunch of tips on how to manipulate the EXIF data: http://msdn.microsoft.com/en-us/library/xddt0dz7.aspx

The property id of 306 holds the date of the photo, its a type 2 property, so the value is stored in a byte array. Here’s how to read the string value:

   1: var property = bitmap.GetPropertyItem(306);
   2: System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
   3: string value = encoding.GetString(property.Value);


Caution, Byte Counting Ahead

Note, the date properties that I’m interested in updating have values stored as byte arrays. More specifically, they’re exactly 20 bytes long. At first I wasn’t respecting that boundary and I just let .Net do it’s thing to convert my formatted date string into a byte array. When I tried to read the value back, it was empty because I overflowed the value.

After some noodling, I changed my code to write out exactly 20 bytes of data, starting at precisely 23 bytes into my new byte array, as follows.

   1: private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
   2: {
   3:     DateTime originalDateTime = GetOriginalDateTime(bitmap);
   4:  
   5:     var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
   6:  
   7:     BinaryFormatter bf = new BinaryFormatter();
   8:     MemoryStream ms = new MemoryStream();
   9:     string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  10:     bf.Serialize(ms, formattedNewDateTime);
  11:     ms.Seek(0, 0);
  12:  
  13:     var tempArray = ms.ToArray();
  14:  
  15:     byte[] byteArray = new byte[20];
  16:  
  17:     var x = 0;
  18:     for (int i = 23; i < (23 + 19); i++)
  19:     {
  20:         byteArray[x] = tempArray[i];
  21:         x++;
  22:     }
  23:  
  24:     SetNewDateTime(bitmap, byteArray, 306);
  25:     SetNewDateTime(bitmap, byteArray, 36867);
  26:     SetNewDateTime(bitmap, byteArray, 36868);
  27: }
 

Note, the previous code shows I’m writing the same date to three different property settings (306, 36867, and 36868). They all had the same date value, so I figured the best thing to do was keep them all in sync.

It Works on My Machine

So, I was finally done with the code and I tested it with a bunch of files on my machine. Worked great. I installed VS 2010 C# Express on my netbook so I could create a console application and run it on my wife’s laptop. I had been abusing a ASP.Net Web Forms application with Visual Web Developer Express on my netbook. I made a quick console app, copied it to my wife’s laptop and ka-pow! It no worky. Puzzled, I tried to think what was wrong for about 30 minutes. After a couple of assertions inserted into my code, I realized she had some non-JPG files in the folder and my program wasn’t filtering for only JPG files. Doh! I did manage to get in a quick “hey, it works on my machine” comment to my wife, but she didn’t think it was as funny as I did.

Nothing But The Code, The Whole Code, So Help Me .Net Runtime?

Here’s the final class I built, in “good enough” format. The entire coding time, not including the installation of VS 2010 Express on my netbook, or .Net 4 on my wife’s laptop was about 90 minutes. The console program just takes this class, passes in the source and destination directories, along with the number of minutes to adjust the timestamp on the photo.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using System.Drawing;
   7: using System.Runtime.Serialization.Formatters.Binary;
   8:  
   9: namespace ImageMetaTool
  10: {
  11:     public class TimeEditorService
  12:     {
  13:         public void AdjustDateTime(string soureDir, string destDir, int minutesToAdd)
  14:         {
  15:             if (!Directory.Exists(soureDir))
  16:             {
  17:                 Console.WriteLine("Source directory does not exist.");
  18:                 return;
  19:             }
  20:  
  21:             if (!Directory.Exists(destDir))
  22:             {
  23:                 Console.WriteLine("Destination directory does not exist.");
  24:                 return;
  25:             }
  26:  
  27:             int x = 0;
  28:             var list = Directory.GetFiles(soureDir, "*.jpg");
  29:             foreach (var filename in list)
  30:             {
  31:                 Console.WriteLine("Processing image {0} of {1}; {2}", x, list.Length, filename);
  32:  
  33:                 using (var fs = File.OpenRead(filename))
  34:                 {
  35:                     var bitmap = new Bitmap(fs);
  36:                     ProcessFile(bitmap, minutesToAdd);
  37:                     bitmap.Save(Path.Combine(destDir, Path.GetFileName(filename)));
  38:                     x++;
  39:                 }
  40:             }
  41:  
  42:             Console.WriteLine("Processing completed.");
  43:         }
  44:  
  45:         private void ProcessFile(Bitmap bitmap, int minutesToAdd)
  46:         {
  47:             if (!IsD60(bitmap))
  48:                 return;
  49:  
  50:             ChangeTimeStamp(bitmap, minutesToAdd);
  51:         }
  52:  
  53:         private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
  54:         {
  55:             DateTime originalDateTime = GetOriginalDateTime(bitmap);
  56:  
  57:             var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
  58:  
  59:             BinaryFormatter bf = new BinaryFormatter();
  60:             MemoryStream ms = new MemoryStream();
  61:             string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  62:             bf.Serialize(ms, formattedNewDateTime);
  63:             ms.Seek(0, 0);
  64:  
  65:             var tempArray = ms.ToArray();
  66:  
  67:             byte[] byteArray = new byte[20];
  68:  
  69:             var x = 0;
  70:             for (int i = 23; i < (23 + 19); i++)
  71:             {
  72:                 byteArray[x] = tempArray[i];
  73:                 x++;
  74:             }
  75:  
  76:             SetNewDateTime(bitmap, byteArray, 306);
  77:             SetNewDateTime(bitmap, byteArray, 36867);
  78:             SetNewDateTime(bitmap, byteArray, 36868);
  79:         }
  80:  
  81:         private void SetNewDateTime(Bitmap bitmap, byte[] newDateTime, int propertyNumber)
  82:         {
  83:             var property = bitmap.GetPropertyItem(propertyNumber);
  84:             property.Value = newDateTime;
  85:  
  86:             bitmap.SetPropertyItem(property);
  87:         }
  88:  
  89:         private DateTime GetOriginalDateTime(Bitmap bitmap)
  90:         {
  91:             var property = bitmap.GetPropertyItem(306);
  92:  
  93:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  94:             string value = encoding.GetString(property.Value);
  95:  
  96:             string value2 = value.Split(' ')[0].Replace(":", "/") + " " + value.Split(' ')[1];
  97:  
  98:             return DateTime.Parse(value2); //2010:08:14 14:23:14
  99:         }
 100:  
 101:         private bool IsD60(Bitmap bitmap)
 102:         {
 103:             var modelProperty = bitmap.GetPropertyItem(272);
 104:  
 105:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
 106:             string modelName = encoding.GetString(modelProperty.Value);
 107:  
 108:             if (modelName.Contains("D60")) // NIKON D60
 109:                 return true;
 110:  
 111:             return false;
 112:         }
 113:     }
 114: }

 

I hope you have a good time futzing with your EXIF data!

#    Comments [0] |
# Thursday, July 31, 2008

Importing Data From Excel In C#

Behind the scenes, I see a lot of companies run on Excel and duct tape. So it's common for clients to hand me a slab of data organized into neat little rows and columns. Excel is just a handy way to throw some data over the wall and get things done. This might be a list of dealers, a collection of user profiles, product information, or anything that just needs to get somewhere else.

Depending on the scenario, I'll might massage this data and slide it into a SQL Server database or an XML file. I'm a web developer so I use the ADO.Net stack on a regular basis. If I were a Windows client developer, I might prefer to solve this problem with the Excel object model, but that really looks like more code to me, so here's how I like to roll:

The following code block accepts an Excel file path and returns an ordinary DataTable object, which can be manipulated easily by the code that calls this method. The first row of the Excel document becomes the columns in the DataTable object and each row thereafter are DataRow objects.

ImportDataFromExcel
(Click to enlarge)

If you like, take a look at both techniques for working with Excel data and see what one speaks to you. With this block of code, I can happily accept large chunks of data from a client without spending precious time fiddling with administrivia.

#    Comments [0] |
# Monday, February 04, 2008

Its only Monday, and I'm Already Out on a Branch

...or two.

I've had two projects this week where their/my bacon was saved by the branching strategy used for the source code repository. In my line of work, I touch a wide variety of projects. Some are still shiny new and others were written years ago; they're all in a source control repository.

Modern source control repositories (and by modern, I mean things that are not your file server or P:\ drive) let you create a branch. Branching might appear complex at first, but it's really pretty simple - at least conceptually. Here's a little story to illustrate the point of branches.

When I create a branch, I have a snapshot of the code at a particular point in time. So, imagine that on Monday, I have a single branch called the "trunk". It contains all of the code for my web site. On Tuesday, I created a branch based on the code in the trunk. I named my branch "BR-1". When I created BR-1, it was a mirror image of the trunk. I edited the code in BR-1 for a couple of days and nearly got it how I wanted it. My edits were isolated to BR-1 and they did not exist in the trunk.

I got a call early on Friday morning about an important change that needed to go out right away. I couldn't implement the urgent change to BR-1 because it contained my partially completed work. It was a pretty small change; a few hours of brilliant coding and I would be ready for a peer review of my changes. So, I checked out the trunk and made the edit right there and checked it back in. I published the urgent change I made on the trunk out to the live web site in the early afternoon and went back to working on BR-1.

By mid-day on the following Monday, I had finished all of my changes to the BR-1 branch. I had merged the BR-1 code into the trunk too. Now my trunk code contained the changes from BR-1 as well as that urgent change that came through on Friday morning. Complete control with the flick of a switch.

In the previous story, I was able to handle the urgent request efficiently because I used branching to manage my code changes. If I didn't have branching available as a software development tool, I would have some bad choices to make. Either I merge the urgent fix with my own enhancements and risk problems by rushing it, or force the urgent change to wait until my own changes were done; neither are necessary.

Branching is great way to ensure that I can always put my work on the shelf in lieu of more pressing matters. Understanding the concepts of branching is essential to being a valuable member of the team. The keys you actually press to make a branch or merge two branches can be left up to the nerdy folk. For more info, check out Pragmatic Version Control Using Subversion.

#    Comments [0] |
# Saturday, November 10, 2007

Thinking about ROI

In my experiences as a software developer, its fairly normal to hear comments like the following:

  • That's too many hours
  • They don't have the budget
  • I'm not paid enough

I had the good fortune of attending a Portland XP Users Group presentation a few weeks ago by James Shore. He got off on a slight tangent and gave us (well, at least me) a simple equation to chew on:

roi

He explained that at its core, Return On Investment is represented by the previous equation. It can help explain quite a bit about the world. This was in response to someone asking about the high cost of the software development methodology under discussion that night.

If you're presented with a scenario where the value is constant, then the only way to play the game is to minimize costs. Think of a job that never changes. If it always provides the same value to a business, management will seek ways to reduce cost in order to improve the ROI equation.

On the other hand, a scenario where value has the capability for growth is much more interesting. If you wanted to make $500,000 a year then you would be challenged to deliver some multiple of that cost as a value to the business.

Here's my favorite take-away: At some point along the graph, as value increases then cost becomes insignificant. This is the place to be.

The initial cost of software can make some people squeamish. I'm certainly not one to be afraid of zeros; I'm much more interested in the value.

  • What is this solution doing for the business?
  • Is there a practice in place for tracking ROI over time?
  • How soon can it begin providing value?
  • Can it provide even more value?
  • It is possible to reduce cost and drive the equation even higher?

This is why I love my job at Pop Art. Driving value higher and then swooping back to cut costs with new technology that makes me more productive. Value will often come in several forms including cash value, brand value and community value. In any case, it all starts with that equation.

#    Comments [0] |
# Monday, November 05, 2007

Interwoven TeamSite 6.7.1

By some cruel trick of nature, I've been surrounded by web content management systems for the past several years. I've written several (who hasn't?) and stood up instances of DotNetNuke, Windows SharePoint 2007, and now Interwoven TeamSite.

I'm told Interwoven has been around for 14 years and they're currently on version 6.7.1 of their TeamSite product. This offering includes features for enterprise content management (ECM) and the assorted adjacent technologies that simply must accompany ECM in a large scale deployment. Since the beginning, their product has included version control and workflow features. I'll describe a few core parts of Interwoven TeamSite here.

Source Control Repository

First off, developers will get a quick leg up if they simply know that TeamSite includes a source control repository. It works like any other. You can get the latest source, edit something, check it back in, compare versions, label a snapshot of the repository as well as branching. There. You're farther ahead than I was when I got my first explanation, about two years ago.

Standards based developers can relish in the fact that they have carte blanche control over the HTML and CSS sent down the wire to the browser. I'm talking about the production site here, not the administration pages used to interact with TeamSite. Nearly everything you'll do with TeamSite is done through a browser, providing that browser is IE or Firefox. Sorry Safari, this car is just a two seater. Again, browser support for the production site you're building is up to you - just talking about the TeamSite administration pages here. You'll continue editing specific files in your favorite tool, such as Visual Studio, Photoshop or TextMate. So once you get the subtle nuances of this source control system in you're brain, the challenge is to build a compelling web site using the same tools you have now; no silver bullets here.

Its also worth noting that this is only a source control repository; not a web development platform. You'll still need IIS, Apache, or some other web server technology to host your site. Interwoven TeamSite is mature enough that it supports Windows, Unix and Linux environments and several popular databases such as SQL Server and Oracle; just what you'd expect from an enterprise content management system.

FormsPublisher

Interwoven's TeamSite product includes a feature called FormsPublisher. This is useful for scenarios where an information worker needs to contribute to the web site, but lack HTML and CSS skills. This type of user can complete a form, let the system validate it, and then press a button to have the system generate the equivalent HTML page. The form can include validation, business logic, database queries or anything else you can dream up. While an HTML expert would prefer their favorite text editor and complete control to the HTML page. An information worker without HTML skills can now edit an existing page, based on an customized form. Now the challenge shifts to having the information worker select the appropriate form to build the page.

A developer configures a set of files and folders to support this process. At a high level, there are three parts in motion. The Data Capture Template (DCT) is an XML file that describes how TeamSite should present the form to the information worker. A Data Content Record (DCR) is an XML file that contains an instance of a form completed by an information worker. You launch TeamSite, click File, New Form Entry and select the form you want to complete. Next, the given form appears in the browser. The fields of the form are defined by the DCT. After you click the Save button, the field values are serialized into a DCR file. These files are organized in a collection of folders on the TeamSite server; one folder per form. Each folder has a conventional set of child folders to hold the DCR files as well as the presentation template file(s), These are the files with the .tpl file extension.

A presentation template converts a DCR into something else. Most of the time, at least for me, that something else will be an HTML page. Imagine a single DCR file that contains both public and private information for a company; perhaps the DCR contains public information about a single product as well as private information for their tech support staff. The DCR file can be send around via workflow for approval and the finally be ran through both sets of presentation templates which results in two different HTML files - one file is deployed to the public and the other file is kept on the Intranet. Both files are assured of having the appropriate content via the approval process and the presentation templates apply the correct branding and layout. This model supports a good workflow model as well as good separation of design from content. Its just one example of using the FormsPublisher in the enterprise.

Since DCR files are just XML, it's relatively painless to import legacy data into TeamSite. These imported XML files map to a given DCT, then they're translated into HTML pages via a presentation template. For example, if you have the last 15 years of press releases on the legacy system, you can import the existing information with some batch processes and stand up a new TeamSite server pretty quick.

Workflow

A workflow describes an automated business process. It can instruct an author to perform an edit or add a new file to the site and manage the process of approvals, taking a snapshot of the system for archival purposes and finally deployment. When a task such as "Edit Content" or "Review Content" becomes active, an e-mail can notify the appropriate party. When the work is completed, the assigned person just pushes it through the hole. Reviewers can click "Accept" or "Reject" after previewing the changes, they don't need to know the next appropriate step in the process - its automated. The system tracks the state and flow of information of the activities throughout the lifecycle according to the established business rules approved by the given company rather than based on how the given employee feels that particular day.

TeamSite has always included a workflow mechanism, but I can only imagine how difficult it was to develop them in the past. This latest version includes a Windows client application that supports drag-and-drop editing of workflow designs. You can work locally with workflows saved to your desktop in an offline mode, but you will eventually need to save the workflow up to the TeamSite server via commands in the tool. Workflows are serialized into XML files in the background, but its rare to look at the raw information. The workflow designer application helps you do the things you would expect such as setting up a series of tasks linked by arrows; some might be conditional based on human interaction or automated entirely by Perl scripts, Java classes or some other business logic.

Its interesting that Interwoven prides itself on a large number of supported platforms for the server product, but the Workflow designer client requires the Windows operating system. As I understand it, some (or all) of the workflow client application was recently purchased from another company. My hunch is the client application is written in C++ based on the look and feel as well as the OS requirement. Its a little clunky but it gets the job done; I sure wouldn't want to hand code all of the XML it generates.

Conclusion

Overall, I'm happy with my experiences thus far with TeamSite. The documentation is rich, it has a thriving online developer community, and the paradigm isn't too hard to grasp. At one point, I started getting bogged down with the massive about of XML configuration files and customization points. After sleeping on it (and a stiff cup-o-coffee) I realized that any other enterprise level application has a similar amount of customization. Since TeamSite embraces a litany of platforms, it makes sense that they don't (or haven't) invested a lot in slick little Windows GUI programs for configuration needs. Why build a server GUI tools when Windows is only one of your supported OS platforms? On the other hand, they could build a few more web based administration forms to get around some of these XML file updates. Once I started tallying up how many configuration pages I go through for Windows SharePoint, I stopped feeling like I was building my own box and installing Linux and started thinking about the broader ideas and why I was configuring the system instead of how - perhaps it just semantics, but I felt better.

#    Comments [0] |
# Wednesday, September 26, 2007

Installing Powershell on Vista

I just got a new Dell Latitude D830 laptop running Vista Ultimate and I'm going about the lengthy process of installing all my software. When I got to PowerShell, I grabbed a copy of the installation file from my 250GB external drive that I had previously downloaded for my previous laptop, a Dell Latitude D610, running Windows XP SP2.

When I ran the install program, I was repeated met with the following error message:

Not enough storage is available to process this command.

A lot of other people had the same problem, but no one had posted a solution. Then, I suspect I figured out the answer the same way others had. I was executing WindowsXP-KB926139-x86-ENU.exe instead of Windows6.0-KB928439-x86.msu, I had overlooked the fact that Windows XP and Vista had different installation programs for PowerShell.

Drat.

#    Comments [0] |
# Tuesday, July 24, 2007

System.Net.WebException : The operation has timed-out.

I've been battling some crazy ASP.NET 1.1 code the past couple of days. First, I was calling some web services with entirely hand written SOAP protocol management. That's always fun. While tedious and too low of a level to be mucking around, it was the fastest means to an end with a crotchity ol' Apache/AXIS web service. Even though I'm fresh off the WCF training regimen; that new stuff wouldn't have helped me here. Perhaps just having the new stuff in my head pushed me in the right direction to tackle this challenge - a man can dream, can't he?

Then, just to rub it in a little, the HTTPWebRequest object decided to give me a swift kick in the shorts. You see, in .Net 1.1, there's a tiny little unknown (unanswered) bug.

I have two instance methods and each make a single call over HTTP with their own HTTPWebRequest object, there's no sharing here. Every time, the second request would hang and throw a "System.Net.WebException : The operation has timed-out." error. The same code runs great in .Net 2.0 so I figure its a framework bug.

I call one method and return a string which is used as an input argument for the second method. Both calls go to the same top level domain, but call to different end points on the domain.

After much thought, trial and error, as well as reading about the same problem other folks were having, I finally figured out the workaround.

It turns out that calling the Abort() method on the HTTPRequest instance after completing the request is sufficient to break up the clog that is preventing subsequent calls. The following code block shows the working helper method. If I comment out the calll to Abort(), it no worky.

private string ExecuteHttpWebRequest(
   string url, RequestMethod method, string message)
{
   HttpWebRequest request = 
      (HttpWebRequest)HttpWebRequest.Create(url);

   request.Timeout = 30000;
   request.KeepAlive = false;
   request.Headers.Add("Cache-Control", "no-cache");
   request.Headers.Add("Pragma", "no-cache");

   switch( method )
   {
      case RequestMethod.Get :

         request.Method = "GET";

         break;

      case RequestMethod.Post :
         
         request.ContentType 
            = "application/x-www-form-urlencoded";
         request.Method = "POST";
         request.ContentLength = message.Length;

         StreamWriter sw = 
            new StreamWriter(request.GetRequestStream());
         sw.Write(message);
         sw.Flush();

         break;
   }

   string html = null;

   using( HttpWebResponse response 
      = (HttpWebResponse)request.GetResponse() )
   {
      Stream rs = response.GetResponseStream();
      StreamReader sr = new StreamReader(rs);
      html = sr.ReadToEnd();
      response.Close();

      // subsequent calls fail without this abort in .Net v1.1
      request.Abort();
   }

   return html;
}	
#    Comments [0] |
# Wednesday, July 18, 2007

Google Reader Keyboard Shortcuts

I've been using Google Reader for a while now and I love it. Its very quick to launch with my SlickRun shortcut of "gr" and it manages my current list of 96 RSS subscriptions very well.

In fact, I recently figured out that this application is perhaps one of the worst web applications to use with the mouse. The mouse is such an impediment to reading several posts efficiently. I launch Google Reader several times a day and its common for me to have nearly 50 unread posts from over 30 RSS subscriptions at any given perusal session.

Here are the Google Reader keyboard shortcuts I use on a regular basis.

  1. Click the "Show Updated" hyperlink in the left column to show only the RSS subscriptions with unread posts
  2. Click the first RSS subscription link of the list in the left column (getting in the mood)
  3. Click the first unread post of the list if the right column (tee it up)
  4. Press space bar to traverse multi-page posts and advance to the next unread post for a given RSS subscription
  5. When all the posts are read for a given RSS subscription, press Shift+N to advance the highlight to the next RSS subscription of the list in the left column
  6. Press Shift+O (this is my Oh face) to open the list of the new highlighted RSS subscription
  7. Press the space bar to traverse this new feed like the previous RSS subscription

 

 googlereader

To Summarize:

Space Bar Scroll through a long post, then skip to next post when the last line of current post is visible
Shift + N Highlight the next RSS subscription in the left column
Shift + O Open the list of posts for the highlighted RSS subscription

 There are a bunch of other keyboard shortcuts for Google Reader. These are the ones I use the most.

#    Comments [0] |
# Sunday, April 29, 2007

My New RSS Reader

Google Reader I used SharpReader for a long time, then briefly used the RSS Reader in Outlook 2007, after an even briefer stint with IE7's RSS reader client (ack!). I don't categorize myself as superstitious, but was else do you call it when you suspect all those posts and media downloads in Outlook 2007 are slowing down your system and you have no real data to back it up?

I liked the Outlook 2007 reader because I was horrible at opening SharpReader on my laptop. I always had Outlook open. It took a while to launch SharpReader, but it was a great application. Then, I'd open SharpReader on a weekend and be horrified by (a) the number of posts that I was behind, and (2) the amount of information that I could have used during the previous week. I'm always looking to get my compile time down, so right or wrong, I'd close applications left and right to give my Dell Latitude D610 as much horsepower as it can wield during the week.

My buddy, Scott, turned me on to Google Reader. As a big time RSS fan boy and an even bigger nerd than I, his opinion is to be respected.

So this weekend, I imported my OPML file into Google's RSS Reader and away I go. So far, I'm pretty impressed. There's a few things I'd like to do, such as return certain posts to "unread" status as I want to come back to them later and rename some folders, but all in all, I likey.

Now, I can open my RSS reader without the cycles of a big application. ( type my new magic word, "gr", in SlickRun and launch my reader anytime I like. Booya!

#    Comments [0] |
# Thursday, February 15, 2007

CardSpace & the Laws of Identity

I attended the Software Association of Oregon (SAO) event today. The Development Special Interest Group (DEV SIG) hosted a discussion about Microsoft CardSpace, the open source framework of OpenID, and basic identity management.

Stuart Celarier walked the audience through Kim Cameron's paper called The Laws of Identity that articulate seven desired aspects of a good identity system.

Microsoft CardSpace was formerly named "InfoCard". This is a joint effort to implement the identity metasystem defined by the laws of identity. CardSpace is the "identity selector" for Windows. It needs IE7 and Microsoft .Net Framework 3.0 to operate. It implements the WS-* specifications in this service.

OSIS - Open Source Identity System: This is an open source group that's involved in the identity space.

Stuart also showed a demo of a system he's been working on. It logs a user into Wachovia banking site using CardSpace.  Scott Kveton of JANRAIN presented OpenID to the SAO DEV SIG group. OpenID hopes to solve the problem of having too many usernames and passwords.

  • Single Signon for the web
  • Simple, light-weight
  • Easy to use, easy to deploy
  • Open development process
  • Decentralized

Your OpenID is a URL: http://kveton.myopenid.com/

  • OpenID comes from the blogosphere
  • Biggest problem with identity; namespace
  • OpenID solves this by using DNS
  • Your identity is a destination
  • You have a unique endpoint on the web

Scott Kveton explained how sites enabled with OpenID enable users to authenticate. Visitors type in their OpenID, and the browser redirects to your OpenID provider. The visitor makes the appropriate decision and the browser redirects back the website.

Scott's site is http://scott.kveton.com

Last week Bill Gates announced support for OpenID. AOL announced support for OpenID this morning. More companies are about to make similar announcments. Here's some interesting stats on adoption:

  • 12-15 million users with OpenIDs.
  • 1000+ OpenID enabled sites
  • 10-15 new OpenID enabled sites each day
  • 7% grown each week with new sites

Kveton also brought up "Microformats" - a way to describe data in an HTML format (contact info, social network, calendar). These can be embedded on pages. There are some interesting ways to use OpenID with these technologies:

  • OpenID + iCal
  • OpenID + hCards
  • OpenID + Social Networking (XFN, FOAP or FOAF?)
  • OpenId + Reputation (jyte.com)

OpenID Predictions from Kveton:

  • 7500 sites supporting OpenID
  • 100 million users with OpenID
  • Big players adopt OpenID

OpenID.net has a ton of info.

Scott Hanselman explained how he enabled OpenID on his blog. Hte added two HTML <link> tags to his website. Simon Willison has an OpenID enabled blog. A visitor can click Sign in with OpenID. The OpenID logo lives inside the textbox. Scott entered his OpenID in the textbox on Simon's site. Using a web service, Simon's blog discovered Hanselman's OpenID provider, then it redirected the browser to Scott's OpenID provider.

Scott's website indicates the OpenID provider is www.myopenid.com

The OpenID provider prompts Scott to authenticate. After a successful login, the browser redirects back to Simon's page and recogizes Scott Hanselman. This is how Simon doesn't need to keep track of usernames and passwords for his blog; a huge benefit.

Stuart helped explain the difference between self-insued cards and managed cards: Business Cards from Kinko's versus a card issued from Visa.

Scott Hanselman displayed a different identity selector using Firefox on Windows. The page contains an HTML <object> tag of type "application/x-informationCard". It wasn't as pretty as the CardSpace in IE7 and .Net 3.0, but it had the same behavior.

There was some last minute discusson on "I-Name", an XRI technology (extensible resource identifier). It sounds like its still being baked.

2idi relays comments on Scott's blog. They will issue an I-Name. =kveton is Scott's I-Name. They have an DNS resolver where visitors may enter xri://=scott.hanselman/photo to redirect to his Flickr account.

#    Comments [0] |
# Sunday, January 14, 2007

Xml Notepad 2007 vs WMHelp XmlPad

I had a need for a client services teammate to get their hands dirty with some XML. In this project, an XML file defines the hierarchy of pages in a web site. With a massive redesign under way, the team needed someone with familiarity of the existing pages to make the changes. With the hopes of giving my non-developer teammate a slick application to help them rearrange some angle brackets, I took a look at Xml Notepad 2007 and WCMHelp's XmlPad.

Xml Notepad 2007

The bits can be downloaded here and this document a nice explanation of the author's intent.

Xml Notepad 2007

The left window shows the tree. The red dots are attributes and the folder icons are elements. The right window shows the values of the attributes. There's an XSL tab for viewing transformations. You can point the application at an XSLT file and see the output too.

The part I like the best about this is application is its simplicity. I need my teammate to look at each node and make a determination. They might need to move it anywhere in the hierarchy. There are four buttons on the right side of the toolbar expressly for this purpose. If they feel a little more ambitious, they can click and drag the node to the new location in the left window.

WMHelp XMLPad 3

You can get the bits here. This application feels a little more powerful than the first application. It can create schemas and validate against them in a more flexible manner than XML Notepad.

WMHelp XMLPad

The attribute names and values appear in the lower left window of this application. There are different views of the XML available too. The picture above shows the source view. There are three other views (Grid, Table and Preview) in the bottom of the right window.

This application doesn't show the simple set of buttons in the toolbar for moving nodes, although they can be dragged around in the tree. I find that dragging can be a little unpredictable with a large node set. With all the on-the-spot decision making going on, I felt that the simple XML Notepad program was better suited for the specific task and user. However, if I'm the one doing XML work, I would probably choose XMLPad if VS.Net 2005 didn't do the trick for some reason.

At some point, I'd like to checkout XMLSpy. I've heard great things about it for years. I chose to examine these two because they were free and this was a one time gig. If I end up doing lots of work with my angle bracket hammer, then I probably will put in a request for the XMLSpy license.

#    Comments [1] |
# Monday, November 27, 2006

Command Spy

I saw this post on Jeff Atwood's blog about a new tool from SlickEdit, the makers of SlickRun; easily the most frequently used program on my laptop day-in and day-out.

The Command Spy
Whenever you click on a menu item or toolbar button in Visual Studio, you are executing what is known as a "command". Unfortunately, it's almost impossible to tell what command is linked to which menu items or toolbar buttons. The Command Spy monitors command execution and allows you to see exactly what commands you've run, how many times you've run them and what key bindings are used to invoke those commands. The main purpose of this tool is to allow you to learn what commands are bound to which keystrokes, so that you can work faster within the IDE.

I installed Command Spy over the Thanksgiving holiday. It totally rocks! Just run the VS.Net add-in while you're coding for a couple of hours and then take a look at your metrics. Command Spy will tell you how many times you've run a command without using the short-cut keystrokes. Its a great way to improve on your productivity.

#    Comments [2] |

Using Microsoft Outlook 2007 as an RSS Reader

I started using the RSS reader in Microsoft Outlook 2007.

I was using SharpReader for as long as I've ever had an RSS reader and subscribed to feeds. I really like having just one application running for my communications and work items (email, tasks, calendar, RSS feeds...). Its super easy to add a feed when you're visiting a web page too. I used folders quite a bit in SharpReader to categorize feeds, but I'm trying the "one big folder" approach for now in Outlook.

Sometimes, I got lazy and didn't open SharpReader on my laptop for a while. The posts would get way out of control. Sure, I could add it to my Start Up programs, but one of my quirks is to keep that list short, if not empty. Some weeks I even tell SQL Server to not startup automatically. Perhaps that's one of the old carry overs from living too long with too little RAM on a weak laptop.

Outlook 2007 as an RSS reader is nice, but I miss some of the basics, like identifying the URL to a blog I've already subscribed to. There's probably a way to display that information easily in Outlook, but I haven't discovered it yet.

To date, the most frustrating thing has been refreshing feeds. It gets joined at the hip with my email send/receive request. So, when someone comes by my desk in a general freak-out mode and asks how to solve a problem raised in a recent e-mail, I click on Send/Receive if I haven't gotten it yet. Now, thanks to the additional RSS feeds, I have to wait much longer as everything is updated. There must be a way to decouple these requests from each other in Outlook.

Overall, I'm very happy with the switch so far.

#    Comments [2] |
# Friday, October 06, 2006

Thank You PayPal!

 One of our Pop-Art-Gives-Back-To-The-Community clients wants to implement some simple online donation services on their website. So, I was flipping through a PDF file from PayPal using my Foxit Reader and I noticed this little phrase at the top of a page:

They're giving me complementary technical documenation! No orange juice or bagel, but the doc is categorized and complete.

I saw another funny part on the PayPal site. The link to the PDF file says it requires (my emphasis) Adobe Acrobat to read it. Ha! I must be flaunting the rules with my Foxit reader; breaking the law! breaking the law!

By the way, I've had great experiences with Foxit up until yesterday. One of our black turtleneck shirt wearing designers created a PDF version of a client proposal. When I opened it in Foxit, some large background images flashed for a second, then the text content appeared without much styling. Each page behaved this way. When I opened it in Adobe Acrobat, the full beautiful proposal displayed great with background and content. Huh. I'm not sure if the designer was leveraging some custom Adobe feature or if Foxit was the culprit; it was the first and only time I didn't get parity though.

#    Comments [0] |
# Saturday, September 30, 2006

Windows Live Writer

My first Windows Live Writer.
#    Comments [0] |
# Wednesday, September 27, 2006

TortoiseSVN version 1.4.0.7501

The other day I fired up TortoiseSVN and it politely informed me that I might enjoy the latest release.

Hurrah!

#    Comments [0] |
# Wednesday, September 13, 2006

iTunes Forced Upgrade

Forced upgrades on the day of the new iTunes launch.
#    Comments [0] |
# Wednesday, August 30, 2006

What The Font?

Image files don't have manifests.
#    Comments [0] |
# Thursday, August 10, 2006

Peer Reviews Rock

I had a fairly minor change to some code today that modified the display order of some reports implemented as unique web user controls that are loaded dynamically based on which items you select.

I wrote out my plan and before editing any code, then I asked my co-worker, Kelly, to review it and see if it held water.

Turns out, I sprung a leak. It was such a simple mistake. The code I inherited used the Session object to store a value and my flawed plan slightly modified the values in the existing Session object. My plan assumed the web user controls executed serially, when in fact, I could not guarantee that level of access to the data in the Session object.

Sans code review, I would have gone ahead and modified the code, tested it, and (hopefully) seen the error of my ways before handing it off for final review before deployment. It would have ran without throwing an error, but not correctly. The peer review saved a significant amount of time, relative to the overall time spent on the task.

Peer reviews rock. Do them early and often!

#    Comments [0] |
# Tuesday, August 08, 2006

You may receive an InvalidCastException error in an ASP.NET-connected Web application

How to fix errors with dynamically loaded web user controls in asp.net that throw an invalidcastexception.
#    Comments [0] |
# Monday, July 24, 2006
# Sunday, July 23, 2006
# Saturday, July 22, 2006

Code Camp - .Net Refactoring

A lecture on refactoring .Net code.
#    Comments [0] |
# Friday, July 14, 2006

AnkhSVN Is a Go

Using the AnkhSVN add-in for VS.Net
#    Comments [0] |
# Wednesday, July 12, 2006

Fiddler Rocks

Fiddler is a nice HTTP monitoring utility to inspect traffic between your browser and a web server.
#    Comments [0] |
# Thursday, June 08, 2006

CSS files with background images (partially retracted)

Getty Up!

7/1/2006 - Partial Retraction: Ok, so after testing this on IIS (I was using Cassini when I learned about it) its a little less cool than I originally thought. You still need to add the file extension to the list in IIS so it knows which program is handling requests for the given file type. That's a little less cool. Stinky for sites where you only have FTP access and not direct control of IIS. The concept worked with Cassini because its all about .Net, all files seem to go up through the .Net handler. It looked a little funny running trace.axd and seeing requests for image files register in the trace utility. I guess that should have been my first clue.

I unearthed a real gem today. Our web standards movement has made our CSS files much more sophisticated. We're reaping the benefits of simple XHTML markup, flexible designs and everything else the web standards movement is enamored about.

There's always been this nagging problem of relative paths to image files in the CSS, javascript files or anything else that isn't really executed on the server before its rendered to the browser. The problem comes in when a web site is developed locally, which means it has an application name in its path. When the site is migrated to the staging or production server, the application name is gone. The following URLs point to the same file, depending on which environment you're working in:

Local Development Environment:
"http://localhost:1978/web/images/gobbler.png"

Staging or Production Environment:
"http://www.company.com/images/gobbler.png"

These can be referred to as "absolute" paths. Note, everthing before the forward slash in front of "images" will depend on the environment.

So here in lies the rub:

The CSS file(s) will often contain references to background images. They can reference the images using a relative path. Relative paths use the double period ("..") syntax to reference the parent folder and thus identify the path "relative" to the current location:

"../../images/gobbler.png"

That can be referred to as a "relative" path.

The problem comes in when two web pages use the same CSS file(s) and they're in different folders. Suppose one page is in the root folder of the web site and the other page is three folders down from the root. They would have to use different "relative" paths to reach the same file, right?

ASP.Net 2.0 to the rescue!

Now, we could always just write an .aspx file that contains the normal CSS content and embed server side code that conditionally renders the application name before all file references to make them "absolute" paths.

The issue with this approach is that your web page uses the <link> tag to reference the .aspx page like this:

<link rel="stylesheet" type="text/css" href="/css/print.aspx" />

I've been told that some browsers won't treat this reference exactly the same as one with a .css extension, so they always download the file instead of caching it. I don't know this to be exactly true, but this gem I describe below solves that problem, so everyone is happy.

Here's how ASP.Net 2.0 can be configured to use server side code inside .css files and dynamically insert the application name:

  1. Open web.config in a text editor
    1. Add an entry to <buildProviders> that tells ASP.Net which build provider to use:
      <system.web>
         <compilation debug="false">
            <buildProviders>
               <add type="System.Web.Compilation.PageBuildProvider" extension=".css" />
             </buildProviders>
         </compilation>
      </system.web>
      

    2. Add an entry to <httpHandlers> that tells ASP.Net which factory to use:
      <httpHandlers>
         <add type="System.Web.UI.PageHandlerFactory" validate="true" path="*.css" verb="GET" />
      </httpHandlers>
      

  2. Open you CSS files in a text editor
    1. In the first line of each CSS file, set the content type:
      <%@ ContentType="text/css" %>
      

    2. Add the application path before each file reference in the CSS file
      background-image:url<%= Request.ApplicationPath %>/images/gobbler.png");
      

Now you can easily move from your development environment to your staging environment without fear of breaking your image paths. Files in various levels of the directory structure can use the same reference because everything has a path that begins at the root. No more ".." syntax headaches. I love it!

#    Comments [0] |
# Monday, May 01, 2006

Code Snippets

I've been meaning to learn more about the VS.Net 2005 details, so after a nicely grilled steak and a glass of wine, I set myself down to read the MSDN library on the topic of Code Snippets.

Code Snippets are one of the great new features in VS.Net 2005. They provide a method for a programmer to quickly insert snippets of code by typing just a few keystrokes.

An examination of the code over the course of several projects will reveal a set of patterns: classes, properties, event handlers, constructors and methods. Just a few characters vary in these patterns.

private int _name;

public int Name
{
   get { return _name; }
   set { _name = value; }
}

The preceding lines of code provide an example of the pattern inherent in a property. The values "_name" and "Name" represent the private and public values accessible to the class. Code Snippets allow you to produce this code with a minimal amount of keystrokes.

To produce the property show above:

Move your cursor to an appropriate position the class
Press Control+K, Control+X to bring up the Code Snippets Intellisense Context Menu
Type "prop" and press the Tab key

This will result in the following code:

The cursor starts at the first green section, which defaults to the integer data type. Entering the new data type will overwrite the characters "int". After the actual datatype is entered, pressing the Tab key will advance the cursor to the next green section, which defaults to "myVar". Entering the actual value here will overwrite the characters on line 19, line 23 and line 24 simultaneously. This is where you should see the value of code snippets. While C# is slightly more cryptic than VB.Net, there is still a great deal of value to be gained by using a tool like Code Snippets.

There are similar snippets for classes, interfaces and many other patterns. There are over 50 snippets by default. Custom snippets can also be created.

Anywhere a pattern exists in code, a solid case can be made for using Code Snippets. Imagine a class that has over 20 properties. The economy of scale becomes obvious.

Code Snippets are great, but its just the start. 3rd party vendors like Developer Express have built great tools like CodeRush, which represent the next level up in productivity. If you have a great deal of code that could be automatically generated, tools like CodeSmith represent the 3rd level. If you stand to gain a huge advantage from code generation, you can author your own code generator using Microsoft.Net classes. That's right; code that writes code. I just blew your mind.

#    Comments [0] |
# Wednesday, April 12, 2006

Wrong Email Address

I've occassionally sent email to the wrong address. Its to the right person, just the wrong account. When I open Outlook and create a new email message, the handy autocomplete feature lets me type just a few letters of the person's name and it shows me the matching entries. If I don't look really close, I sometimes send email to Dave's personal account, instead of his business account. That sucks, especially when its important, and he doesn't see it for a while.

Google to the rescue.

A quick Googling, and I had the answer to my problem in about 30 seconds.

http://www.feld.com/blog/archives/2005/09/clearing_your_m.html

I figured out how to clear my autocomplete cache in Outlook. I rarely send email to Dave's personal account, so now I can start fresh!

Yay.

#    Comments [0] |
# Monday, April 10, 2006

ASP.Net 2.0 Out-Of-The-Box

Over the weekend, I was playing in a sandbox, working out some ideas for our client, PICA.

I was really impressed with all of the out-of-the-box functionality available in ASP.Net 2.0, specifically these:

  • Personalization
  • Membership
  • Menu Control
  • Security Trimming
  • Two Way Data Binding

I was able to build a full app, albeit wanting of some CSS love, in about four hours time.

The site shows a few pages of content. It lists some events that were created in a password protected part of the site. The events can be added to a cart and "bought" by a user by clicking a dynamic link to PayPal. The user's cart is persisted via cookies if they come back later. The user's cart is also migrated to a persistant data source if they decide to authenticate. The menu of the site uses the asp.net menu control and it has a nice "Security Trimming" feature that omits any items in the site map that the given user doesn't have access to view.

These features really let me focus on the core business problem, rather than getting bogged down by plumbing and implementation details. The number of new features in ASP.Net 2.0 is huge and a little overwhelming, but I'm starting to wrap my arms around it, after months of reading. I really dig it.

 

#    Comments [0] |
# Saturday, March 11, 2006

Xml is kicking my ass again

Fun with XSD Schema on a Saturday night. Wahoo!!
#    Comments [0] |
# Friday, March 10, 2006

Generics versus Object Arrays

The .Net programmers on my team were debating the concepts of how a generic class is better than an object array.
#    Comments [0] |
# Wednesday, March 08, 2006

Microsoft Certified Partner Benefits and Windows Server 2003

Software licensing benefits through Microsoft Certified Partner program and Action Pack program.
#    Comments [0] |
# Friday, March 03, 2006

Gotta Love .Net

A project manager came to me this week and asked for a new web page on an existing site. No problem. Then she said this enterprise client is very interested in ROI for this page. How can the client track hits on this page and activity within the page? Well, they have software for log file parsing, but its not easily availlable to the client. So, I fired up Visual Studio.Net and built some software that writes the unique combinations of referring URLs and the date to an XML file. If two people come from the same referring URL on the same date, the counter of the element is incremented. If its the same referring URL, but the date is different, a new element is inserted into the XML file and the counter is set to one. I wrote the software for this mini-tracking service and passed it off to another developer for the actual build. 

This solution allows the client to capture the desired data and periodically recieve reports on the traffic to this specific page. I spent about 30 minutes writing 67 lines of C# code (including blank lines for readability). The Microsoft .Net Framework provided a very simple method for me to manipulate an XML file on disk, and increment counters based on the activities on the URL.

A few days ago, we had another rush order for a different client. The task was to create a simple form that enabled visitors to submit a varying number of email addresses to a central repository. The new ASP.Net Wizard control was perfect for this. It handled all of the state control and provided the Previous and Next buttons for me. I supplied the interface on each wizard step along with the logic for validation and data persistence. It was an easy way to plug in the logic that was unique to my application, but let .Net handle the common tasks of paging between steps and notifying the business logic of when it should ask for data validation or when its time to save the data. Glorious.

With the new presentation layer developer on staff, I was able to stick to what I do best, writing software, and hand off this application to the specialist who made it look just right in my browser. Its fantastic when you can leverage what people and frameworks do best; then you can focus on what you do best. It really helps you be curious about the other specialists skills instead of being wildly dependent on doing a sufficient job in those niche areas yourself. Its like having won the entire game in darts by surpassing 18 points and still having one more game of Cricket to play to complete the match. Its interesting, but not dangerous.

2006 is going to be a great year.

#    Comments [0] |
# Wednesday, March 01, 2006

We Like Mail


I did some research for my boss last week on the new Office 12 and the Exchange 12 products due out in Q4 of 2006. They look pretty good. There's a ton of information out there: blogs, sites, events and workshops. Everyone uses Microsoft Word, Excel, and PowerPoint. Email has been a mission critical service since the mid-to-late 90's. The Exchange 12 Server has been moved to x64 based architecture to increase reliability. They're not even bothering with lessor systems. I've been attending workshops regularly with my team on the new SharePoint tools and Workflow. Based on my research documented on our Wiki, it looks like we're targeting a deployment of Q1 2007. Its going to be a good year.

 

#    Comments [0] |
# Tuesday, February 21, 2006

Windows Live Mail Beta

So I opted in for the beta of Windows Live Mail. It's pretty sweet. Everything you'd expect from a leading AJAX enabled browser application. Plenty of Windows client-like behaviors, right clicking, context menus, the works! The future looks bright.

#    Comments [0] |
# Tuesday, February 14, 2006

Outsourcing Gems

This podcast summed up my last few outsourcing experiences pretty well. If you're working for a company that includes this option in it's technical strategy, you have to listen to it.

#    Comments [0] |
# Friday, February 03, 2006

Windows Workflow Foundation

I've been reading up on a new technology emerging from Microsoft. Its called Windows Workflow Foundation and its part of the WinFX. Here's some high level background info from a key website:

http://www.windowsworkflow.net

Windows Workflow Foundation is an extensible programming model and runtime components for building solutions on the Windows platform. As a platform component, Windows Workflow Foundation will be included with the WinFx runtime components 3.0. Windows Workflow Foundation will also be available as a downloadable component in the second half of 2006.

You can start using Windows Workflow Foundation within your own applications or through Windows SharePoint Services in Office "12".

This technology has a very broad reach. I can envision several applications that would benefit from an easier method of building sophisticated workflow. A visual designer is used to build the workflows. That's going to help non-programmers feel more at ease with developing a system using WWF.

The technology is currently in Beta 2. I'm a little discouraged that a specific installation order is required. This site lets you download the beta, but the text about the specific install order is easy to miss.

 

The following must be installed in the order presented.

If you want only to run WinFX applications:

WinFX RTC

If you want to develop and run WinFX applications:

WinFX RTC

[Optional] Visual Studio - You can use either a full version (available in the MSDN Subscribers Download section) or the free Visual Studio 2005 Express Edition.

Windows SDK

[Optional] Visual Studio 2005 Extensions for WinFX

[Optional] Visual Studio 2005 Extensions for Windows Workflow Foundation

I can think of at least three applications we're talking about right now that could benefit from this technology. Yet, I'm very mindful of not jumping in too fast. I know it will be an important technology, but we need to develop some competencies in it first. Saddle up!

#    Comments [0] |
# Wednesday, February 01, 2006

Bye Bye, Old Friend

End of support for Windows 98 and Windows Me

Effective June 30, 2006, support for Windows 98, Windows 98 Second Edition, and Windows Millennium Edition (Windows Me) will end. Existing support documents, however, will continue to be available through the Microsoft Support Product Solution Center site.

This was like a weird uncle for me. I never knew him that well, but others might have. I made the jump from Win'95 to Windows 2000 and skipped right over it.

#    Comments [0] |
# Tuesday, January 31, 2006

Web 2.0 and What It Means To You

Hey, I got the January post in just in time (that's JIT) to you programmers.

So, like a lot of things, the importance of Web 2.0 depends on who you are. If you're new to the term of Web 2.0, I've included some important links that help frame the conversation below. Its a difficult term to define. Some of the best I've seen define it as an attitude or thought process. They utilize the most compelling features of the latest software: the web as a service, exposing data that was once isoloated in a silo, enabling the network to comprehend data as well as you do, and applications that improve simply by having more people use them.

Here are a few brief scenarios of people who may or may not know the Web 2.0 stick came up and knocked 'em in the head.

Chuckie the 14 year old music enthusiast
Come on, Chuckie doesn't know what this is!?!? He was his rock and he wants it now. Its tough being a teen! Services like Napster and blogs are great for exposing people to ideas, concepts and points of view. Chuckie has over 200 people in his Instant Messenger list and he interacts with them every night after school.

Shawndell the housewife and online consumer
Websites like Amazon expose their data for others to consume and build upon. The plethora of services spawned by Amazon’s web service contributes to a rich market place of products and services available. Where else can you read up and compare the iRobot Roomba 4210?

Mystie the hardcore C# web application programmer
Well, it means Mystie has a good job for a while. No matter which Web 2.0 camp you're in, computer-centric or people-centric, you have to admit its not easy to build systems that run quickly, without errors and get better the more people use them. Mystie also has to get on her horse and start learning a ton of new technologies: AJAX, Web Services, Avalon, Indigo, and WinFX.

Alphonzo the designer
Zo’s realm of possibilities just got bigger; Photoshop is just the start. He’s thinking about interactivity, how to represent dynamic information that he’ll never see, like a movie review, instant message, or aggregated blog posts. A few years ago, he was greatly limited by how much could be accomplished in the project lifecycle. The frameworks that have sprung up in the past year have reduced the barriers to really cool applications.

There have been some great posts about this new word. I've aggregated some of them here. I've included the links to the website and some excerpts from the postings. I encourage you to follow those links and keep up on the conversation. Its sure to have some insightful nuggets beyond what I've posted here. They respresent a wide range of opinions and viewpoints on the definition and significance of Web 2.0.


http://www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20.html

The concept of "Web 2.0" began with a conference brainstorming session between O'Reilly and MediaLive International. Dale Dougherty, web pioneer and O'Reilly VP, noted that far from having "crashed", the web was more important than ever, with exciting new applications and sites popping up with surprising regularity. What's more, the companies that had survived the collapse seemed to have some things in common. Could it be that the dot-com collapse marked some kind of turning point for the web, such that a call to action such as "Web 2.0" might make sense? We agreed that it did, and so the Web 2.0 Conference was born.

In our initial brainstorming, we formulated our sense of Web 2.0 by example:

Web 1.0 --> Web 2.0

DoubleClick --> Google
AdSense Ofoto --> Flickr
Akamai --> BitTorrent
mp3.com --> Napster
Britannica Online --> Wikipedia
personal websites --> blogging
evite --> upcoming.org and EVDB
domain name speculation --> search engine optimization
page views --> cost per click
screen scraping --> web services
publishing --> participation
content management systems --> wikis
directories (taxonomy) --> tagging ("folksonomy")
stickiness --> syndication

What we believe to be the core competencies of Web 2.0 companies:

  • Services, not packaged software, with cost-effective scalability
  • Control over unique, hard-to-recreate data sources that get richer as more people use them
  • Trusting users as co-developers
  • Harnessing collective intelligence
  • Leveraging the long tail through customer self-service
  • Software above the level of a single device
  • Lightweight user interfaces, development models, AND business models

The next time a company claims that it's "Web 2.0," test their features against the list above. The more points they score, the more they are worthy of the name. Remember, though, that excellence in one area may be more telling than some small steps in all seven.

http://radar.oreilly.com/archives/2005/10/web_20_compact_definition.html

  • Web 2.0 is the network as platform, spanning all connected devices; Web 2.0 applications are those that make the most of the intrinsic advantages of that platform: delivering software as a continually-updated service that gets better the more people use it, consuming and remixing data from multiple sources, including individual users, while providing their own data and services in a form that allows remixing by others, creating network effects through an "architecture of participation," and going beyond the page metaphor of Web 1.0 to deliver rich user experiences.
  • That's a great definition, though it also seems that many people pushing various web technologies (AJAX) seem to think that's a Web 2.0 thing even though its been around for years. Your definition is the most accurate for what I'd consider the "true" Web 2.0 applications (del.icio.us, Upcoming, etc.) Posted by: Ben Bangert at October 2, 2005 10:05 AM
  • Web 2.0 is definitely about people -- I believe that the central principle of success in web 2.0 applications is harnessing the collective intelligence of users -- and in my talks, I've often pointed to "the mechanical Turk, a 19th century chess playing automaton with a man hidden inside, as a metaphor for modern web applications, with programmers hidden inside them, performing their daily tasks. (See also my debate with Dave Stutz about how even these web applications harden over time.) I believe that time will show that Web 2.0 started out with exactly the opposite of Dan's formulation: "Web 1.0 was about connecting computers and making technology more efficient for computers. Web 2.0 is about connecting people, and making technology for efficient for people.”

http://radar.oreilly.com/archives/2005/08/not_20.html

Tim Bray writes: I just wanted to say how much I’ve come to dislike this “Web 2.0” faux-meme. It’s not only vacuous marketing hype, it can’t possibly be right. In terms of qualitative changes of everyone’s experience of the Web, the first happened when Google hit its stride and suddenly search was useful for, and used by, everyone every day. The second—syndication and blogging turning the Web from a library into an event stream—is in the middle of happening. So a lot of us are already on 3.0. Anyhow, I think Usenet might have been the real 1.0. But most times, the whole thing still feels like a shaky early beta to me.

  • While being completely right in the details (we are quite arguably on 3.0 or even 8.0 if we're thinking about the internet compared to other software versioning), Tim is completely wrong about the big picture. Memes are almost always "marketing hype" -- bumper stickers is a better way to say it -- but they tend to catch on only if they capture some bit of the zeitgeist. The reason that the term "Web 2.0" has been bandied about so much since Dale Dougherty came up with it a year and a half ago in a conference planning session (leading to our Web 2.0 Conference) is because it does capture the widespread sense that there's something qualitatively different about today's web.
  • More immediately, Web 2.0 is the era when people have come to realize that it's not the software that enables the web that matters so much as the services that are delivered over the web.
  • You have to remember that every revolution occurs in stages, and often isn't recognized till long after the new world is in place. The PC revolution began in the early 80s, and most of the key PC companies and technology innovations were founded in that decade, but it wasn't till the mid-90s that the new shape of the computer industry was clear to everyone.
  • Perhaps I'm biased, because O'Reilly was the source and has been one of the biggest promoters of the Web 2.0 meme, but I think it captures exactly where we are at this moment: a widespread awakening to the fact that the game has changed.
  • There might be a better name (I tried "internet operating system" on for size starting back in 2000), but the fact that Web 2.0 has caught on says that it's as good a term as any. While the patterns that constitute Web 2.0 are far from completely understood, there's a kind of intuitive recognition of sites that are expressing the new model.
  • I guess it's the old debate between language purists, and language pragmatists. The right words are the ones people actually use, and this word is catching on.
  • Obviously I can't disagree with your definition of Web 2.0, with O'Reilly being the source... But what shifted my opinion of the meme from "hot air" to "something useful" was Ian Davis' suggestion that Web 2.0 is an attitude not a technology.

http://www.nik.com.au/archives/2005/10/26/what-web-20-means-for-business/

  • Tim O'Reilly, who describes Web 2.0 as an "architecture of participation"
  • Jeff Bezos said "Web 1.0 was all about making the web easier for users to understand, Web2.0 is all about making the web easier for computers to understand".
  • Web 2.0 is all about making software, web applications and a web service easier to inter-operate.
  • AJAX is about making the web experience for a user as easy if not better than the desktop experience.
  • Flickr and Gmail are not Web 2.0 applications – Web 2.0 applications have nothing to do with using Javascript, CSS, XHTML, using Web 2.0 colors and fonts but rather how the application presents itself to other applications
  • The great Web2.0 applications will be those that take care of a small niche, and do it very well. A great Web 2.0 application becomes a module in a bigger broader solution while at the same time being an application that the user can also make use of independently.
    • Google maps
    • Google Maps Mania, a blog showcasing integration work. o As an analogy, it is like using standard libraries when developing applications – there is no need to write your own components when they are readily available.
    • The core competency at Amazon is their massive catalog and the breadth of metadata available with each article within the catalog. Amazon are adapting to Web 2.0 and are opening up their application as a web service. This service becomes a back-end component to other offerings that have added value in their own way.
    • Salesforce.com realizes that they are never going to be able to extend their application to suit every requirement of every customer. Their approach is to open up their CRM and allow others to develop interfaces that are able to speak to it.
    • If today you are designing a web application for Web 2.0, think not about how the user will see it. Do not attempt to implement a solution that does everything, and do not attempt 'one size fits all' solutions. A Web 2.0 business should rather think more about how they can take and application and work with other solutions, specifically piggy-backing off of large base providers such as Salesforce. This approach provides maximum value to your customer or user and allows a Web2.0 company to focus on one core competency and to be the best at it.
#    Comments [0] |
# Saturday, November 05, 2005

AJAX and Telerik's r.a.d. Callback product

AJAX - Asynchronous Javascript and XML I had a great time working with some AJAX controls on Friday. Telerik has a product named r.a.d. Callback that implements AJAX and extends its other products nicely.
#    Comments [0] |
# Monday, September 26, 2005

Oh Glorious ViewState; Why Are Thee So Fat?

Making ViewState a little thinner through the ajax model.
#    Comments [0] |
# Tuesday, August 30, 2005

dotnetnuke is PDK (pretty damn kewl)

I looked at DotNetNuke and its the bomb!
#    Comments [0] |
# Thursday, August 25, 2005
# Sunday, August 21, 2005

Project Server 2003

Bump, Set, Spike! I'm spiking Project Server 2003 this week. Make sure you have the shepards crook handy if I stray into the deep end.
#    Comments [0] |