George Durzi

in

October 2008 - Posts

Formatting Source Code in your Blog

I really like it when blogs authors take the time to properly format source code in their posts. I work in Visual Studio every day, and immediately make a visual connection to well-indented and color-coded source code.

If you own the book WPF Unleashed, you know that one of the best things about it is that all the XAML looks exactly like it does in Visual Studio (or Blend). So purrty!  I heard an interview with Adam Nathan in which he talked about how he had to manually format each snippet of code for the publisher.

Thankfully, there are easier ways to do this for blogs. I often include code snippets in my blog posts, but I'm guilty of relying too much on the Windows Snipping Tool to just "snip" code out of Visual Studio.

That's a cop out, so what are some better ways? Here are some freely available tools that I've tried out:

http://formatmysourcecode.blogspot.com/

This is a pretty simple tool - you paste your source code into a textbox, specify some options, and click a button to generate HTML which you can paste into your post.

    /// <summary>
    /// Ensures the correct content types are added to the
    /// Pages library associated with the Publishing Portal.
    /// </summary>
    private void EnsureContentTypes (SPWeb site, string pagesList) {
      SPContentTypeCollection pageCTs = site.Lists[pagesList].ContentTypes;
      foreach (SPContentType contentType in site.Site.RootWeb.AvailableContentTypes) {
        if (IsCustomContentType(contentType) && pageCTs[contentType.Name] == null)
          pageCTs.Add(contentType);
      }
    }

Looks decent, but the output isn't specifically color-coded to the language that the code is in.

c# code format

The tool at http://www.manoli.net/csharpformat/  is one of my favorites. It can format c#, vb, html/xml/xaml/aspx, t-sql, and others. You can also choose to show line numbers and alternate line backgrounds.

When using this, you specify whether or not to embed the necessary styles in the generated output. You can upload the style sheet it uses to your blog, and configure your blog engine to make the style sheet visible to all your posts.

Here's some nicely formatted xml:

   1:    <ElementManifests>
   2:      <ElementManifest Location="masterPages.xml" />
   3:      <ElementManifest Location="pageLayouts.xml" />
   4:   
   5:      <ElementFile Location="MasterPages\PublishingMinimalTmp.master" />
   6:      <ElementFile Location="MasterPages\PublishingMinimalPreviewTmp.png" />
   7:      <ElementFile Location="PageLayouts\MinimalTmp.aspx" />
   8:      <ElementFile Location="PageLayouts\MinimalPreviewTmp.png" />
   9:      <ElementFile Location="PageLayouts\MinimalTOCTmp.aspx" />
  10:      <ElementFile Location="PageLayouts\MinimalTOCPreviewTmp.png" />
  11:    </ElementManifests>

and some c#:

   1:  List<Person> peeps = new List<Person>()
   2:  {
   3:      new Person{FirstName="George", LastName="Durzi", Age=32},
   4:      new Person{FirstName="Mark", LastName="Durzi", Age=29},
   5:      new Person{FirstName="Amy", LastName="Shafer", Age=33}
   6:  };
   7:   
   8:  IEnumerable<Person> results = peeps.Where(p => p.LastName == "Durzi");
   9:  double averageAge = peeps.Average(p => p.Age);

Note how the formatting missed some c# keywords like List and IEnumerable - that's my only complaint with the tool.

c# code format Plugin for Windows Live Writer

Mike Ormond took this to the next level and made it into a plugin for Windows Live Writer which you can download here. The plugin gives you a new option called Formatted Code under the Insert menu item.

 

I like this tool because I use Windows Live Writer to write my posts, but check out how it got thrown off by the < and > characters - rendering them as &lt; and &ht;.

   1:  List&lt;Person&gt; peeps = new List&lt;Person&gt;()
   2:  
   3:  {
   4:  
   5:      new Person{FirstName="George", LastName="Durzi", Age=32},
   6:  
   7:      new Person{FirstName="Mark", LastName="Durzi", Age=29},
   8:  
   9:      new Person{FirstName="Amy", LastName="Shafer", Age=33}
  10:  
  11:  };
  12:  
  13:  
  14:  
  15:  IEnumerable&lt;Person&gt; results = peeps.Where(p =&gt; p.LastName == "Durzi");
  16:  
  17:  double averageAge = peeps.Average(p =&gt; p.Age);

I also get pretty annoyed with extra carriage returns, somehow they sneak into the HTML when using this plugin. And since it's based on the same code as the c# code format tool, it's also going to miss the List and IEnumerable keywords.

SyntaxHighlighter

You can download this from Google Code at http://code.google.com/p/syntaxhighlighter/.

To use this tool, you have to upload some JavaScript and CSS files to your Blog and reference them from your post. This is explained in the Usage page for SyntaxHighlighter which you can find at http://code.google.com/p/syntaxhighlighter/wiki/Usage.

You can style code snippets by daisy-chaining css attributes to the style applied to a <pre> tag that the code lives in, e.g. class="c-sharp:nocontrols" to denote that the snippet is in c#, and that I don't want any of the SyntaxHighlighter controls to be displayed.

This was the toughest one to get working, and since it's JavaScript based can get pretty slow if you're applying it to a large amount of code. You won't come across this limitation for normal code snippets in your post. SyntaxHighlighter also has support for the most number of languages.

Unless I'm doing something wrong, the way this works is through a script in the window.onload event. This caused some issues during authoring in Windows Live Writer - the onload script would recreate the code snippet every time I switched between web preview mode and HTML mode.

I can't get this to work property, so I'm pasting a snip of what it looks like when it's working.

 

The most solid and feature-rich of all the tools, but I couldn't get it to work properly...

Verdict

Based on ease of use and the consistency of results, I recommend the c# code format tool at http://www.manoli.net/csharpformat/.

I don't event want to look at the View - Source of this post!

Links

TFS Builds for SharePoint Projects - Part 1

In my last post - TFS Builds for SharePoint Projects ... It's Hard! - I was just starting to look into what it would take to get TFS to build and deploy SharePoint Solutions.

It turns out that getting started on the right track isn't really as hard as I initially thought. I was also happy to find a lot of helpful information out there in the SharePoint community.

Deployability

I'm convinced that deployability is the biggest obstacle that stands in the way of achieving this. I don't think that's a real word - it has a red squiggly under it when I type it - but I take it to mean structuring your projects so that they can be packaged as SharePoint Solutions (WSP cabinet files).

Solutions make your life easier in multiple ways by enabling you to :

  • Deploy assemblies to the GAC
  • Deploy files to the 12 folder on all the WFEs in your farm
  • Package multiple features together
  • Bundle in CAS policies and changes to the SharePoint web.config

I've seen that if you don't structure your project around Solutions to begin with, it's hard to steer it back in the right direction. Take the time and do it right up front!

Not to trivialize this, but once you've nailed deployability, this just becomes an exercise in using MSBuild...

Dependencies

Another important thing you need to take care of is to make sure all your project dependencies are available to TFS during the build.

For example, Microsoft.SharePoint.dll doesn't exist on the TFS server by default. You can either GAC it - and other dependencies - on the TFS server, or include it in your solution.

In the walkthrough in this post, I'll add Microsoft.SharePoint.dll into a solution folder and reference it from there. If you use this approach, make sure you store these dependencies at a high enough level in your source tree, so that you only keep 1 copy of each that every project can reference.

Walkthrough

I'm going to use a sample SharePoint project from an MSDN article written by Ted Pattison to put together a TFS build definition to build and deploy the project to a SharePoint server.

The most common approach I've seen involves using a custom build target in your Visual Studio solution to generate your SharePoint Solution WSP. This requires defining a new build target in your project's csproj or vbproj file and then including the necessary targets file in your Visual Studio solution.

Although you end splitting up pieces of your build script between the targets file and TFSBuild.proj, I'm ok with this because there's value in automatically generating the WSP during a "Desktop Build".

I'll show how you can extend this approach to allow TFS to build and deploy the SharePoint solution for you.

External Tools

The walkthrough uses the MSBuild Community Tasks project; this needs to be installed on the TFS server.

Although I'm not using them in the walkthrough, tools such as STSDEV and WSPBuilder have taken away the grunt work of handling the creation of the DDF and SharePoint WSP files. Definitely worth looking into. There's no reason why you can't integrate these tools into the MSBuild process.

OfficeSpaceFeature.targets and Cab.ddf

The use of a custom build target to run MAKECAB.exe and create a SharePoint WSP has been exhaustively discussed (see here and here), so I won't go into too much detail except to point out some changes.

When you compile in Visual Studio, binaries are dropped in bin\Debug|Release. However, when TFS compiles your code, it puts the binaries outside the root of your source code in Binaries\Debug|Release.

The DDF file used by MAKECAB.exe needs to point to a consistent location so the MAKECAB process handle both a desktop build and a TFS build.

In this case, in the AfterBuild target specified in the OfficeSpaceFeature.targets build target file, we copy the DLL to the DeploymentFiles directory.

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <MAKECAB>"C:\Windows\System32\makecab.exe"</MAKECAB>
    <DiskDirectory1>"$(OutDir)"</DiskDirectory1>
    <DiskDirectory1 Condition="HasTrailingSlash($(OutDir))">"$(OutDir)."</DiskDirectory1>
  </PropertyGroup>
  <Target Name="OfficeSpaceFeaturePackage">
    <Copy SourceFiles="$(TargetPath)" DestinationFiles="$(SourceDir)\DeploymentFiles\$(TargetFileName)"/>
    <Exec Command="$(MAKECAB) /F DeploymentFiles\Cab.ddf /D CabinetNameTemplate=$(MSBuildProjectName).wsp /D DiskDirectory1=$(DiskDirectory1)" />
  </Target>
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
  <UsingTask AssemblyFile="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" TaskName="MSBuild.Community.Tasks.MAKECAB" />
</Project>

You can see that in the DDF file, we always pull OfficeSpaceFeature.dll from within the DeploymentFiles directory.

.OPTION EXPLICIT
.Set CabinetNameTemplate=OfficeSpaceFeature.wsp
.Set Cabinet=on
.Set MaxDiskSize=0
.Set CompressionType=MSZIP;
.Set DiskDirectoryTemplate=CDROM;

DeploymentFiles\manifest.xml manifest.xml

TEMPLATE\FEATURES\OfficeSpaceFeature\feature.xml OfficeSpaceFeature\feature.xml
TEMPLATE\FEATURES\OfficeSpaceFeature\elements.xml OfficeSpaceFeature\elements.xml
TEMPLATE\FEATURES\OfficeSpaceFeature\LetterTemplate.docx OfficeSpaceFeature\LetterTemplate.docx

TEMPLATE\LAYOUTS\OfficeSpace\LetterGenerator.aspx LAYOUTS\OfficeSpace\LetterGenerator.aspx
TEMPLATE\IMAGES\OfficeSpace\PithHelmet.gif IMAGES\OfficeSpace\PithHelmet.gif

DeploymentFiles\OfficeSpaceFeature.dll OfficeSpaceFeature.dll

Create a TFS Build Definition

Go through the New Build Definition wizard and create a simple TFS build definition for this project. Queue a new build, and take a look in the build's drop location. You can see that the WSP was automatically created during the build and deposited in the drop location.

Doing More with the Build Definition

Let's do a couple of more interesting things with the build definition.

When you created the build definition, a file called TFSBuild.proj was created in source control. Create a file called Environment.proj in the same directory, we're going to use this file to store some parameters for the build.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <CopySharePointWSPTo>\\SharePointWFE\Drop</CopySharePointWSPTo>
    <SolutionDeploymentUrl>http://portal</SolutionDeploymentUrl>
    <STSADM>"%commonprogramfiles%\microsoft shared\web server extensions\12\bin\stsadm.exe"</STSADM>
  </PropertyGroup>
</Project>

By pulling out configuration settings - such as the portal Url - to a separate configuration file, you can use the same build definition in different environments. All you need to change are the values in Environment.proj.

In TFSBuild.proj, you can reference Environment.proj as follows:

<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">

  <!-- Do not edit this -->
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />

  <Import Project="Environment.proj"/>

You can now refer to the variables in Environment.proj using the MSBuild syntax for variables, e.g. $(SolutionDeploymentUrl)

As the first step in our deployment project, we want to copy the WSP to a location on one of the SharePoint Web Front End servers. We define this network share in the CopySharePointWSPTo property.

We can add a custom BuildStep that outputs the progress to the TFS Build Explorer. We then use the CreateItem syntax to select all the WSP files in the drop location, and copy them to the location at $(CopySharePointWSPTo)\$(BuildNumber)

  <Target Name="AfterDropBuild">
    <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
      BuildUri="$(BuildUri)" Name="Copy SharePoint Solution WSP to drop location"
      Message="Copy SharePoint Solution WSP to drop location $(CopySharePointWSPTo)\$(BuildNumber)">
      <Output TaskParameter="Id" PropertyName="CopyWSPStepId" />
    </BuildStep>

    <CreateItem Include="$(DropLocation)\$(BuildNumber)\**\*.wsp">
      <Output ItemName="SolutionWSPs" TaskParameter="Include"/>
    </CreateItem>
    <Copy SourceFiles="@(SolutionWSPs)" DestinationFolder="$(CopySharePointWSPTo)\$(BuildNumber)"/>
  </Target>

You can see the sequence of build steps in the TFS Build Explorer.

What's Next

In the next post, I'll demonstrate how to automatically generate the stsadm scripts necessary to deploy the WSP Solution into SharePoint. We'll also take a look out how to automatically deploy the solution.

Update (10/28/2008)

I wanted to clean up some of the tasks happening in the AfterBuild target that gets executed during a Desktop Build and TFS Build.

It appears that some MSBuild variables aren't being properly recognized during a Desktop Build, I found that my Copy task was failing during a Desktop Build. Here's my updated OfficeSpaceFeature.targets:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   3:    <PropertyGroup>
   4:      <MAKECAB>"C:\Windows\System32\makecab.exe"</MAKECAB>
   5:      <DiskDirectory1>"$(OutDir)"</DiskDirectory1>
   6:      <DiskDirectory1 Condition="HasTrailingSlash($(OutDir))">"$(OutDir)."</DiskDirectory1>
   7:    </PropertyGroup>
   8:    <Target Name="OfficeSpaceFeaturePackage">
   9:      <Copy ContinueOnError="true" SourceFiles="$(TargetPath)" DestinationFolder="DeploymentFiles"/>
  10:      <Copy Condition="'$(IsDesktopBuild)'=='false'" SourceFiles="$(TargetPath)" 
  11:            DestinationFiles="$(SourceDir)\DeploymentFiles\$(TargetFileName)"/>
  12:      <Exec Command="$(MAKECAB) /F DeploymentFiles\Cab.ddf /D CabinetNameTemplate=$(MSBuildProjectName).wsp 
  13:            /D DiskDirectory1=$(DiskDirectory1)" />
  14:      <Delete ContinueOnError="true" Files="DeploymentFiles\$(TargetFileName)"/>
  15:    </Target>
  16:    <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
  17:    <UsingTask AssemblyFile="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll" 
  18:               TaskName="MSBuild.Community.Tasks.MAKECAB" />
  19:  </Project>

If there's a better way to do this, please post it in the comments - I'm now doing two Copy steps to handle both scenarios. It's ok if the first one fails, it will generate a Warning in MSBuild - I didn't add an $(IsDesktopBuild) condition to it because it wasn't recognizing the condition.

TFS Builds for SharePoint Projects ... It's Hard!

Current Landscape

I recently started looking into what it would take to use Team Foundation Server to run automated builds and deployments of SharePoint projects.

There's quite a bit of material out there that demonstrates how to use Visual Studio to create deployment packages for SharePoint solutions, e.g. using a build target to have MAKECAB create a WSP for a custom SharePoint solution.

However, there's little to no material that shows how to get TFS to do this for you. Ideally, I would like to queue up a new Team Build, have TFS compile my code and create a WSP for my SharePoint solution, and finally deploy it to a selected SharePoint environment.

The stuff I've found makes flawed assumptions, such as having SharePoint and TFS on the same server. Umm, no.

This is getting more important as large companies turn to SharePoint as an application platform. Some of these companies have to comply with regulations such as Sarbanes-Oxley, which call for separating the roles of people who develop and deploy the software.

Lack of Guidance

Frankly, I'm disappointed in the lack of guidance from Microsoft on this subject. Articles are only recently starting to appear on MSDN about the importance of application lifecycle management practices for custom SharePoint applications.

The problem with these is that they tell you what you should do, but not how to do it! It's as if somebody just realized that this is important.

I have no doubt that this guidance will be available soon, I'm just amazed that it took so long.

Why is this Difficult?

How did it get to this? Well ... when building SharePoint projects, developers just weren't considering the "deployability" of their code.

Tools like the Visual Studio Extensions for WSS hide this issue from the developer. F5 deployment to SharePoint, WOW! Deployment managers were still tapping out stsadm commands to deploy features and solutions into SharePoint.

Getting TFS to do this for you also gets a little complicated. The TFS server performing the builds needs all your solution's dependencies, e.g. all the SharePoint dlls you might be using. You end up having to include these in your solution so that they are available to TFS when compiling.

Also, if you don't have TFS and SharePoint installed on the same server (and who does?), how do you deploy your solution to the SharePoint farm.

I don't have the answers, I wouldn't be writing this post if I did :)

I've seen some promising tools such as SPALM, which use the Microsoft Guidance Automation Toolkit to create a SharePoint software factory. I have to play around with this a little more, but it looks like the most promising tool out there.

What's Next?

I'm going to be working on this over the next two weeks and plan to share my findings.

We also have a Deployment and Packaging session happening at MOSSCamp 2008 in a few weeks. I will share all my learnings - successful or not - in that session.

If we have something working, I'll be sure to post it to the MOSSCamp CodePlex repository.

Let's Hear about your Experience

Are you doing this in your organization? I would love to hear about how people are doing this.

OCS 2007 R2 Announced

The next version of OCS, Office Communications Server 2007 R2 was announced this past Monday at VoiceCon Amsterdam 2008.

Check out Chris Mayo's post in which he describes some of the new features of R2, including:

  • Attendant console
  • Desktop sharing without using LiveMeeting
  • Dial-in phone conferencing (yay!)

If you do development on the UC platform and are planning to be at PDC, check out Chris's post about some of the UC sessions.

Also, don't miss out on the Hands-On Labs at PDC! There's some very exciting stuff happening with the UC SDKs, and you can be among the first to see and work with the new bits.

Building a Custom List using the Persona WPF Presence Control

Please check out my previous post about the WPF Presence Controls for Office Communicator 2007 for an overview of the controls and some examples of how to use them. This post will demonstrate how to build a simple custom WPF ListBox that uses the Persona control and binds to a custom collection.

The controls already include a PersonaList control, so why would you want to do this?

By building the list from scratch, you can control the look and feel of the list using a DataTemplate, and also bind the list to custom data specific to your application. You also now have full control over implementing the user's interaction with the custom list.

Let's start with a class that represents the entity to display in the list, we can use an example of sales associates working at different branches of a store.

We can now start building the DataTemplate that the custom ListBox will use. I've intentionally barely applied any styling here - mostly because I don't know how to :)

You'll also want to put this DataTemplate in a Resource Dictionary in your project if you plan to use it in more than one place (or just to keep things well organized). Otherwise, just drop it in the Resources section of your page.

 

Remember that SipUri is defined as a DependencyProperty of the Persona control, allowing us to bind to it using the {Binding Path=...} syntax.

At this point, all you need to do is create a ListBox and set its ItemTemplate to the DataTemplate you created:

Let's populate the ListBox with some data - I've over-simplified here for the sake of example and dropped this code into the page constructor.

This is what you end up with:

This is still a simple list, but it demonstrates that you can build a custom list - and style it however you want - while still taking advantage of the Persona control that comes with the WPF Presence Controls for Office Communicator 2007.