Monday, August 18, 2008

Enabling Item Scheduling through code

Many settings in MOSS are reachable through code. You could say all, because all settings you see in the GUI are based on actions performed by code. However, the translation is not always very transparant.

A library can have item scheduling enabled. This allows you to specify a window in time in which a page should be visible to the world. When you enable item scheduling through the GUI you first have to enable "Minor versions" and "Moderation" in the versioning settings. This is quite easy in code:

//pageLibrary is a SPList object. I assume you know how to get to it.
pageLibrary.EnableMinorVersions = true;
pageLibrary.EnableModeration = true;
pageLibrary.Update();

The next part is where you hit a brick wall. There is no "Manage Item Scheduling" property for list objects. What shows as a checkbox in the GUI hides a couple of things that happen when you turn Item Scheduling on. The most important is that EventReceivers are added to the list. Normally, when working with EventRecievers you're making your own. Here the objective is to add Microsoft's own EventReceivers through code:

pageLibrary.EventReceivers.Add(SPEventReceiverType.ItemAdded, "Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c", "Microsoft.SharePoint.Publishing.Internal.ScheduledItemEventReceiver");
pageLibrary.EventReceivers.Add(SPEventReceiverType.ItemUpdating, "Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c", "Microsoft.SharePoint.Publishing.Internal.ScheduledItemEventReceiver");

Thats it! Seems easy enough, don't you think? These 5 lines of code made up a days work though.

Friday, August 15, 2008

CAML filtering using a time

CAML is the query language used to get content from libraries and lists in a SharePoint environment. The keywords are reminiscent of SQL, but they have been formatted to be XML. This means they are very hard to read and write. This is why many use the great (and free) u2u CAML builder.

There are two pittfalls I dove into headfirst recently. When you create a custom control in c# you can specify a query to be executed on a SPList object. Easiest is to create your query in u2u and then copy-paste it into your code. However, you should remove the <query> and </query> tags from your query. If you don't, the result will be all items from the list. Your query will do nothing. This can be quite hard to spot if you expected most or all your items to be returned.

The second pittfall was in my query parameters. I wanted all items where a DateTime field was lower then "now". Now can be specified as
<value type="DateTime"><today /></value>
or you can get your code to output something like this:
<value type="DateTime">2008-08-08T16:34:07Z</value>

This will work. Kinda. It may take some time before you notice that the query processes the date just fine. However, the time part will be ignored. The second example effectively filters like it was:
<value type="DateTime">2008-08-08T00:00:00Z</value>

If you want the time value to be used you've got to say:
<value type="DateTime" includedatetime="'TRUE'"><Today /></value>


Note: I used both the <Today /> and the yyyy-mm-ddThh-mm-ssZ format in my examples. Both exhibit the same behaviour on dates.

Monday, August 11, 2008

Using more than 3 filters in a CQWP

The CQWP has some limitations. One of those is that you are restricted to using no more than 3 filterfields for filtering your content. In some cases that's just not enough. There are more ways than 1 to circumvent this. The most obvious is defining a custom query in CAML (msdn). This will work very well when your webpart is to be used in one particular place and doesn't have to be customized after it's added.

However, when you want to use it in a more dynamic manner this won't do. The problem that came across my desk last week was that I had to check three boolean fields in the contenttype, and a date. The solution was to create an extra site column of the type "calculated". The value of this field was based on the other three boolean fields. Only when all three of them where false, the aggregate field would be true. Now I can filter on this field and the date field. I even have 1 filter left.

Of course, this assumes that the fields you want to use in your filter can be aggregated in a standardized way. But if that's the case, this might just save you from having to write a custom web part.

Thursday, August 7, 2008

"local device name is already in use"

There are quite a number of things you could be doing wrong when you're facing this error message. A quick Google search will show a lot of them. One thing Google doesn't mention is that it can also be related to a duplicate GUID that is used by two custom site columns and referenced by a feature reciever.


In my defense, maybe I was just recycling GUIDs out of fear I might someday run out... Or something like that.

CQWP and custom column troubles

Last week I've been struggling with one of the most deceptive bugs I've ever encountered. I had created a custom site column, a date-time field. Multiple contenttypes (all pages) used this site column. Then I used a content by query webpart to show a list of pages on the site, filtered by my custom date field. The first time I tried this I got no results. I thought something had gone wrong in the declaration of my columns and types, so I made some minor modifications and tried again. It worked, I got results and I proudly published my work.

One day later, a co-worker complained that the results of the CQWP were incorrect. I checked, and he was right. Some results should not have been shown but were, and some that should have been shown were missing. More than a day I spent on this until I found this great posting by Ranjan Banerji (the fact that the link reads "CQWP nightmares" should be a hint):
http://techblog.ranjanbanerji.com/post/2007/10/30/Content-Query-Web-Part-(CQWP)2c-Cross-List-Query-Nightmare-Part-3.aspx

What happens is that the custom date field gets assigned some generic name in the SQL database. This name is then used in the query by the CQWP. However, there is no guarantee that this name is the same in every content type that uses the custom site column. So the query could be right for only one of your contenttypes. It could also work for most. Maybe even all. But you might not notice you're missing some results until someone else points it out to you...

Rating