Thursday, 18 November 2010

DateTime format safe for file system

Couldn't find anything similar when I searched so thought I'd throw this out there for use in file names in a Windows file system:

DateTime.Now.ToString ("yyyyMMddHHmmss")

Will output "201001312359"

Add a ".txt" or ".dat" if you're hard-core and you're all set.

If you found this post helpful, please support my advertisers.

Wednesday, 17 November 2010

How to determine if a SPPublishingPage is published

You can programmatically determine if the SPPublishingPage you're dealing with is in a published state by retrieving the SPFileLevel of the page's corresponding SPFile:

if (page.ListItem.File.Level == SPFileLevel.Published) …

You can also access this through the PublishingPage.ListItem.Level property.

The same SPFileLevel enumeration will also indicate if the page is in draft mode (checked in but not published) or checked out but in my experience this property will return SPFileLevel.Published or SPFileLevel.Draft more often than not.

To determine whether the page is checked out, use the SPFile.CheckOutStatus property in WSS 3.0 or the  SPFile.CheckOutType property in SP2010. Any value other than None means the page is checked out.

If you found this post helpful, please support my advertisers.

SPSite.AllWebs vs SPWeb.Webs

There's a subtle distinction between SPSite.AllWebs and SPWeb.Webs to be aware of—and yes, despite this being the SharePoint API we're talking about, it's more than just a naming oddity!

SPSite.AllWebs returns the immediate child webs of the site collection and all of their child webs (in other words, all webs recursively within the site collection); SPWeb.Webs has a much narrower scope and only returns the first level child webs immediately below the SPWeb object.

So what does this mean and how does it benefit you? If you're writing code that recursively walks the site hierarchy using SPWeb.Webs, you may be able to avoid the overhead by simply using SPSite.AllWebs. Recursion is fun and all but it adds complexity where it may not be warranted.

If you found this post helpful, please support my advertisers.

Thursday, 28 October 2010

Free Antivirus for Windows Server 2008

I've been a long time home user of Avast! antivirus. It's a great product that doesn't bog down my machine. Unfortunately the free home edition won't install on Windows Server 2008 R2 and I run a single-boot W2k8 environment because of my Hyper-V love affair.

Don't get me wrong, I normally despise AV and security in any form… the guys I work with are probably fed up with me always shouting "security != productivity" when the bloody proxy policy has once again broken something or prevented me doing my job. But, every now and then, I feel incline to download evil things and God forbid those things include a virus of some kind—a quick scan would then come in handy…

ClamWin to the rescue! Don't know about the name but it doesn't include a real-time scanner which meets my requirements. So far so good… evil things downloaded and appear to be virus free!

Wednesday, 27 October 2010

Empty ImageUrl results in empty src and duplicate request

Not exactly a new issue but an empty ASP.NET ImageUrl tripped us up today. In this case there were no visible symptoms but two requests were coming through when Fiddlering the page view: the first initiated by the user and the second initiated by the page itself towards the end of the trace. In development with a debugger attached, this was manifest with Page_Load, CreateChildControls, etc being called twice for no obvious reason.

Because I initially thought I introduced the problem with the control I was working on, I first attempted to convince ASP.NET CreateChildControls was complete; I did so by clearing the Controls collection before unleashing my own code and setting the ChildControlsCreated property true once done. Neither of these tricks had any affect and the Fiddler trace had me convinced something beyond the ASP.NET pipeline and IIS had to be at fault.

That turned out to be the case but ASP.NET was still to blame ;-)

Specifically, we were adding a server-side ASP.NET image control but not initialising its ImageUrl property; the image src was later being set by jQuery at runtime on the client side. As mentioned, there were no visible symptoms of the double request: the image src was set as expected by jQuery and everything appeared okay on the surface. The IE dev toolbar also showed image src as being correctly set and there were no 404s in the mix.

It wasn't until we looked at the raw HTML that our ever-faithful admin extraordinaire noticed the empty src="" attribute. Removing the attribute removed the problem so I can only conclude IE is helpful enough to attempt to interpret a request for an empty image as a request for the parent directory of the current page while parsing the HTML before any Javascript runs. Thanks again, IE!

Notably this problem wasn't reproducible in Firefox or Chrome.

To fix the problem, we first set a default value for the ImageUrl property but that left me feeling dirty since it was still resulting in an unnecessary request. When I realised the server-side Image tag wasn't actually being used for anything server-side anyway, I replaced it with a boring old HTML img tag with no src. Microsoft has other, equally lame workarounds for this if you're interested; note they also don't plan to fix this bug.

If you found this post helpful, please support my advertisers.

Friday, 15 October 2010

Must-know SharePoint debugging tips

This post is a follow-on to my Must-Know Visual Studio debugging tips article; I'm separating out my SharePoint debugging tips focused on list, feature, and solution deployment that don't relate to Visual Studio.

Here you go:

  • Try activating your feature without the –force attribute; you'll likely need to deactivate the feature first
  • Try uninstalling and reinstalling the feature
  • If something stuck and rebooting seems to clear some kind of cache deep inside SharePoint, stop IIS and restart the various SharePoint Windows services

@echo off
@echo Stopping services...
iisreset /stop /noforce
net stop "Windows SharePoint Services Timer"
net stop "Windows SharePoint Services Administration"
net stop "Office SharePoint Server Search"
net stop "Windows SharePoint Services Search"
net stop "Windows SharePoint Services Tracing"
@echo Starting services...
net start "Windows SharePoint Services Tracing"
net start "Windows SharePoint Services Search"
net start "Office SharePoint Server Search"
net start "Windows SharePoint Services Administration"
net start "Windows SharePoint Services Timer"
iisreset /start
@pause

  • Create a new list from your list definition (or at least, a new list item)
  • Rebuild your solution and redeploy
If you found this post helpful, please support my advertisers.

Thursday, 14 October 2010

The Mysterious SourceID Attribute

I see a lot field definitions around that unnecessarily include a SourceID attribute:

<Field
SourceID="http://schemas.microsoft.com/sharepoint/v3"

More often than not, the value of this attribute is set as above. Microsoft's own documentation samples tend to include this attribute.

So what does it do? Not a lot as far as I can tell; I don't include it with my own field definitions because inspecting the field after deployment with SharePoint Manager proves SharePoint sets it automatically with the GUID of the list that created the field. The Field Element documentation also indicates it is optional.

If you're intent on using it, the documentation describes this attribute as "containing the namespace that defines the field, such as http://schemas.microsoft.com/sharepoint/v3" so rather than use the default namespace it may be advisable to use your own (I haven't tried this myself).

If you found this post helpful, please support my advertisers.

Wednesday, 13 October 2010

SharePoint and Chrome - Better Together

I've been using Google's Chrome browser since its first release in 2008; I've loved nearly every second of the experience. Who would've thought there was room left to innovate in the browser space? Chrome's omnibar and rapid-fire JavaScript rendering, among other tweaks, are simply light years ahead of the competition.

While I normally rely on IE for my MOSS/SharePoint editing interactions, as of late I'm making the switch to Chrome in that space as well. What I've found to date has blown my mind.

Yes the MOSS 2007 UI degrades somewhat but it's still very useable. More importantly, Chrome drastically reduces the time it takes to accomplish basic tasks like modifying page content or viewing list data. I'm not saying these are normally slow in SharePoint but they can be in the www.westernaustralia.com environment (it's an ageing site with a lot of content and a lot of customisations); some pages in particular nearly grind to a halt in IE8 with the corresponding process consuming upwards of 1GB of memory the more I interact with the page.

Chrome "fixes" many of these slowdowns I'd previously attributed to the SharePoint environment and gives me all the Chrome goodness I've come to love over the last two years. It almost makes the SharePoint editing experience pleasurable!

If you found this post helpful, please support my advertisers.

Monday, 13 September 2010

How to pass JSON arrays and other data types to an ASMX web service

Ah interoperability… great fun, great fun.

So jQuery is your new best friend and, along with JSON, there's nuthin' you can't do. The server side stuff is still there in the background and you've got some old school ASP.NET (.asmx) web services hanging around but DOM elements are otherwise flying all over the place, postbacks are just so passé, and even the marketing girls are mildly impressed at your skillz. You're branching out, shifting code and complexity from the server to the browser, and it's time to do some heavier data shunting. Here a few things to know about passing JSON data to an ASMX web service that may help you on your way…

JSON.stringify

Know it, use it, love it. It's part of the JSON2 library and you need it if you don't have it already. Use it to prepare (aka properly encode) your JSON data before sending it off to the big mean ol' web server:

data: {"days": JSON.stringify(["Mon", "Tues"])}

That will encode as &days=["Monday","Tuesday"]

Yeah, I know, it's another file to download but the guy who wrote JSON also wrote this and it can be merged and minified. I've tried writing my own mini-version as a function and while this works for simple strings, save yourself some time when it comes to arrays and the like and just use this sucker.

Arrays

Arrays seem trickier than they should… maybe I'm just a dumb guy—probably. Anyway, you can pass a JSON array to an .asmx web service without much work at all.

The client-side call listed above is everything you need to do from that end. On the server side, create yourself a new web service method with a List<string> parameter:

[WebMethod]
[ScriptMethod (UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public string ConsumeArray (List<string> days)
{…}

That's all there is to it. If you're not passing in strings, declare the List<> parameter with a type of object or something else. You can use .NET arrays in the web method signature as well if you really want (need) to.

Integers

When in doubt, stringify:

data: { "i": JSON.stringify(2) }

An int parameter on the web service end will handle this graciously.

Booleans

The good ol' boolean—a simple concept computer science has managed to bastardise like no other…

When in doubt, stringify:

data: { "b": jsonpEncode("true") }

Like the int parameter, a bool in your web method signature will take care of this.

A brief note: JSON, or rather jQuery's parseJSON function, is a particular beast and doesn't seem to know about anything other than the lower case true and false strings. If, for any reason, you ToString a bool in your .NET web service and try to pass it back, parseJSON will fail. If you forget to brush your teeth in the morning, parseJSON will fail.

Dates

Sorry, on my todo list ;-)

Tools

When working through this stuff, it pays to have Fiddler open to inspect the requests you're sending through and any error messages you're getting back. I find Fiddler sometimes breaks this stuff so try turning off the capture if you're getting weird errors; optionally, revert to Firebug (Firefox only, of course).

Fully decoding the data you sniff from a JSONP request passed along in the query string will require some additional tooling; in short, you'll want to decode the value using a free online tool like Opinionated Geek's URL decoder.

If you found this post helpful, please support my advertisers.

Wednesday, 8 September 2010

Exposing the Global Assembly Cache

If you're familiar with the Global Assembly Cache (GAC) you're probably aware there's a special file system viewer thingy (technical term) sitting over top of the GAC contents at c:\windows\assembly; this is a nice convenience when it comes to registering assemblies in the GAC—simply drag and drop, avoiding a trip to the command line and gacutil –i

More often than not, this is all good. When you need to dive into the real GAC, to extract an assembly, drop in debugging symbols, or whatever, you'll quickly realise the viewer is somewhat limiting.

To get past the GAC's outer facade, you've got a few options:

  • From a command line, browse to c:\windows\assembly\gac_msil
  • Map a network drive to \\machine-name\c$\windows\assembly\gac_msil
  • Create a virtual drive: subst z: c:\windows\assembly\gac_msil where 'z:' is any unmapped drive letter
  • Start –> Run c:\windows\assembly\gac_msil
  • Turn off the viewer altogether to browse the GAC directory structure normally within Windows Explorer: create a new DWORD named DisableCacheViewer with a value of 1 below the HKLM\Software\Microsoft\Fusion key

Five ways to do the same thing? Well this is Windows after all—and there are probably more!!! ;)

If you drop the gac_msil bit you'll find there are other directories that make up the GAC proper to explorer but most of what you'll be after resides below gac_msil. Each assembly is represented in by name as a folder with different versions represented as sub folders named as the version number with a GUID appended; the assembly proper will live in one of these folders.

If you found this post helpful, please support my advertisers.