MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Thursday, May 1, 2008

Propagate Content Type Changes

This is something that I put together a while ago but I'm only just now getting to the point where I can document it.  I was looking for a solution to a common problem of propagating changes to content types deployed via a Feature and I came across a post by Søren Nielsen.  Søren created a custom stsadm command which handles pushing down the content type changes.  I didn't want to re-invent the wheel but I needed the ability to call the code in different ways and I wanted to try my hand at using the SPContentTypeUsages class so I decided to use what he created and just refactor it to meet my goals.  Søren does a great job at explaining the problem and what the code is doing so I won't reiterate it here.  My modified version of the code can be seen below:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Collections.Specialized;
   4: using System.Diagnostics;
   5: using System.Text;
   6: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
   7: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
   8: using Microsoft.SharePoint;
   9:  
  10: namespace Lapointe.SharePoint.STSADM.Commands.ContentTypes
  11: {
  12:     /// <summary>
  13:     /// This code was derived from Søren Nielsen's code that he provides on his blog: 
  14:     /// http://soerennielsen.wordpress.com/2007/09/11/propagate-site-content-types-to-list-content-types/
  15:     /// </summary>
  16:     public class PropagateContentType : SPOperation
  17:     {
  18:         /// <summary>
  19:         /// Initializes a new instance of the <see cref="PropagateContentType"/> class.
  20:         /// </summary>
  21:         public PropagateContentType()
  22:         {
  23:             SPParamCollection parameters = new SPParamCollection();
  24:             parameters.Add(new SPParam("url", "url", true, null, new SPNonEmptyValidator()));
  25:             parameters.Add(new SPParam("contenttype", "ct", true, null, new SPNonEmptyValidator()));
  26:             parameters.Add(new SPParam("verbose", "v"));
  27:             parameters.Add(new SPParam("updatefields", "uf"));
  28:             parameters.Add(new SPParam("removefields", "rf"));
  29:  
  30:             StringBuilder sb = new StringBuilder();
  31:             sb.Append("\r\n\r\nPropagates a site scoped content type to list scoped instances of that content type.\r\n\r\nParameters:");
  32:             sb.Append("\r\n\t-url <site collection url>");
  33:             sb.Append("\r\n\t-contenttype <content type name>");
  34:             sb.Append("\r\n\t[-verbose]");
  35:             sb.Append("\r\n\t[-updatefields]");
  36:             sb.Append("\r\n\t[-removefields]");
  37:  
  38:             Init(parameters, sb.ToString());
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Gets the help message.
  43:         /// </summary>
  44:         /// <param name="command">The command.</param>
  45:         /// <returns></returns>
  46:         public override string GetHelpMessage(string command)
  47:         {
  48:             return HelpMessage;
  49:         }
  50:  
  51:         /// <summary>
  52:         /// Runs the specified command.
  53:         /// </summary>
  54:         /// <param name="command">The command.</param>
  55:         /// <param name="keyValues">The key values.</param>
  56:         /// <param name="output">The output.</param>
  57:         /// <returns></returns>
  58:         public override int Execute(string command, StringDictionary keyValues, out string output)
  59:         {
  60:             output = string.Empty;
  61:             
  62:             using (SPSite site = new SPSite(Params["url"].Value.TrimEnd('/')))
  63:             {
  64:                 Process(site, Params["contenttype"].Value, Params["verbose"].UserTypedIn, Params["updatefields"].UserTypedIn,
  65:                     Params["removefields"].UserTypedIn);
  66:             }
  67:             return OUTPUT_SUCCESS;
  68:         }
  69:  
  70:         /// <summary>
  71:         /// Processes the content type.
  72:         /// </summary>
  73:         /// <param name="site">The site.</param>
  74:         /// <param name="contentTypeName">Name of the content type.</param>
  75:         /// <param name="verbose">if set to <c>true</c> [verbose].</param>
  76:         /// <param name="updateFields">if set to <c>true</c> [update fields].</param>
  77:         /// <param name="removeFields">if set to <c>true</c> [remove fields].</param>
  78:         public void Process(SPSite site, string contentTypeName, bool verbose, bool updateFields, bool removeFields)
  79:         {
  80:             Verbose = verbose;
  81:             try
  82:             {
  83:                 Log("Pushing content type changes to lists for '" + contentTypeName + "'");
  84:                 // get the site collection specified
  85:                 using (SPWeb rootWeb = site.RootWeb)
  86:                 {
  87:                     //Get the source site content type
  88:                     SPContentType sourceCT = rootWeb.AvailableContentTypes[contentTypeName];
  89:                     if (sourceCT == null)
  90:                     {
  91:                         throw new ArgumentException("Unable to find Content Type named \"" + contentTypeName + "\"");
  92:                     }
  93:  
  94:                     IList<SPContentTypeUsage> ctUsageList = SPContentTypeUsage.GetUsages(sourceCT);
  95:                     foreach (SPContentTypeUsage ctu in ctUsageList)
  96:                     {
  97:                         if (!ctu.IsUrlToList)
  98:                             continue;
  99:  
 100:                         using (SPWeb web = site.OpenWeb(ctu.Url))
 101:                         {
 102:  
 103:                             SPList list = web.GetList(ctu.Url);
 104:                             SPContentType listCT = list.ContentTypes[ctu.Id];
 105:                             ProcessContentType(list, sourceCT, listCT, updateFields, removeFields);
 106:                         }
 107:                     }
 108:                 }
 109:                 return;
 110:             }
 111:             catch (Exception ex)
 112:             {
 113:                 Log("Unhandled error occured: " + ex.Message, EventLogEntryType.Error);
 114:                 throw;
 115:             }
 116:             finally
 117:             {
 118:                 Log("Finished pushing content type changes to lists for '" + contentTypeName + "'");
 119:             }
 120:         }
 121:  
 122:         /// <summary>
 123:         /// Processes the content type.
 124:         /// </summary>
 125:         /// <param name="list">The list.</param>
 126:         /// <param name="sourceCT">The source CT.</param>
 127:         /// <param name="listCT">The list CT.</param>
 128:         /// <param name="updateFields">if set to <c>true</c> [update fields].</param>
 129:         /// <param name="removeFields">if set to <c>true</c> [remove fields].</param>
 130:         private static void ProcessContentType(SPList list, SPContentType sourceCT, SPContentType listCT, bool updateFields, bool removeFields)
 131:         {
 132:             if (listCT == null)
 133:                 return;
 134:  
 135:             Log("Processing content type on list:" + list);
 136:  
 137:             if (updateFields)
 138:             {
 139:                 UpdateListFields(list, listCT, sourceCT);
 140:             }
 141:  
 142:             //Find/add the fields to add
 143:             foreach (SPFieldLink sourceFieldLink in sourceCT.FieldLinks)
 144:             {
 145:                 if (!FieldExist(sourceCT, sourceFieldLink))
 146:                 {
 147:                     Log(
 148:                       "Failed to add field "
 149:                       + sourceFieldLink.DisplayName + " on list "
 150:                       + list.ParentWebUrl + "/" + list.Title
 151:                       + " field does not exist (in .Fields[]) on "
 152:                       + "source content type", EventLogEntryType.Warning);
 153:                 }
 154:                 else
 155:                 {
 156:                     if (!FieldExist(listCT, sourceFieldLink))
 157:                     {
 158:                         //Perform double update, just to be safe
 159:                         // (but slow)
 160:                         Log("Adding field \""
 161:                            + sourceFieldLink.DisplayName
 162:                            + "\" to contenttype on "
 163:                            + list.ParentWebUrl + "/" + list.Title,
 164:                              EventLogEntryType.Information);
 165:                         if (listCT.FieldLinks[sourceFieldLink.Id] != null)
 166:                         {
 167:                             listCT.FieldLinks.Delete(sourceFieldLink.Id);
 168:                             listCT.Update();
 169:                         }
 170:                         listCT.FieldLinks.Add(new SPFieldLink(sourceCT.Fields[sourceFieldLink.Id]));
 171:                         listCT.Update();
 172:                     }
 173:                 }
 174:             }
 175:  
 176:             if (removeFields)
 177:             {
 178:                 //Find the fields to delete
 179:                 //WARNING: this part of the code has not been
 180:                 // adequately tested (though what could go wrong?  … :)
 181:  
 182:                 //Copy collection to avoid modifying enumeration as we go through it
 183:                 List<SPFieldLink> listFieldLinks = new List<SPFieldLink>();
 184:                 foreach (SPFieldLink listFieldLink in listCT.FieldLinks)
 185:                 {
 186:                     listFieldLinks.Add(listFieldLink);
 187:                 }
 188:  
 189:                 foreach (SPFieldLink listFieldLink in listFieldLinks)
 190:                 {
 191:                     if (!FieldExist(sourceCT, listFieldLink))
 192:                     {
 193:                         Log("Removing field \""
 194:                            + listFieldLink.DisplayName
 195:                            + "\" from contenttype on :"
 196:                            + list.ParentWebUrl + "/"
 197:                            + list.Title, EventLogEntryType.Information);
 198:                         listCT.FieldLinks.Delete(listFieldLink.Id);
 199:                         listCT.Update();
 200:                     }
 201:                 }
 202:             }
 203:         }
 204:  
 205:         /// <summary>
 206:         /// Updates the fields of the list content type (listCT) with the
 207:         /// fields found on the source content type (courceCT).
 208:         /// </summary>
 209:         /// <param name="list">The list.</param>
 210:         /// <param name="listCT">The list CT.</param>
 211:         /// <param name="sourceCT">The source CT.</param>
 212:         private static void UpdateListFields(SPList list, SPContentType listCT, SPContentType sourceCT)
 213:         {
 214:             Log("Starting to update fields ", EventLogEntryType.Information);
 215:             foreach (SPFieldLink sourceFieldLink in sourceCT.FieldLinks)
 216:             {
 217:                 //has the field changed? If not, continue.
 218:                 if (listCT.FieldLinks[sourceFieldLink.Id] != null
 219:                      && listCT.FieldLinks[sourceFieldLink.Id].SchemaXml
 220:                         == sourceFieldLink.SchemaXml)
 221:                 {
 222:                     Log("Doing nothing to field \"" + sourceFieldLink.Name
 223:                         + "\" from contenttype on :" + list.ParentWebUrl + "/"
 224:                         + list.Title, EventLogEntryType.Information);
 225:                     continue;
 226:                 }
 227:                 if (!FieldExist(sourceCT, sourceFieldLink))
 228:                 {
 229:                     Log(
 230:                       "Doing nothing to field: " + sourceFieldLink.DisplayName
 231:                        + " on list " + list.ParentWebUrl
 232:                        + "/" + list.Title + " field does not exist (in .Fields[])"
 233:                        + " on source content type", EventLogEntryType.Information);
 234:                     continue;
 235:  
 236:                 }
 237:  
 238:                 if (listCT.FieldLinks[sourceFieldLink.Id] != null)
 239:                 {
 240:  
 241:                     Log("Deleting field \"" + sourceFieldLink.Name
 242:                         + "\" from contenttype on :" + list.ParentWebUrl + "/"
 243:                         + list.Title, EventLogEntryType.Information);
 244:  
 245:                     listCT.FieldLinks.Delete(sourceFieldLink.Id);
 246:                     listCT.Update();
 247:                 }
 248:  
 249:                 Log("Adding field \"" + sourceFieldLink.Name
 250:                     + "\" from contenttype on :" + list.ParentWebUrl
 251:                     + "/" + list.Title, EventLogEntryType.Information);
 252:  
 253:                 listCT.FieldLinks.Add(new SPFieldLink(sourceCT.Fields[sourceFieldLink.Id]));
 254:                 //Set displayname, not set by previous operation
 255:                 listCT.FieldLinks[sourceFieldLink.Id].DisplayName = sourceCT.FieldLinks[sourceFieldLink.Id].DisplayName;
 256:                 listCT.Update();
 257:                 Log("Done updating fields ");
 258:             }
 259:         }
 260:  
 261:         /// <summary>
 262:         /// Fields the exist.
 263:         /// </summary>
 264:         /// <param name="contentType">Type of the content.</param>
 265:         /// <param name="fieldLink">The field link.</param>
 266:         /// <returns></returns>
 267:         private static bool FieldExist(SPContentType contentType, SPFieldLink fieldLink)
 268:         {
 269:             try
 270:             {
 271:                 //will throw exception on missing fields
 272:                 return contentType.Fields[fieldLink.Id] != null;
 273:             }
 274:             catch (Exception)
 275:             {
 276:                 return false;
 277:             }
 278:         }
 279:     }
 280: }

 

By refactoring the code slightly I'm now able to use the code via the stsadm command, which I called gl-propagatecontenttype, or I can call the Process method via my Feature Receiver by just adding a reference to the assembly - this way I can push changes to content types down to the lists they are bound to when my Feature is re-activated.  Here's the syntax of the command:

C:\>stsadm -help gl-propagatecontenttype

stsadm -o gl-propagatecontenttype


Propagates a site scoped content type to list scoped instances of that content type.

Parameters:
        -url <site collection url>
        -contenttype <content type name>
        [-verbose]
        [-updatefields]
        [-removefields]

 

I suggest you avoid the use of the -removefields parameter if possible - I left it there because I thought I might need it but it's usually not a good thing to do something so destructive in batch like that (just make sure you at least test the change before going to production with it).

Again - props to Søren - I just retooled his code some.

46 comments:

Anonymous said...

Please can you provide a download or easy copy version of this code? Maybe even just in a .txt file?

Gary Lapointe said...

The code is available via the download link (top right of page).

Anonymous said...

What exactly do the arguments -removefields and -updatefields do? Thanks.

Gary Lapointe said...

The -removefields argument will remove any fields from the list scoped content type that are not in the site scoped content type. The -updatefields argument will update any fields in the list scoped content type to make it match that of the corresponding site scoped content type.

Sam said...

Hi,

Very useful stuff - helped me complete my very basic feature receiver :-) Thankyou.

just one detail - you dont update the description, hidden, version, or documenttemplate properties of the content types.

Also there is one little Gotcha:
SPContentTypeUsage.GetUsages(sourceCT); will return all uses of sourceCT including content types that inherit from sourceCT. You need to be careful to traverse content type heirarchy correctly. Not sure what the answer is at the moment still looking.....

Sam

Anonymous said...

Brilliant extension thanks!

Nick said...

Thanks Gary and of course Søren for this one. Your owed many saved hours from a lot of people for these extensions!

Just a question Gary - if you wanted to update a feature defined content type content type, can you think of a good way to update the feature, the site content type and then the list content types? I can see this or Søren's command will take care of the site-->list propogation but any ideas on forcing an update to the feature and then the subsuquent site and list CT's? Thanks in advance

Gary Lapointe said...

Nick - can you elaborate on what you mean by updating the Feature? Are you just talking about re-activating it (which you can do with the stsadm activatefeature command by passing the force parameter)? I'm assuming you know that though are looking for something more different?

Nick said...

Hi Gary,

I am just a little mindful of MS's words of caution "Do not, under any circumstances, update the content type definition file for a content type after you have installed and activated that content type.". I figured forcing the update is good enough but could that ever cause any issues?

Nick said...

And thanks for the prompt reply too!

Gary Lapointe said...

Well, you definitely have to understand the implications of what changes you are making. The recommendation in that article is basically Microsoft's way to avoid potential pitfalls. I think that if you test your deployments and you understand the nature of the changes you are making then you're okay - if things don't work out then you know it's just test so you can always blow the changes away - typically though I tell my clients, after initial roll-out, make all your content type changes via the browser and then update your feature with those changes in case you need to rebuild the farm - it requires a good change control process but it's definitely safer.

Nick said...

I have figured out what I was missing in that conversation - nicely detailed in another one of Sørens posts.

I don't suppose you have a good command to covert from 'virtual' to 'feature' based CT definitions without directly modifying the DB? That idea makes me a little (lot) nervous

Gary Lapointe said...

I don't have anything that will convert the virtual CTs to feature CTs - I suspect there's no way to do it without hitting the DB.

Søren Nielsen said...

Hi gary

Just came across your post searching for some other content type stuff (very interested in the copy method) - and then you reference my old post! Cool :-)

Glad you could use it. It needed a bit of refactoring, good job.

Gary Lapointe said...

Søren - I tried posting to your blog to let you know that I'd snagged your code and incorporated into my commands but I kept getting errors trying to post a comment so I figured you'd eventually stumble upon it :)

hkossu said...

Hi,

About the gl-propagatecontenttype and especially the -updatefields parameter:

I have a deployed contenttype and fields as a feature and afterwards i change values of single choisefield to my xml and do following operations
1) upgradesolution
2) deactivatefeature -force ...
3) activatefeature -force ...
4) gl-propagatecontenttype -updatefields

The result is that values for choisefield in sitecontenttype has changed, BUT values in list contenttypes has not.

I guess the first if comparison in UpdateFields(..) is not catching the changes..

thanks for the great tool!

-hkossu

Gary Lapointe said...

Any chance you can provide the schema xml for the field? (you may have to email it to me)

Christoph said...

Man, you saved me! Searched for days updating used Content Types!

Yeahh, your extensions are awesome!

Fernando Vargas said...

I have a feature that creates three site columns and then tries to attach them to different content types that inherit from eachother using the FeatureReceiver.

To clarify: I have to create the fields in code because they are lookupfields and cannot add them in CAML.. a different story.


Linking the first field to the top content type (My Document) works fine but attempting to link the second field to a child content type (My Correspondence) fails.

LinkFieldToContentType(SPContext.Current.Site.RootWeb, "My Document", (SPField)lookupSecurityClearance);

...so far so good!
LinkFieldToContentType(SPContext.Current.Site.RootWeb, "My Correspondence", (SPField)lookupDocumentType);

... crash. The error:
LinkFieldToContentType: My Correspondence / The object has been updated by another user since it was last fetched.


Here is the linking code.

public static void LinkFieldToContentType(SPWeb web, string contentType, SPField field)
{
try
{
SPContentType ct = web.ContentTypes[contentType];
ct.FieldLinks.Add(new SPFieldLink(field));
ct.Update(true);
}
catch (Exception ex)
{
AddErrorToWindowsEventLog("LinkFieldToContentType:"+ex.Message, EventLogEntryType.Error);
}

}

I just don't understand why the error pops up or why .update(true) simply doesn't work.
If I use simply .Update() nothing creashes but inheritance doesn't happen.

Any suggestions?
Thanks,

Gary Lapointe said...

Try to change your code so that instead of using the SPContext.Current.Web object you open a new instance of the SPWeb object each time.

Fernando Vargas said...

Thanks a lot Gary.
You pointed me in the right direction.

Ed. said...

Hi Gary,

Great library. We use it a lot.

I'm having issues with this command. I've added a field to a site collection content type (Interior Page) that is a calculated field:

=IF([Article Date]="","",TEXT([Article Date],"dddd, dd MMMM yyyy"))

When I run your command to propagate the changes, I get:

PROGRESS: Adding field "FormattedArticleDate" to contenttype on /en-US/Archive/a
boutUs/newsAndArticles/Pages
Unhandled error occured: The formula refers to a column that does not exist. Check the formula for spelling mistakes or change the non-existing column to an existing column.
Finished pushing content type changes to lists for 'Interior Page'
The formula refers to a column that does not exist. Check the formula for spell
ing mistakes or change the non-existing column to an existing column.

The error doesn't make any sense. The Article Date field does exist (I can see it when I look at properties for a page).

Any help would be greatly appreciated. Thanks.
- Ed.

nCubed said...

Gary (and Søren!)... propagate ContentType is exactly what I was working on... thanks for posting your solution and custom stsadm commands. They are an absolute must and time saver! (*_*)

Merlin said...

Hi,

Your command his really usefull.
I notice that some modification are not handle by this command. For Example, I change a site column to be "Required". After running the script, the configuration is not propagated.
Is that normal ?
Do you plan to implement this

thx again

MM

tripwire said...

@Merlin

I thought this as well. Running the command without parameters doesn't seem to do anything noticable at the list level.

You need to add the -updatelists flag.

This has worked so well that I've since scripted it and replaced what was an otherwise very expensive and very clunky solution.

@Gary Do you ever grow tired being thanked for your incredible contributions? :)

tripwire said...

Not sure if anyone else is getting this, or if it's just me.

All my custom site columns are getting the internal name at the List Content Type level for all sub sites and collections.

e.g. Document Category becomes Document_x0020_Category.

I'm running the latest version of Lapointe.SharePoint.STSADM.Commands.wsp and have tested this several times with new document libraries.

The root site is unaffected by this issue - only sub sites and collections.

I'm running the command with the -updatefields and -verbose parameters.

Looking at the output I can see some odd things occurring. The process seems to run multiple times on the same list - I'm assuming for each of the inherited content types?

And in the case of the affected sub sites, the first iteration on a library seems to Delete and Add every column, including system and sealed fields.

8<--log snip

PROGRESS: Processing content type on list:/AboutUs/Docos
PROGRESS: Starting to update fields
PROGRESS: Deleting field "ContentType" from contenttype on :/AboutUs/Docos
PROGRESS: Adding field "ContentType" from contenttype on :/AboutUs/Docos
PROGRESS: Done updating fields
PROGRESS: Deleting field "SelectFilename" from contenttype on :/AboutUs/Docos
PROGRESS: Adding field "SelectFilename" from contenttype on :/AboutUs/Docos
PROGRESS: Done updating fields
PROGRESS: Deleting field "FileLeafRef" from contenttype on :/AboutUs/Docos
PROGRESS: Adding field "FileLeafRef" from contenttype on :/AboutUs/Docos
PROGRESS: Done updating fields
PROGRESS: Deleting field "Created" from contenttype on :/AboutUs/Docos
PROGRESS: Adding field "Created" from contenttype on :/AboutUs/Docos
PROGRESS: Done updating fields

--8< end

Any help appreciated.

tripwire said...

@Gary: To clarify the above, the column ID is still the same and updates are still occurring.

But the Name and not the DisplayName is being applied at the List Content Type level.

i.e. From XML definition.

DisplayName="Document Type"
Name="Document_x0020_Type"

Hope that helps.

tripwire said...

If it's any help, it looks as though ln:255 isn't taking effect.

I only just upgraded your solution so, if this was a recent code change, then it's possible I need to recycle the app pools?

tripwire said...

It looks like the column order is also being affected on the List Content Type. Couldn't see anything in the code that was managing this.

Additionally, I don't suppose there's also a method to update the document template? This doesn't seem to be affected by this command.

Gary Lapointe said...

Odd - not sure why that's happening. It should only be updating fields if the schemas are different for the field and for fields such as ContentType, Created, etc. they should really never be different. No idea why the display name is changing to the internal name either. Odd...

Gary Lapointe said...

I haven't changed the code in months so not sure why it's not working for you. I'll have to find some time to play with it but I'm swamped right now with some 2010 stuff.

tripwire said...

Hi Gary, I trust your 2010 testign is going well.

Just wanted to let you know that I deleted the site columns and content types and then re-added via feature across all site collections. The column renaming problem no longer ssmes to occur.

I'm now desperately hoping that you might have the opprtunity to add paramters or code mods to
optionally push down Document Template and also correct the column order.

The latter occurs when an inherited CT has columnar differences and the parent CT is used with the command. Any column that differs from the parent (e.g. Title column is marked as Required) gets Deleted and then Added to the end.

Alternately, an -ignoreChildren parameter would do the trick. I could then write a batch file to hit each CT in turn.

Let me knw how much I owe you! I'm not kidding. :D

Gary Lapointe said...

I think I have a fix for the column ordering and document template. I've pushed out my changes if you want to take a look - hopefully it works for you. Thanks again for all your feedback - it's most appreciated!

Craig said...

We have the same problem with the display name changing to the internal name. It only happens on one list that uses the content type though!
Did you ever get to the bottom of that issue Gary?

tripwire said...

Hi Gary, you really are great. I'm going to start doing some serious testing now!

My main issue still seems to be with CT inheritance. Did you come up with anything for this, or should I just run the command on all required content types in order of inheritance?

tripwire said...

Hi Gary, initial testing has been successful. Column ordering is restored and the document template field is pushed down.

In all cases, any child CTs will inherit the column values and flags (e.g. required, read only, hidden) of the CT specified in the command. Similarly, the document template is overwritten.

Running the command again on each affected child CT restore the correct values for the list CT. I have batched this process and it's very quick to run.

As a final feature request, do you think you could add -doctemplate as an optional flag??

Again -- thank you, thank you, thank you. You've saved me hours of laborious manual work and down-time to repair broken CTs across an ever-growing farm!

Gary Lapointe said...

tripwire - thanks for the testing feedback - Not sure that I'll have time to address the inheritance issue as my focus right now is getting this stuff working on 2010 - I might be able to do something when I start migrating this command over but can't promise anything (same with the doctemplate switch).

Gary Lapointe said...

Craig - I haven't had a chance to look into this but I will see what I can do when I start migrating this command to 2010.

Priya said...

Gary,
The above solution is not propagating the readonly, ShowInNewForm,ShowInEditForm attribute changes made in the xml file. Do you have any idea how to achieve this?

Gary Lapointe said...

The API doesn't expose those attributes in a way that allows me to edit them without using reflection. You'd basically need to use reflection to get the internal XML representation and edit that XML.

Priya said...

Thanks Gary...Do you have any idea how to use reflection to achieve this..Can you point me some sample code so that I will get better idea.

Gary Lapointe said...

Look at how I'm setting the content type ID in the propagatecontenttypes.cs class. You'll need to do something similar with the FieldLink object for the field.

Kieran said...

Hi Gary

What's the main difference between your utility above and using the contenttype.update(true) command , that updates that contenttype and all child references. Am I missing something?

all the best

Gary Lapointe said...

If you've made changes declaratively they are not pushed down and calling that overload will do nothing. The overload is only if you've made programmatic changes.

Anonymous said...

It seems this command doesn't push down 'default values'

I have a field declared in xml

1


The field is pushed down to all existing pages lists, but the default value is unfortunately not.

/Lars

Craig said...

Gary, I am using the command gl-propagatecontenttype to propagate changes to my content type in my site collection to all Pages libraries that currently utilize it. The only thing that I changed in my content type is a single line of text field with a new max length of 30 characters instead of 20 characters. When I run the command stsadm -o gl-propagatecontenttype -url http://sharepointdev -contenttype "Tab Page" -verbose -updatefields. Do you know why my new max length of 30 was not pushed down to my lists? I check the code and it looks like it should do that. Please let me know what your thoughts are here on this matter.