Wednesday, 23 June 2010

Use BackConnectionHostNames instead of DisableLoopbackCheck in production

If you're running SharePoint, you may have come across advice to configure the DisableLoopbackCheck registry key if you're running Windows 2003 SP1 and above and/or .NET 3.5 SP1.

Adding the DWORD will certainly keep you up and running and avoid search/crawl errors like the below but it's not the way to be doing in a production environment, despite popular belief.

Access is denied. Verify that either the Default Content Access Account has access to this repository, or add a crawl rule to crawl this repository. If the repository being crawled is a SharePoint repository, verify that the account you are using has "Full Read" permissions on the SharePoint Web Application being crawled. (The item was deleted because it was either not found or the crawler was denied access to it.)

What you should be doing in production is configuring specific sites by name using the BackConnectionHostNames Multi-String Value below HKEY_LM\system\CurrentControlSet\Control\LSA\MSV1.0. The Microsoft KB article isn't clear about the format this value should take but I've found adding each site without the scheme on a new line works.

Here's an example:

intranet1.site.com
intranet2.site.com

Bob Fox additionally suggests adding a new DWORD named DisableStrictNameChecking with a value of 1 to HKEY_LM\system\CurrentControlSet\Services\Lanmanserver\parameters and rebooting to avoid having to reboot every time a new site is configured. I got away without rebooting at all by simply restarting the IISAdmin service.

Saturday, 19 June 2010

Don't use XmlRoot with .webpart files

Just noticed using an [XmlRoot("MyRoot")] attribute on my webpart class in conjunction with a .webpart file (as opposed to a .dwp file) gives me this error when attempting to add the webpart to a page:

Incompatible Web Part markup detected. Use *.dwp Web Part XML instead of *.webpart Web Part XML.

Incompatible_web_part_markup_detected

Well I don't want to use *.dwp Web Part XML so I removed the XmlRoot attribute instead. I'm feeling wild, you see.

Beware the old doco: http://msdn.microsoft.com/en-us/library/dd584160(office.11).aspx 

How to add a web project item to a class library

This is an old trick but I've found myself hunting for it repeatedly as of late…

If your current visual studio project doesn't provide the option to add a web user control, web page, etc (perhaps because the project is a class library or a WSPBuilder project), you can easily tweak the project file enable support for these templates.

To do so:

  • Unload the project (right-click the project in Solution Explorer and select Unload Project from the context menu)
  • Edit the .csproj file (right-click the project in Solution Explorer and select Edit MyProj.csproj from the context menu)
  • Locate the ProjectTypeGuids element and add a magic project type GUID to beginning of the list: {349c5851-65df-11da-9384-00065b846f21};
  • Save your changes and reload the project (right-click and Reload Project)

When you're done editing the .csproj file it should look like this:

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Now you can add user controls and whatnot to your heart's content!

Update Feb 2011: When I last had to this in VS2010, things were a bit different. The class library contained a ProjectGuid element but no ProjectTypeGuids. According to the post I link to above, a ProjectTypeGuids element can be added below the ProjectGuid so I pasted in the above. Although VS "helped out" by rejigging the XML for me and modifying the GUID when I subsequently inspected it, the project allows me to add web items.

Each class library seems to get a different (unique) ProjectGuid value so I'm not sure how adding the child ProjectTypeGuids element (which was subsequently removed by VS) actually worked… but it did ;-)

Wednesday, 9 June 2010

TFSDeleteProject /Collection Parameter in TFS 2010

As far as I can tell, deleting a team project in TFS 2010 is still a task that must be completed at the VS2010 command line; while the operation is relatively straightforward, this command changes slightly with the introduction of project collections in TFS2010 and the value expected by /collection:<url> parameter may not be glaringly obvious.

Firstly, see the documentation.

Next, determine the URL for the team project you want to delete. I wasn't sure how to discover this but after some trial and error I found this format seems to work:

http://server:port/tfs/collectionName

I've since realised the URL can be determined from the TFS Administration Console (in the General tab under the Team Project Collections node) and other derivatives returned a 404 error.

Note http://server:port/tfs is the Server URL configured for my TFS instance in the Administration Console so your mileage may vary.

Monday, 7 June 2010

Australian Weather Data (RSS…)

Despite my best efforts, I've been disappointed in my (repeated) attempts over the last twelve months to locate a free Australian weather feed I can consume via RSS and present as I wish. The Bureau of Meteorology is operating in the dark ages when it comes to the distribution of its authoritative weather data, Yahoo! weather—although available in a nicely structured XML document—is not available for commercial use, and all other feeds available at the time of writing send down HTML-formatted data with very limited rights for modifying the HTML itself.

Naturally, this is all very frustrating, especially when your client's intranet site needs weather for three specific towns in the middle of the outback or you're trying to build a redistributable weather widget (SharePoint web part anyone?).

Having given up on a free RSS feed, I returned to the BoM site as they do provide weather data via FTP (they also provide registered users access to additional services for a subscription fee). The terms are a bit unclear about distribution and attribution so I'll leave them up to you to interpret. This post presents my approach to accessing and parsing the basic (free) weather data file. A bit more work is involved than simply pointing a nice XDocument at an RSS feed URL and LINQ-ing away but hey, lower-level stuff is always fun and educational ;-)

By the way, this code will eventually make its way into SharePoint web part; if you're interesting in licensing this web part for use on one of your own sites, please drop me a line – info@mediawhole.com.

Access

The BoM provides weather data in the form of .dat, .txt, .html, and other file types via anonymous FTP. The .dat file is what we're after and for this example, I'll be using the IDA00006.dat file (Western Australia).

.dat files contain forecast information by town with details separated by a hash (#) character. The first line in the file describes the file format so if you get lost is a sea of hashes, start there. The Precis Forecast Products page provides formal documentation, of a sort.

Since the .dat file is just a text file we'll be using the WebClient class to manage access to the FTP location and open a readable stream over the .dat file URL:

WebClient client = new WebClient();

using (Stream data = client.OpenRead("ftp://ftp2.bom.gov.au/anon/gen/fwo/IDA00006.dat";))
{
}

Parsing

With access to the file contents, we can now parse it for easier access. To do so, we'll use a StreamReader to enumerate the file, tokenize each line, and store the results in a generic list:

using (StreamReader reader = new StreamReader(data))
{
    List<List<string>> weatherData = new List<List<string>>();
    string currentLine;

    while ((currentLine = reader.ReadLine()) != null)
    {
        string[] tokens = currentLine.Split('#');
        weatherData.Add(tokens.ToList<string>());
    }
}

It's worth pointing out the nested list structure I'm using here in place of a dedicated collection type. Each line in the file represents data for a single town; individual data elements within each line are separated by a hash character. To accommodate this, I tokenise each data element using the String.Split() method after reading the next line and store those elements in a nested list; each outer list item therefore contains data for a single town.

If that sounds at all complicated, here's a picture of the end result:

NestedListsThe parsed data is finally cached (not shown) for subsequent access. Notably, the BoM doesn't specify any limits on access attempts per day or an effective TTL value as do many RSS feeds. There's nothing stopping you from retrieving this data with every page load but your site will likely slow down and you'll also be guilty of gumming up the intertubes. You may also run the risk of being blocked from accessing the site.

Data Extraction

The final step in this process is locate and extract the weather data you're after (most likely for a specific town). I use the List<T>.Find() method, supplying a predicate that matches on town name, to accomplish this:

List<string> town = weatherData.Find(currentTown => currentTown[WeatherIndex.Location].ToLowerInvariant() == "perth");

Final Touches

The free data doesn't provide any form of condition code so associating a forecast (e.g. "Mostly sunny") with an icon is going to be tricky. I'm hopeful the BoM uses standard phrases that can be matched (or at least partially matched as a fallback (e.g. contains "sun") against known terms for hookup to generic icons but this could become a maintenance problem.

But that's it, weather!

Wednesday, 2 June 2010

Upgrade SQL Server Edition or Change License Key

Apparently upgrading from a developer version of SQL Server is possible. These screenshots are from an MSDN installation of SQL Server 2008 and entering a non-MSDN key will presumably legitimise the bits for production use (I don’t have a retail or volume key available so this is a guess at this stage).

To get here, re-run the installation wizard and select Edition Upgrade from the Maintenance screen:

SQL Server License Key Change Edition Upgrade

Then enter your new product key or use a free edition instead:

SQL Server License Key Change Edition Upgrade 2

You can also do this at the command line:

Setup.exe /q /ACTION=editionupgrade /INSTANCENAME=<MSSQLSERVER or instancename> /PID=<PID key for new edition>" /IACCEPTSQLSERVERLICENSETERMS

Thursday, 20 May 2010

Disable the Output Cache Programmatically with PowerShell

Restoring a content database from a production environment to a development machine for dev purposes can sometimes get a little bit interesting.

Our production environments generally have output caching enabled for performance reasons(see /_Layouts/sitecachesettings.aspx in your own environment) but developing or troubleshooting an issue in dev normally requires output caching be disabled to you can view the results of your work. Of course the output cache settings are stored within the site collection so using a production backup will normally bring the output cache setting along with everything else.

Of course you can manually disable output caching but that means remembering to do so. As our attach process is scripted in a batch file for repeatability and automation purposes, I recently added a step to disable the output cache programmatically. To do this, I wrote a little PowerShell script and call it from the batch file:

Param($Url)

write-host "User must be a site collection administrator or have full control within web application policy!"

[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint.Publishing”)

$cacheSettings = new-object Microsoft.SharePoint.Publishing.SiteCacheSettingsWriter($Url);
$cacheSettings.EnableCache = $false;

$cacheSettings.Update();

The script takes a single named parameter (Url) which specifies the site collection URL. As you can see, I'm working with the SiteCacheSettingsWriter class to set the EnableCache property to false before sending through an Update() command; of course the same thing can easily be accomplished in C#.

I call the script like so from my batch script:

powershell "& ./DisableOutputCache.ps1 -Url http://dev-moss"

I've parameterised this script since I restore content databases from multiple sites and didn't want the URL hard-coded.

As per my note, the account used to execute this script must be a site collection owner or have rights configured the web application policy or you'll encounter an access denied error.

 

How to supply named arguments to a PowerShell script

Calling a parameterised PowerShell script from a batch file or command window using name parameters is pretty easy, just flag each paramater name and supply a value like so:

powershell.exe "& ./MyScript.ps1 -MyParam1 'MyValue 1' –MyParam2 MyValue2"

Note I’ve wrapped MyValue1 in single quotes because of the space; the single quotes are stipped off the argument when it’s presented to the script.

MyScript.ps1 would use the parameters like so:

Param($MyParam1, $MyParam2)
write-host $MyParam1
write-host $MyParam2

The Param statement declares the formal parameters accepted by the script and must be the first executable line of code in the script. The parameters can now be used like any other variable, as demonstrated by the two write-host calls.

Disclaimer: I’m a PowerShell rookie! 

Building SharePoint 2010 Solutions with Visual Studio 2010

[Update: a video recording of this presentation is now available here]

I’m presenting the second of tonight’s Perth SharePoint User Group sessions:

Building SharePoint 2010 Solutions with Visual Studio 2010

Description:

SharePoint 2010 development is now a first class citizen with a new level of tool support in Visual Studio 2010. This session will demonstrate the basics steps to create a SharePoint project, configure a solution package, and create and work with project items like features and the new visual web part. Debugging, LINQ to SharePoint, and considerations for working with stand-alone applications will also be discussed.

Target Audience: Developers

Hope to see you there and don’t forget to register (for catering purposes) if you’re planning to attend: http://perthsharepointmay2010.eventbrite.com/

Thursday, 6 May 2010

How to merge Hyper-V snapshots to a parent .vhd

Jason Neurohr has a detailed, illustrated post on the subject of merging Hyper-V snapshots to a parent .vhd, leaving you with a single .vhd file you can run without having to worry about the snapshots. I found Jason’s post excellent and the steps described worked like a charm the first time I had to do this (while I didn’t persist, I found exporting a VM with snapshots from the Hyper-V R2 console just wasn’t working—the snapshots weren’t exported as expected).

The merge process is actually quite simple and I’m therefore reproducing just the relevant details from Jason’s post for my own future benefit:

  • Backup the parent .vhd and all snapshot disks (.avhd)
  • Change the extension for all snapshot differencing disks from .avhd to vhd
  • Within the Hyper-V MMC, select the Edit Disk… option
  • On the Locate Disk screen, locate the snapshot disk to be merged
  • Select the Merge option and then the To the parent virtual hard disk option
  • Finish the wizard and create a new virtual machine using the merged disk

If multiple snapshots exist, merge the most recent snapshot into the parent disk and work in reverse snapshot date order all the way back to the parent.