Friday, 5 March 2010

Enable testing of web services

I’m bound to forever forget how to switch on (or off) the ASP.NET web service test screen and documentation options. For future reference:

http://msdn.microsoft.com/en-us/library/b2c0ew36(VS.85).aspx

or add this to the system.web section of the service’s web.config file:

<webServices>
        <protocols>
            <add name="HttpSoap12"/>
            <add name="HttpSoap"/>
            <add name="HttpGet"/>
            <add name="HttpPost"/>
        </protocols>
</webServices>

Wednesday, 3 March 2010

Fiddler redirects localhost. to IP address

Debugging local HTTP traffic using Fiddler, while not intuitive, is easily accomplished by adding a dot to your localhost address. In other words, http://localhost:1234 becomes http://localhost.:1234 and traffic is captured like it always is.

This wasn’t quite working for me this morning: localhost was resolving to the IP address of the host machine, without the port or directory bits. I’d earlier disabled capturing by clicking the “Capturing” button in the status bar and that seemed to be causing this behaviour; enabling capture once again and the old dot trick worked again.

Incidentally, after closing Fiddler, I noticed IE8 does the same thing so this is likely a browser behaviour rather than a Fiddler oddity.

Friday, 12 February 2010

Best Practice List Development in SharePoint

I had the honour of speaking on the subject of best practice SharePoint list development at last weekend’s SharePoint Saturday event held in Perth, Western Australia but I want to take this discussion one step further. I’m therefore creating this post to point to my slide deck from the event and as an index for follow-on, detailed posts about some of the items I had to gloss over. There’s precious little out there, in a single location, on the subject of list development guidelines and I’m hoping to change that. If you want to contribute what you feel is a best practice I’ve missed or challenge one of the practices I do have listed, please comment and join the discussion.

Slides are available here: Best Practices for Developing SharePoint Lists (PowerPoint | PDF)

Here are the two key resources I cite in the presentation:

Wednesday, 10 February 2010

YSlow Configuration “Use a Content Delivery Network (CDN)”

The westernaustralia.com site is served to users all over the world from the most remote city in the world: Perth, Western Australia. In other words, we target users in core markets across Australasia, Europe, and North America and while we expect a consistent homepage load time below ten seconds in all cases, Perth to New York, Perth to London, and, really, Perth to anywhere equates to high latency, congestion, and poor response times. Couple that with SharePoint, a rich, interactive site design, and an excessive number of page requests and you'll begin to appreciate why we chose to serve the site across Akamai's content delivery network.

I'll describe the setup in detail someday but for now, I've finally managed to convince YSlow the site is being served using a CDN; while we use better tools for global performance measurement (Gomez), seeing a low grade—in part because the hundred-odd requests were perceived to be served without a CDN—was starting to get me down ;) YSlow is a great plugin for analysing your site in Firebug but it's grading system can be a bit tough.

The YSlow FAQ will tell you how to configure the plugin to acknowledge the use of a CDN by setting a extensions.yslow.cdnHostnames preference. I won't repeat those details here but will expand on how the string value might be configured to tell YSlow a CDN is in use—whether you’re telling the truth or not. It’s surprisingly simple.

In our particular case, www.westernaustralia.com is a CNAME to an Akamai URL so telling YSlow to use akamai.com as a CDN doesn’t make sense and YSlow isn’t smart enough to figure things out on its own. Since www.westernaustralia.com is implicitly its own CDN, configuring the the extensions.yslow.cdnHostnames value as westernaustralia.com sorts everything out. I had to restart Firefox (3.5.x) and reload the page for things to take effect.

If you’re not using a CDN and want to turn off this particular component of your grade (or at least make it an “A”), simply add your own URL; regardless of whether you’re actually using a CDN, YSlow will think you are.

SharePoint 404: The resource cannot be found

If a page element is missing—either a user control hasn’t been deployed or something else that needs to be in place for the page to compile dynamically is awol—you might be the proud owner of a 404 even though the page itself does exist.

HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

After setting the SafeMode element’s CallStack element true in web.config, turning off CustomErrors, and setting the debug=”true” attribute on the compilation element, you’re possibly looking at a beautiful ASP.NET error message with no stack trace and not a lot to go on.

Well here’s a trick: view source on the error page and and scroll down below the closing HTML tag. Behold the stack trace! It should tell you exactly where to look for the problem.

Tuesday, 9 February 2010

westernaustralia.com Relaunch and Other Website Recent Activities

If you’ve been following the progress of the westernaustralia.com website over the years, you’ll already know the site was one of the first in Australia to be launched on the MOSS 2007 platform and has been consistently voted as one of the top fully branded SharePoint sites in the world ever since. Having worked on the site since the get-go and as the current technical lead for the project, I’m the proud owner of the site’s programmatic aspects; today I’m tickled pink to tell you, the SharePoint community, about yesterday’s launch of the new Tourism Western Australia brand, Experience Extraordinary, and, more specifically, the brand’s impact on the westernaustralia.com website.

At the moment we’re working with the Digital Marketing team on a two (or three phase) approach (depending on how you look at it) to implement the new brand. The first phase unfolded in August last year and involved the neutralisation of the preceding brand’s elements (The Real Thing) while the latest creative design agency, Host, was appointed. Phase 1 of the new brand implementation kicked off in the dev team a little over three weeks back and involved making the Photoshop mockups provided by Host into a living website. This work focused largely on the page banners (vibrant imagery, the tab, and extra height), the navigation, the background colour (trust me—everything is more complicated than you might think at first glance with wa.com!), what we call the ePostcards element, and the footer elements.

Here’s a picture of what I think is a visually stunning website and a vast improvement over its predecessor:

wacom homepage 4 Experience Extraordinary Phase 1

Phase 2 holds still more secrets but you can bet we’ll be making all the middle bits fit with the rest of the changes. One of the challenges we’ll be facing is getting the home page weight down from a whopping 1750KB and 100 requests. Stunning, certainly, but our CDN of choice (Akamai) allows us to get away with this sort of monkey business.

In addition to all the glam, we also took the opportunity to overhaul the heading infrastructure. This subsystem is designed to give content editors the ability to upload new banner images and all of the corresponding display data required to render the Flash banner. The Flash banner itself was built by Host from our previous banner and it takes as a parameter an XML file providing all the information it needs to display the banner images, text, links, and colours.

Obviously this data is stored in a list to which the FlashBanner.cs web part communicates at render time. Like I say, nothing in wa.com is as it first appears and one of the more complex requirements was allowing a subsite to override the banners displayed for for it and its children (in effect to support the major subsections of the site such as the destinations). Apart from simply walking up the site hierarchy recursively until a banner list is found, we then needed interrogate the list and emit an XML file acceptable to the banner; the URL to the file is finally cached with a dependency on the file itself.

Interestingly, this was one of the first times we started using event receivers on the banner lists to invalidate the cache when a banner is added, edited, or deleted; this keeps the content editors happy and productive and is one less of those annoying “is my banner cached in the browser cache | Akamai | reverse proxy| application cache | output cache | blob cache?” questions ;)

I’ll also point out we’re using a simple custom list for this instead of a picture library or custom list derived from a picture library. The previous version of this list was in fact a custom picture library but it suffered from broken thumbnails (which we now know how to fix) and proved rather cumbersome to use in practice (no reordering, a difficult view, etc). The move away from the picture library wasn’t, in retrospect, necessarily a great decision but it was probably the best decision at the time and came with considerable deliberation. The one major problem with the current approach is banner images must be uploaded separately to a real picture library and then referenced from the list; this is a bit of a double-up and disconnects the image itself from its metadata. As our understanding of list development and the peculiarities of these other list types increases, we may revisit this approach.

Also at a technical level, we recently move the site from our old IBM BladeCenter kit running Windows Server 2003 x86, MOSS 2007 SP1 (not even the Infrastructure Updates—gasp!), .NET 3.0, and a non-clustered SQL Server 2005 x86 database server. Everything was virtualised on ESX 4.0. We actually had three of these farms: one in prod, one in DR, and one for authoring; as content deployment never worked for the site, this meant a daily backup and restore of the content database from authoring to production and corresponding switch to or from DR (with DR actually being treated as a standby prod environment). That must have really sucked for our admin who had did that essentially manual task every business day since launch in May 2007. What. A. Drag.

From that setup, we moved to what was briefly the bees knees: Windows Server 2008 x64, MOSS 2007 SP2 + June 2009 CU, .NET 3.5 SP1, and a clustered SQL Server 2008 x64 database environment. Everything is still virtualised on ESX 4.0 and the entire setup is mirrored in DR. We’ve finally done away with the authoring farm so content editors are editing content in the production farm (we’re working towards a tiered security design and approval workflows, by the way, but our content editors have been working with the environment for years now and are mature in their understanding of how not to break the site ;). So no more daily content deployments and those of us in the dev team can finally start working with .NET 3.5 and LINQ.

For more on how we run the site, check out my Perth SharePoint User Group presentations and videos (things have changed but not that much):

http://blog.mediawhole.com/2009/02/how-we-do-wcm-at-tourism-wa-im-speaking.html

http://blog.mediawhole.com/2009/03/presenting-at-next-sharepoint-user.html

If you’ve read this far, you deserve the chance to win a prize. Want the opportunity to take an extraordinary taxi ride around the state of Western Australia? If so, check out the “brand activation” microsite: http://www.extraordinarytaxiride.com.au/

Note: I am employed by Tourism WA as a contractor working for Diversus. Mediawhole, mediawhole.com, and this blog are not associated directly with Tourism Western Australia or the westernaustralia.com website. The information provided above  is published independently as a member of the public and does not reflect the views of Tourism Western Australia or Diversus. Please consult a Tourism WA representative for more information about its brand, campaigns, and websites.

Friday, 22 January 2010

Buying a laptop? Make sure it supports VT

Just borrowed a colleague’s Toshiba M300/Intel Core2 Duo Mobile Processor P7350 laptop only to find the CPU doesn’t support VT (or the Intel Virtualization Technology/hardware-assisted virtualisation) required by Hyper-V and VMware; I was planning to use this laptop for Hyper-V-based demos. Rats.

The laptop in question is only six months old and otherwise nicely equipped with 4GB of RAM and a 320GB hard drive so it’s a shame about this niggle… back to Virtual PC and 32-bit for me I suppose—how quaint!

Which leads me this mental note: when buying a laptop go to extreme lengths to determine whether the CPU offers VT and do some Googling to determine whether it can actually be enabled in the BIOS (apparently the various manufacturers can disable/enable VT at their discretion).

In the P7350 case, Intel’s technical data was incorrect on publication—originally specifying support for VT. If you’ve actually got one of these and are reading this post, sorry mate—hope you weren’t planning to do too much 64-bit virtualisation!

Wednesday, 20 January 2010

SharePoint 2007 Slipstreamed Installer

I hadn’t noticed previously but Microsoft released slipstreamed SP2 installers (x86 and x64) at the end of 2009:

http://www.microsoft.com/downloads/details.aspx?FamilyId=3015FDE4-85F6-4CBC-812D-55701FBFB563&displaylang=en

http://www.microsoft.com/downloads/details.aspx?familyid=2e6e5a9c-ebf6-4f7f-8467-f4de6bd6b831&displaylang=en

Makes life a little bit easier so you don’t have to do the slipstreaming yourself. Apparently previous installers will not work with Windows Server 2008 R2.

Saturday, 16 January 2010

Manage Active Directory Users and Groups with PowerShell

With a new dawn and the SharePoint 2010 beta upon us, I figured it was finally time to a) dip my toes into the PowerShell pool and b) learn how to automate the creation of users, groups, organisational units, and other objects within Active Directory. In the PS case, it still seems like an unnecessary evil and, between all the other things on my “TO LEARN” list, it hasn’t been a high priority; that said, it seems to work and I kind of, sort of, maybe just like it. We’ll see… On the automation front, I figured I’ll probably blow away more than my fair share of development environments before my time is up so I might as well make creating the darned things a little bit easier and more repeatable.

My search for information along these lines quickly led me to Quest’s ActiveRoles Management Shell for Active Directory—a set of PowerShell commandlets. The download is free and if you grab it I’d also strongly suggest you pull down the admin guide as well—it’s comprehensive and includes examples. To install, run the .msi and finish the wizard with the defaults.

From there you’ll need to add the related snap-in in a PS window as follows:

Add-PSSnapin Quest.ActiveRoles.ADManagement

The first thing I wanted to create was a user object and this was reasonably straightforward. Be warned some of the commandlets take a lot of arguments, the names of which don’t always correspond to the UI you see in the AD Users and Computers snap-in: password is –UserPassword, for example, and Job Title is simply –Title. Here’s the command I used:

new-QADUser -name 'Bob' -ParentContainer 'OU=DirtyWords,DC=spdev,DC=mediawhole,DC=com' -SamAccountName 'Bob' -UserPassword 'TH1Smis1s' -FirstName 'Bob' -DisplayName 'Bob' -UserPrincipalName 'bob@spsdev.mediawhole.com' -Title CEO

This creates the user but new-QADUser doesn’t allow you set all of the things I want to using the standard parameters. In my case, I also want to set the account to never expire. To do this, I used the set-QADUser commandlet as well, which seems to let you get to much more detail:

set-QADUser 'CN=Bob,OU=DirtyWords,DC=spdev,DC=mediawhole,DC=com' -PasswordNeverExpires $true

Next I created a new group using the new-QADGroup commandlet:

new-QADGroup -ParentContainer 'OU=DirtyWords,DC=spdev,DC=mediawhole,DC=com' -name 'Managers' -samAccountName 'Managers' -grouptype 'Security' -groupscope 'Global'

…before adding my new user to my new group with the add-QADGroupMember commandlet:

add-QADGroupMember -identity 'CN=Managers,OU=DirtyWords,DC=spdev,DC=mediawhole,DC=com' -member 'CN=Bob,OU=DirtyWords,DC=spdev,DC=mediawhole,DC=com'

This last exercise proved somewhat troublesome as the examples in the admin guide and specific documentation about the –member parameter indicated I could supply ‘spsdev\Bob’ in place of the string above I ended up using successfully. The error message was fairly explicit about this:

Add-QADGroupMember : Cannot resolve directory object for the given identity: 'spsdev.mediawhole.com\bob'.

One other commandlet I haven’t yet managed to get working is the new-QADObject commandlet to create a new OU:

new-QADObject -ParentContainer 'DC=spsdev,DC=mediawhole,DC=com' -type 'organizationalUnit' -NamingProperty 'ou' -name 'Dirty Words'

This command fails on the type parameter with;

New-QADObject : A referral was returned from the server…

+ CategoryInfo          : NotSpecified: (:) [New-QADObject], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Quest.ActiveRoles.ArsPowerShellSnapIn.Powers
hell.Cmdlets.NewObjectCmdlet

I’ll probably look back at this post one day after figuring this one out and laugh at my lack of understanding but for now it’s got me stumped.

Tuesday, 12 January 2010

Can’t start Hyper-V VM after Windows Server 2008 R2 upgrade

Oh the tedium… Having used Windows Server 2008 R2 for a while, I was keen to upgrade the Hyper-V host environment supporting my dev infrastructure from RTM to R2. R2 seems faster and more refined; I was also wanting to play with the latest version of the System Center Virtual Machine Manager—which requires R2.

The upgrade process went smoothly once I figured out the installer would eventually present me with the upgrade option after telling it to install the Windows Server 2008 R2 Standard (Full Installation) option; this was briefly confusing to me  since I didn’t want to do a full installation! I shut down all my VMs properly beforehand and everything seemed pretty much good (I did have to recreate the one VM I normally auto-start but luckily I was able to reuse the .vhd itself and the machine came back to life with little hassle).

Today, however, I went to create a new VM but was unable to start it post-creation. Clicking start popped up a message telling me the VM could not initialize and Hyper-V could not create or access saved state file. The physical location of my VMs hadn’t changed but I read this error message as a permissions issue—but where to start?! The only thing I had to go on was that I was creating the new VM on another volume.

Fortunately Jevgenij Martynenko saved the day for myself and others in this forum post with the old trick of granting access to the root of the drive. I remember having to do this for ASP.NET back in the day for some reason…

Anyway, as per the instructions, I granted ‘Authenticated Users’ ‘List folder / read data’ permission to the root of my E:\ drive, setting the scope drop down to This folder only. I did not replace all child object permissions as apparently that causes more problems.

Starting the new VM was successful after this change.

Windows Server 2008 R2 Scratch Disk

When building out a new VM I’ve occasionally created a second disk for holding non-system files and temporary data; this disk is easy to blow away every so often and saves the hassles of defragging and compressing.

I recently built a Windows Server 2008 R2 VM and created a system drive and secondary drive in Hyper-V manager before installing the OS. During the OS installation process, I created a new partition and noticed—but thought nothing of—the fact the installer didn’t nag me about creating a reserved system partition as well.

Carrying on my merry way, I eventually found the secret system reserved partition on my “scratch” disk (d:\) when I went to format what I expected was a completely unallocated disk. Bummer—I’m not clear on the ramifications of simply deleting this reserved partition but it currently means dragging around an additional 6GB of D: drive and being unable to blow away my scratch disk.

Saturday, 9 January 2010

I’m Speaking at SharePoint Saturday Perth this February

How exciting—our very first SharePoint Saturday event in Perth! A BIG shout out to Jeremy Thake for organising the venue, speakers, sponsors, prizes, and everything else! The speaker line-up looks fantastic and the sessions cover a range of topics so I reckon this is going to be a great day.

In addition to attending the event, I’ll also be presenting on the subject of SharePoint list development. Here’s the official blurb for my talk:

Lists are key to understanding the power of the SharePoint platform and provide core data storage and management facilities in a SharePoint environment. Lists can be customised to suit custom requirements but doing so isn’t always straightforward.

Learn how to create, deploy, manage, and interact with SharePoint lists in this jam-packed session. Discussion will cover the pros and cons of using SharePoint lists before focusing on methods for creating and deploying lists between environments in a repeatable manner; techniques for working with lists and data using the SharePoint API with also be demonstrated.

Existing experience with the SharePoint UI, a cursory knowledge of content types, and ASP.NET development experience is recommended but not assumed.

If there’s anything you’d like me to cover in particular please drop me a line or post a comment below. If all goes well I’ll also be recording the session so feel free to make a request or post a question even if you’re unable to attend in person.

Finally, Diversus are sponsoring the event and their package includes a JVC video camera to be given away—thanks guys!!!

Diversus - Perth SharePoint Development

Friday, 8 January 2010

Add and manage workflows using the API

Programmatically applying and configuring an out-of-the-box workflow to a SharePoint list isn’t the most obvious thing to do but fortunately it’s not exactly hard either. I had to dig around a bit to find the pieces I need to do this and thought I’d share…

List Settings - Versioning

In my case I’m applying an approval workflow to a Pages library created by the Publishing feature; before I wrangle the workflow I need to configure the versioning settings on my Pages list. Through the UI, I can do this by configuring these settings as follows:Configure_List_Versioning_for_WorkflowEssentially, content approval is required, users need to be able to create major and minor (draft) versions, all editors should be able to see all drafts, and checkout is required to edit documents.

To do this setup programmatically, I set the following properties on my Pages list:

SPList pages = web.Lists ["Pages"];

// Require content approval
pages.EnableModeration = true;
// Create major and minor versions
pages.EnableVersioning = true;
pages.EnableMinorVersions = true;
// Who should see draft items
pages.DraftVersionVisibility = DraftVisibilityType.Author;
// Require documents to be checked out before they can be edited
pages.ForceCheckout = true;

pages.Update ();

Remove Existing Workflows

In some cases, you may also want to cleanup a list’s existing workflows (i.e. remove them) and this is easily accomplished using the SPList.RemoveWorkflowAssociation() method. Note several examples on the interwebs suggest doing this within a foreach loop but doing so will invalidate the enumerator after the first item is removed; use a for loop or a while loop instead:

// Remove existing workflows
for (int associationIndex = 0; associationIndex < pages.WorkflowAssociations.Count; associationIndex++)
{
    SPWorkflowAssociation workflowAssociation = pages.WorkflowAssociations [associationIndex];
    pages.RemoveWorkflowAssociation (workflowAssociation);
}

If you need to remove individual workflows, inspect the SPWorkflowAssociation.Name property.

Workflow Setup…

With that out of the way, let’s move on to the workflow configuration proper. In short, it’s all about the collection of SPWorkflowAssociation objects exposed through the WorkflowAssociations property of the SPList class. The AssociationData property exposed by the SPWorkflowAssociation class is also crucial.

Task and History List Management

When you setup a workflow through the UI, you’ll be prompted to specify names for the task and history lists (the latter of which will be hidden); when you’re doing this programmatically, you have to manage this part yourself. It would have been nice if the API did this automatically but it doesn’t. The CreateListAssociation method we’ll examine in a moment also requires a reference to each list so you need to a) determine if each list exists or create it and b) retrieve a reference to each list. To wrap all of this logic up together, here’s my helper method (based on this by Marco):

private void EnsureWorkflowLists (SPWeb web, out SPList taskList, out SPList historyList)
{
    const string WORKFLOW_TASK_LIST_NAME = "Workflow Tasks";
    const string WORKFLOW_HISTORY_LIST_NAME = "Workflow History";

    // Task list

    try
    {
        taskList = web.Lists [WORKFLOW_TASK_LIST_NAME];

        Console.WriteLine ("Tasks list found.");
    }
    catch (System.ArgumentException)
    {
        // List does not exist so create it
        Guid taskListId = web.Lists.Add (WORKFLOW_TASK_LIST_NAME, string.Empty, SPListTemplateType.Tasks);
        taskList = web.Lists [taskListId];

        Console.WriteLine ("Created task list for web.");
    }

    // History List

    try
    {
        historyList = web.Lists [WORKFLOW_HISTORY_LIST_NAME];
        Console.WriteLine ("History list found.");
    }
    catch (ArgumentException)
    {
        Guid historyListId = web.Lists.Add (WORKFLOW_HISTORY_LIST_NAME, string.Empty, SPListTemplateType.WorkflowHistory);
        historyList = web.Lists [historyListId];
        historyList.Hidden = true;
        historyList.Update ();

        Console.WriteLine ("Created history list for web.");
    }                        
}

Call EnsureWorkflowLists method like so:

SPList taskList;
SPList historyList;
EnsureWorkflowLists (web, out taskList, out historyList);

Workflow Template

Before you can finally create the new workflow association, you’ll also need an SPWorkflowTemplate. You can do this by calling the GetTemplateByName method hanging indirectly off the SPWeb object:

SPWorkflowTemplate approvalTemplate = web.WorkflowTemplates.GetTemplateByName ("Approval", System.Globalization.CultureInfo.CurrentCulture);

If you have any issues here, ensure the Routing Workflows feature (or the relevant feature for whatever OOB workflow you’re dealing with) is enabled at the site collection level (/_layouts/ManageFeatures.aspx?Scope=Site).

Create the Workflow Association

With the leg work behind us, actually applying a new workflow association to the list and configuring it is quite easy, with one exception I’ll cover in a moment. To begin, create a new workflow association using the SPWorkflowAssociation.CreateListAssociation method and supply the workflow template and list references created previously:

SPWorkflowAssociation approvalWorkFlowAssociation = SPWorkflowAssociation.CreateListAssociation (approvalTemplate, "Page Approval", taskList, historyList);

Note the name parameter “Page Approval” is arbitrary but will appear in the UI and notification emails; I therefore aim to make it user friendly and somewhat specific.

The created object can then be configured, in particular using the AllowManual, AutoStartChange, and AutoStartCreate properties.

As mentioned, there’s one last hurdle and that’s providing the XML string to the SPWorkflowAssociation.AssociationData property. As far as I’m aware, this is the only way to specify the nominated approvers group and a few other properties such as the ability to delegate tasks and whether a change to the list item should interrupt the current workflow. I believe the easiest way to create this string is to copy an example (below) or configure the workflow as desired in the UI and use SharePoint Manager to extract the XML from the workflow association (highlighted in the image below).

WorkflowAssociations_AssociationData_SPM

<my:myFields xml:lang="en-us" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD">
<my:Reviewers>
<my:Person>
<my:DisplayName>Approvers</my:DisplayName>
<my:AccountId>Approvers</my:AccountId>
<my:AccountType>SharePointGroup</my:AccountType>
</my:Person>
</my:Reviewers>
<my:CC></my:CC>
<my:DueDate xsi:nil="true"></my:DueDate>
<my:Description></my:Description>
<my:Title></my:Title>
<my:DefaultTaskType>1</my:DefaultTaskType>
<my:CreateTasksInSerial>false</my:CreateTasksInSerial>
<my:AllowDelegation>true</my:AllowDelegation>
<my:AllowChangeRequests>true</my:AllowChangeRequests>
<my:StopOnAnyReject>true</my:StopOnAnyReject>
<my:WantedTasks xsi:nil="true"></my:WantedTasks>
<my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>
<my:MetadataSuccessField></my:MetadataSuccessField>
<my:MetadataSuccessValue></my:MetadataSuccessValue>
<my:ApproveWhenComplete>true</my:ApproveWhenComplete>
<my:TimePerTaskVal xsi:nil="true"></my:TimePerTaskVal>
<my:TimePerTaskType xsi:nil="true"></my:TimePerTaskType>
<my:Voting>false</my:Voting>
<my:MetadataTriggerField></my:MetadataTriggerField>
<my:MetadataTriggerValue></my:MetadataTriggerValue>
<my:InitLock>true</my:InitLock>
<my:MetadataStop>false</my:MetadataStop>
<my:ItemChangeStop>true</my:ItemChangeStop>
<my:GroupTasks>true</my:GroupTasks>
</my:myFields>

Drop the XML into the SPWorkflowAssociation object you just created using the AssociationData property and you’re all set to add the association to your list using the SPList.AddWorkflowAssociation method:

pages.AddWorkflowAssociation (approvalWorkFlowAssociation);

Before updating the list list, you may also want to configure your new workflow as the default content approval workflow:

pages.DefaultContentApprovalWorkflowId = approvalWorkFlowAssociation.Id;

Follow that with pages.Update () and you’re done!

Monday, 4 January 2010

Enable ICMP echo (ping) in Windows Server 2008 R2

Windows Server 2008 won’t respond to ping requests out of the box—they’re blocked by default in the Windows Firewall ruleset. In the pre-R2 days, I used a simple command to enable ping in my development environments but apparently netsh firewall has been deprecated:

netsh firewall set icmpsetting 8

The replacement is—wait for it—netsh advfirewall firewall, like so:

netsh advfirewall firewall add rule name=”ICMP Allow incoming V4 echo request” protocol=icmpv4:8,any dir=in action=allow

Richard Siddaway has a PowerShell equivalent (presumably, I haven’t tried it myself but should have, I know… tsk, tsk).

Thursday, 24 December 2009

(!) All conflicts resolved but no files checked in due to initial conflicts

A minor annoyance with VS2008 SP1 and TFS 2008 SP1 today when attempting a check in. Here’s the stack trace:

  • Delete a folder and a bunch of images from VS Solution Explorer yesterday (right-click, Delete)
  • Check in the deletion and explicitly check for the delete action for the relevant files during the check in process
  • Arrive this morning to do a final cleanup and make sure everything in the project was checked in before Christmas… hey, all those images are still wanting to be checked in!
  • Attempt check in from Solution Explorer (specify comment, work item as per our policy configuration); Resolve Conflicts dialog pops up with the message “(!) All conflicts resolved but no files checked in due to initial conflicts”. Try again… believing, from memory, I’d seen this problem before and a second check in attempt had solved it. Same deal.
  • Resume Conflict Resolution from File –> Source Control. Nothing found, “All conflicts resolved”
  • Attempt check in from Source Control Explorer window in VS; same deal.
  • Drop into the VS 2008 Command Prompt and navigate to the solution; tf checkin and tf resolve produce the same results as VS. tf checkin tells me “Conflict: The latest version of item $/… is deleted.
  • Try tf resolve . /r /auto:AcceptTheirs but the command tells me “There are no conflicts to resolve”
  • Confirm folder and images are definitely not present in either the project (via Solution Explorer) or the source control tree (via Source Control Explorer).
  • Get Specific Version with ‘Overwrite all files even if the local version matches the specified version’ option checked; no change to the project or file system; same error message on check in.
  • Undo Pending Changes… from Solution Explorer context menu. Success! Now I have nothing left to check in (any other files not involved in the deletion process were checked in separately) and TFS is tangle-free yet again.

Installing Live Mesh Beta on Windows Server 2008 R2

It’s never easy… the downloader doesn’t seem to work from the web or somesuch, or at the very least add a new key named Installer below

HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer

and then create a DWORD named DisableMSI. Set the value to 0.

In my case I’d already downloaded the installer so I ran it locally, no change to UAC, no reboot, no –force switch on the installer. I’m meshed.

http://msdn.microsoft.com/en-us/library/aa368304(VS.85).aspx

errorData during SharePoint 2010 Products and Technologies Configuration Wizard

When specifying database settings for my new SharePoint 2010 beta farm, I came up against an error pop I’ve never seen before under 2007:

An error has occurred while validating the configuration settings.  An exception of type System.ArgumentNullException was thrown.  Additional exception information:  The errorData argument cannot be null or zero length.

Parameter name: errorData


M’kay. What does that mean?!? I wonder to myself.

Checking the application log revealed a critical error that set me straight:

SQL database login for 'master' on instance 'SP2010BETA' failed. Additional error information from SQL Server is included below.

Login failed for user 'SPDEV\Administrator'.


I’m trying a least privilege install and of was still logged in as the administrator, instead of the setup user (criminal); I configured the setup account as an admin during the SQL Server 2008 install but not the administrator account I was using.

Login as setup and I’m back on track. Of course,
by the sound of it, I shouldn't be doing least privilege in 2010.





Dirty Words (Michael Hanes) - blog.mediawhole.com mailto:info@mediawole.com

Monday, 21 December 2009

SharePoint Saturday Perth Announced

The very first SharePoint Saturday event is coming up in the new year (early February). Check out the details here and submit your speaker submissions by 4 Jan:

http://www.sharepointsaturday.org/perth/default.aspx

See you there!

Wednesday, 16 December 2009

Flushing the Blob Cache

Sean McDonough has a really comprehensive post on several options for flushing the MOSS 2007 blob cache. Worth a read if you’re in need:

http://sharepointinterface.com/2009/10/30/manually-clearing-the-moss-2007-blob-cache/

Inner exception details displayed instead of wrapper exception

Wrapping an exception using the Exception (string, Exception) constructor and throwing the new exception in place of the original isn’t behaving as expected for me today: ASP.NET 3.5 displays the inner exception message in its default exception display; I expected to see the wrapper exception message. Oddly enough, details pertaining to the wrapper are logged to the Application event log.

So this:

try {
// Setup to make something go wrong
throw new Exception ("Inner exception");
}
catch (Exception ex)
{
// Wrap the inner exception and throw
// a new exception instead
throw new Exception ("wrapper exception", ex);
}

spits out this (note both the message and source error clearly pointing at the inner exception line):

Inner exception

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Exception: Inner exception

Source Error:

Line 18: try
Line 19: {
Line 20: throw new Exception ("Inner exception");
Line 21: }
Line 22: catch (Exception ex)

Despite what the message and highlighted code reveal, the stack trace does imply the wrapper exception was involved along the way.

[Exception: Inner exception]
_Default.Page_Load(Object sender, EventArgs e) in Default.aspx.cs:20

[Exception: wrapper exception]
_Default.Page_Load(Object sender, EventArgs e) in Default.aspx.cs:24 …

Adding a second try/catch to catch any exception of type Exception and inspecting the results locates the inner exception property where I’d expect it to be. Alternatively, “swallowing” the inner exception in the original catch block and throwing a new exception using the Exception (string) constructor does just that—no sign of the inner exception. I’ve additionally tried catching different types of exception (I was originally throwing ApplicationExceptions and catch Exceptions) and constructing my own exception classes with the same effect.

Maybe I’m missing something but this seems like pretty basic stuff and a few other vaguely related search results hint that I’m not alone with this one. Of course, I can always handle all exceptions myself to prevent anything killing the application but the current approach is convenient at the moment for debugging outside production where it’s less convenient to attach a debugger or check the event logs.

If you’ve got any ideas or have come across this yourself, I’d love to hear your thoughts!