Friday, 20 April 2007

Feature to create lookup fields on Codeplex

In a recent post I posted some sample code for a feature receiver which would create lookup fields (site columns which get their data from lists). A couple of people left comments asking for full set of files.

I've put these on Codeplex at http://www.codeplex.com/SP2007LookupFields.

Some notes:-

  • I've enhanced the solution to be more generic and deal with creating multiple lookup fields in one feature. Now the name of the list can be included in the CAML and the feature receiver will parse this, find the list and fix the reference via the list GUID using the API. Note currently the list must be in the root web, though it would be trivial to extend this.
  • All the hardcoded values have been removed, e.g. the path to the file containing the CAML definition is now passed as a feature property.
  • I mentioned in the earlier post that you could have a dependent feature so that the assembly containing the feature receiver also gets deployed automatically on feature activation. This doesn't quite make sense since assemblies can only be deployed using SharePoint solutions not features. Hence I've wrapped the feature in a SharePoint solution which deploys the assembly and feature. When the feature is activated the assembly is already in the GAC and the feature receiver runs happily. Note it would also be fairly simple to enhance the solution such that the assembly gets deployed to a site bin with appropriate CAS policy for highly-controlled environments.
  • I've also included an STSADM script to take care of deploying the solution - you need to edit the URL in this file to point to your SharePoint site.

Hope this is useful, let me know if you have feedback..

21 comments:

Anonymous said...

Hi Chris.
Great job. This actually works. But I have a nother question for you. How could I implement a feature receiver wich starts when i create a new instance of my Site Definition? Visual Studio provides something like a Site Provisioning Handler-Class SiteProvisioning.cs - but I figuered out it actually does not work. Do you know anything about that?
THX in advance

Chris O'Brien said...

Hi,

Yes, so assuming you are creating a VS project with one of the templates which come with VSeWSS, there will be a file called SiteProvisioning.cs in your solution.

I've not had any particular problems with this - I can write code which modifies the site with no issues. I might write a post with more details though, as it's an area with a lot of possibilities.

What problems are you having?

Chris.

Matt Taylor said...

Hey Chris

Meant to leave a comment earlier to thank you for this project. I downloaded it and had it up and running in hours with the minimum of tweaks. What I really like about it is the generic approach you've taken that means the specifics of the particular site columns being targetted are all held in config files - spot on! It works like a charm and "feels" like the right way to manage this vital configuration step.

Nice one.

Chris O'Brien said...

Thanks for the feedback Matt, much appreciated!

Chris.

Bruce Sandeman said...

Hi Chris,
Very useful code, but for some reason I can only make it work for the first column defined within the fields xml file.

I can happily change the first field's name and it is created differently, but with multiple fields in there it does not seem to work.
Can you give me any pointers?
thanks
Bruce

Bruce Sandeman said...

No worries,
I found the problem, needs a line of code replaced as at http://www.codeplex.com/SP2007LookupFields/WorkItem/View.aspx?WorkItemId=5485

Chris O'Brien said...

Hi Bruce,

Sterling work, many thanks for contributing.

I've updated the source code and checked in a new release with the update.

Much appreciated..

Chris.

Jessica Hardy said...

Chris,

I've used this blog entry to get just what I needed to create site columns based on lookup lists. Thanks for the help! Now, the next bridge I have to cross is integrating these into a list definition. I can get the lookup list site columns into a content type, because that uses FieldRef but when creating a list definition, you basically have to redefine each field even if they are in the content type (which, BTW, I find annoying :) ). Any ideas?

Thanks,
Jessica

Chris O'Brien said...

Hi Jessica,

Hmm, I've not looked at adding columns to a list definition in this way I'm afraid. But I would have thought you'd need to add the FieldRef entries to the list's schema.xml file. I note there's 'fields' element in here, so that might be the place.

HTH,

Chris.

John Haigh said...

Hi Chris,

I think this article is great.

I have a Site Definition Solution that I created through VSeWSS that has two content types(i.e Contact and Address),
and then two lists "ContactList" and "AddressList" created from these content types.
The second content type "Address" has a lookup field "RelatedContact" that refers to the ContactList.

Is it possible to add the feature receiver solution you wrote here that is on codeplex into my Site Definition Solution specifically into
SiteProvisioning.cs (done through SiteProvisioning.cs as mentioned above) so that when the site is provisioned
the lookup field has the proper GUID that references the GUID for the ContactList that WSS created when the Site was provisioned?

Thanks,

John Haigh

Chris O'Brien said...

Hi John,

Absolutely - I can't think of any reason why this wouldn't work. There's probably a couple of approaches:

- chop out the code I use and add it to your SiteProvisioning.cs (as you suggest)
- use Feature stapling to link the code to the site definition

Either should do the job. Let me know how you get on.

Cheers,

Chris.

John Haigh said...

This is great news. I will let you know how it goes and when successful I'll send my code over so that hopefully it can be posted so others can benefit.

Thanks,

John

John Haigh said...

Hi Chris,

I chopped out the code you wrote and added into SiteProvisioning.cs, updated a lookup field in the XML file that will have the GUID replaced, deployed through VSeWSS and then created a site and I get this error when creating a site "Value does not fall within the expected range." I did a google search and found that the problem may be within the XML and then doubled checked the XML.

The other concern after reading your articles is that my sense is that I can't do this through VSeWSS and I need create a stand alone Solution with a Feature/Feature Receiver as you did in this article (codeplex etc.) and move all Content Types, Lists, etc. into a VS solution as you outlined in your articles "Automatically setting custom permissions on new sites" or "Creating, deploying and updating custom site definitions"

Is this the case?

Thanks,

John

John Haigh said...

Hi Chris,

I was able to find a workaround in siteprovision.cs.

Here is the code that worked to create the lookup field:

Inside OnActivated I added the following code:

SPWeb web;
SPSite site;

if (properties.Feature.Parent is SPWeb)
{
web = properties.Feature.Parent as SPWeb;
site = web.Site;
}
else
{
site = properties.Feature.Parent as SPSite;
web = site.RootWeb;
}

web.AllowUnsafeUpdates = true;

SPList addressList = web.Lists["AddressCTList"];
SPList provinceList = web.Lists["ProvinceCTList"];

provinceList.Fields.AddLookup("RelAddress", addressList.ID, true);
provinceList.Update();

Thanks,

John

Chris O'Brien said...

Hi John,

Good to hear you got sorted. Thanks for posting the code!

Cheers,

Chris.

P.S. In response to your earlier comment, if you were going down the Feature route you wouldn't be able to use VSeWSS for this. This is because VSeWSS doesn't support feature receivers and regenerates the files on every build.

However, it is possible with VSeWSS 1.1 CTP, since this allows you the developer to add to the feature files by hand. Note though this is still just at 'preview release' stage, so there could be the odd bug.

HTH,

Chris.

Adrian Hara said...

Hi, great explanation. However, I've a problem. When the code tries to delete the column, i get an error, saying that it's still associated with a list or content type.

Looking at the site, there are no other lists there, so that's not the problem. However, in the FeatureActivated event, which actually calls the deletion code, it seems that the content type containing this lookup site column is already deployed (i can find it using the object model while in a breakpoint in the debugger) and I guess that's why the deletion won't work. Do you have any idea why I get this behaviour?

Thanks,
Adrian

Chris O'Brien said...

Hi Adrian,

I think that's the expected behaviour.

In addition to removing the field from any lists, you would need also need to remove it from any content types (or delete the content type) before the field can be deleted.

If you also delete from the content type you should be OK.

HTH,

Chris.

ben said...

Chris -

my question to look up columns is this. Why can't the Assigned To Column in a task list be used as a look up Column.

I have a workflow associated with a document library and I would like to have who the workflow is assigned to as a column in my document library.

Would this feature be able to do something like that? Is that possible?

Brian Bilbro said...

Hi Chris,
Thanks for the blog posts. These are great. My team is just now getting into SharePoint and these are great resources.

I just wanted to pass along some info. I'm doing the exact same thing as John Haigh above. However, I had a slight variation. I don't add the lookup column dynamically. I add as normal through the CAML definition files.

What I do is just update the existing field with the correct list reference. I kinda prefer that method since everything else can be defined as normal.

here's my change:

SPList applicationsList = web.Lists["MercerApplicationsListDefinition instance"];

SPList projectsList = web.Lists["MercerProjectsListDefinition instance"];

projectsList.Fields["Applications"].SchemaXml = projectsList.Fields["Applications"].SchemaXml.Replace("fd648b7f-9e58-469e-b1cf-5d365e26764c", applicationsList.ID.ToString());

projectsList.Update();

Chris O'Brien said...

@ben,

Sorry, don't have an answer on this one. If this column is of Person/Group type and this column type cannot be used as a lookup, it could just be that it isn't supported.

Sorry I can't be more help,

Chris.

Chris O'Brien said...

@Brian Bilbro,

That's a great tip - logically thinking about what's going on there, I shouldn't be surprised that works. I guess either way some code needs to be run so it could be a matter of preference as to the where/what.

In general though, the fact that the SchemaXml property is writable and can be used for schema updates opens some great possibilities :-)

Cheers,

Chris.