views:

344

answers:

1

Hi all:

I created a custom column in a custom content type in a Sharepoint Web manually (e.g. /MySite/MyWeb). I now want to programmatically copy this content type across to another web (e.g. /MySite/MyWeb2). However, upon looping through the custom content type in code, I could only find 2 fields: Content Type and Title (expected: Title and custom column). The custom column was missing. I'm very sure that the content type and field are added at the web level.

The custom content type is inherited from Item.

When I loop through the web's fields, I can see the custom column, and that was copied to the new web. It is only within the content type that the custom column is not showing up.

Any ideas why this is happening?

Thanks.

+1  A: 

It took me over a quarter dozen methods to finally be able to successfully and programmatically copy a content type from one web to another. Now, of the methods that failed, only one of them would replicate your exact problem and not completely combust, so I'll assume that is the method you attempted and analyze why it fails. Then I'll show you a method that works properly.

You cannot programmatically add fields to a content type instance which is only scoped on the web level. Attempting to do so provides the following clear error:

This functionality is unavailable for field collections not associated with a list.

So if your method was to basically create a content type in the new web that inherits from Item, and then just add your custom column, it would fail to add the custom column. I'm assuming that is how you tried to copy the content type, as every other approach I tried either failed, or worked and retained the field.

To make it work, create a new content type that is scoped to Web2's web level, but inherits from the original content type from Web1. So, using the ALICE content type I tested it with, you would have something like this.

SPContentType ct2 = new SPContentType(Web1.ContentTypes["ALICE"], Web2.ContentTypes, "ALICE");
Web2.ContentTypes.Add(ct2);
Web2.Update();

This created the ALICE content type on the second web, which has the custom column that the original ALICE content type has. Now, since the original ALICE content type isn't on Web2, then this ALICE doesn't actually inherit from that ALICE. It'll instead inherit from Item, which you can confirm both in the UI and programmatically. So it becomes a successful copy.

EDIT

Here is the exact code I used, minus a couple lines for outputting debug and not stating my actual Web2 address. It was run from a workflow on Web1. I guess instantiating a content type separately is the major difference. Let me know if this continues to produce that same error.

SPWeb website = null; //Web1, which has ALICE already.
SPWeb web = null; //Web2, which does not yet have ALICE.
SPSite site = null;
try {
    website = workflowProperties.Web;
    site = new SPSite('webaddress of web2');
    web = site.OpenWeb();
    SPContentType ct = website.ContentTypes["ALICE"];
    SPContentType act = new SPContentType(ct, web.ContentTypes, "ALICE");
    act.Group = "Custom Content Types";
    web.ContentTypes.Add(act);
    web.Update();
}
finally { if (web != null) { web.Dispose(); } if (website != null) { website.Dispose(); } if (site != null) { site.Dispose(); } }
ccomet
What will happen to the custom column I've created in web1? Will it get created in the proper web level in web2 as well? What I've done was copy any custom fields from web1 to web2 first, then create a new content type in web2 that inherits Item, then loop through the fields of web1's content type and copy any extra fields to the new content type. At first it didnt work when I just add the fields, but using contentType.FieldLinks.Add(new SPFieldLink(field)) it seemed to be working. I'll give your way a try as well. Thanks.
BeraCim
Ok tried out your method... got "A content type can not be added outside of its scope" exception.
BeraCim
@BeraCim Okay, your original method is what I expected, and is exactly what would cause the error I cited. I'm a bit surprised that you got that error, since I've received that error during some of the other tests, but not the version I wrote. I will do some additional testing. However, I'm glad that you did find a method that worked.
ccomet
@BeraCim Alright, I've edited my answer to reflect exactly the code I used which worked. This was done inside of a workflow, so it had maximum permission. Setting web.AllowUnsafeUpdates was unnecessary. Let me know if this is more successful.
ccomet
For those who stumble on this answer later... while this code will technically work, after some more research it is more recommended to use FieldLinks, as explained in [BeraCim's later question](http://stackoverflow.com/questions/2756759/difference-between-fieldlinks-and-field-in-sharepoint)
ccomet
Got the following exception again: A content type can not be added outside of its scope" at this line: SPContentType act = new SPContentType(ct, web.ContentTypes, "ALICE");, which was the same exception I got before. I'm not doing this in workflow. This sounds like the content type can not be found within the content type inheritance hierarchy.
BeraCim
@BeraCim Alright... now I'm totally perplexed. At the very least, you do have the proper direction to use FieldLinks. I'll be diverting my attention to figuring out why my code is working for me (as opposed to why it isn't working for you).
ccomet