tisdag 31 maj 2011

Audience targeting failing after upgrade to SharePoint 2010

This is not the final solution! In some cases it can be, but in my case it turned out to fail. More coming up...

If you're upgrading to SP2010 and used audience targeting in MOSS for lists simply by enabling it in the UI or by using code, you can stop reading now. But if you enabled it by adding the Target_x0020_Audiences field in a custom list schema, this can be of interest!

I'm upgrading a customer's intranet from MOSS to SP2010. A great deal of information is displayed using CQWP together with Target Audiences.

In a earlier project for the same customer I moved this intranet from one farm to another and then I began to realize how SharePoint actually handles audience targeting. Since audiences are handled by the SSP in MOSS, the definitions and IDs are stored in the SSP database (in a table called something like OrgleList), and since the audience set in a list item is stored with the ID, the connection breaks when moving the content database for the web application to another farm where there is another SSP dealing with the audiences. Even if the definitions are the same, they get a new ID and so everything fails. I've published some code to handle this at codeplex: Audience Migration .

But now I'm upgrading to SP2010 and the upgrade handles this almost perfectly (or at least as perfect as in can get) since I also upgrade the SSP database when setting up the User Profile Service. "almost" means ofcourse that there's been a horrible change in the Publishing features that enable the use of audience targeting:

Sometimes my Content Query Web Parts (CQWPs) in the upgraded intranet showed nothing when I specified that audience filtering should be applied, and it showed all items when I also included untargeted items even though they were targeted. Not the same behaviour as in the old MOSS intranet. So I tried to create new CQWPs to make my head a little bit clearer. And yes, I found out that if I set the query to point to a specific list, the correct behaviour sometimes fails, but if I set it to use the site collection or a web it worked fine... Strange... Ok, start ILSpy:

When the CQWP is building the query to execute, it loops through properties like CommonViewFields, DataMappingViewFields and SystemViewFields to aggregate the ViewFields-tag in the query. The actual query is registered in the ULS and I noticed that sometimes the query used the field id to specifiy a FieldRef in the ViewFields-tag and sometimes it uses the internal name. And yes, when the query scope is set to a specific list it uses internal names and otherwise it uses the field IDs. When looking in the code it should get the field name from the SPList.Fields collection, but the query looks for the field called Audience even though the field used for audience targeting in the list is named Target_x0020_Audiences. But I also noticed that before looking for the field in the list, the code tries to get the field name from a thread-safe cache...

But how come that the query looks for a field named Audience when it should be Target_x0020_Audiences?
In MOSS the Target Audiences field, defined in the PublishingResources feature, was named Target_x0020_Audiences so i looked in the file to make sure. And yes, they changed it!!! Now it's simply named Audience. The ID is fortunately the same.

Now we're getting somewhere. Some of the list schemas in the intranet are customized (unghosted), i.e. the schema xml is saved in the database. When mounting the MOSS content database, the upgrading process corrects the field names in the customized lists, but since the schema of an uncustomized list is outside of the database, the upgrade process can't correct this.

The effect is that if you have several CQWPs in a page and some points to specific lists and at least one of those lists is customized or using the new field name Audience, the behaviour of the web parts will depend on which is rendered first. If an upgraded customized list (or a new list with "correct" field name Audience) is rendered first, the field name Audience will be cached and used as view field in that query and every following query of other CQWPs in that page! If an old uncustomized list is rendered first, the others with correct field names will fail when it comes to audience filtering.

So, after a long time of looking around, the solution was simple: don't forget to change the Name and StaticName of the Target_x0020_Audiences field to Audience. I don't think many will ever notice this problem. When you manually enable audience targeting in a list, the list schema will be customized and saved in the database since the Audience field is added to the list. In my case, I enabled audience targeting in my lists by including the field in the schema.xml file.

Hope I can help someone out there!

fredag 6 maj 2011

User Profile Service troubles

Most of you reading this probably have a lot of broken harware after smashing screens, keyboards and god knows what. Setting up the User Profile stuff in SharePoint 2010 is hell. But after tips and tricks you actually might succeed! Look at this to get details of how it really works http://www.harbar.net/articles/sp2010ups.aspx
Followed it by the numbers but still, things got stuck when starting the User Profile Synchronization Service. ULS said a lot of strange stuff, like a service was too busy or that the proxy for User Profile Service application couldn't be found.
A few broken keyboards later I found a strange thing in the event viewer for security. An audit failure for the user profile service account. A classic NULL SID, and the DisableLoobackCheck trick in the registry did not solve it. A closer look at the failure reason: "The user has not been granted the requested logon type at this machine.". The logon type here is 4... googling... Aha, found it! A lot of posts out there says that the farm account must be able to logon locally and that it is set via the local security policy. But logon type 4 does NOT mean "logon locally". It means "log on as a batch job"!
Looking in the local security policy shows that the Performance Log Users group is allowed to logon as a batch job. A little reminder in my head rang a bell. I recall seeing that somewhere out there (wouldn't surprise me if it is the microsoft documentation :) ) Another reminder told me something about the default schema set in the databases (Profile DB, Social DB, Sync DB). Some people out there say something about a bug and that the default schema should be set to dbo.
Trying all this... I can now feel safe to buy a nice keyboard!

That stuff about the Performance Log Users group seems to be important for other service applications aswell. Have no idea why these things aren't set correctly in the current farm though.