As documented on msdn, the IgnoreIfAlreadyExists attribute in the file element provisions a file even if it already exists at the specified URL. However it doesn't works as expected.
Searching for solution on the web, I came across a fantastic blog post by John Leino where he has suggested a programmatic approach to workaround this issue.
John describes creating a feature receiver which upon activation would get all the files along with their properties from the module element. Based on these properties, the files would then be added/overwritten on their target locations.
I created a small POC using John’s approach and it really worked well !. But his code seems to break under the following conditions:
Added a CheckOutStaus method to get the file checked out status:
For publishing sites the content needs to be approved before it can be seen by other site users (e.g. on the masterpage library). So I added a method CheckContentApproval to verify that whether content approval is enabled on the target SharePoint library:
Updated the UpdateFilesInModule method to looks like this:
So in the above method after from getting all the files and their properties from the module, we are doing two thing
Searching for solution on the web, I came across a fantastic blog post by John Leino where he has suggested a programmatic approach to workaround this issue.
John describes creating a feature receiver which upon activation would get all the files along with their properties from the module element. Based on these properties, the files would then be added/overwritten on their target locations.
I created a small POC using John’s approach and it really worked well !. But his code seems to break under the following conditions:
- If you have multiple modules in your element.xml file.
- If the target SharePoint library forces the checking and checkout policy.
Added a CheckOutStaus method to get the file checked out status:
For publishing sites the content needs to be approved before it can be seen by other site users (e.g. on the masterpage library). So I added a method CheckContentApproval to verify that whether content approval is enabled on the target SharePoint library:
Updated the UpdateFilesInModule method to looks like this:
So in the above method after from getting all the files and their properties from the module, we are doing two thing
- Getting the checkout status of the file.
- Checking whether content approval is enabled on the target SharePoint library and accordingly approving the file.
Great that you found my blogpost as a good example and great that you have extended it with some modifications, I´ll add them to my personal code lib :-). / Johan - not John ;-)
ReplyDeleteAwesome...had the problem with Page Layouts deployed by features.
ReplyDeleteWould be great you post the Project as a zip, not only the source code. Had some trouble with namespaces.
Thanks, saved me!
Thanks Christoph, I am glad it helped you.
ReplyDeleteI'll post the project soon.
Another approach to this would be a feature receiver that simply deletes the file in question before using an standard modules feature to upload your new copy.
ReplyDeleteYou could use Activation Depenencies to ensure that the orginal file is deleted before the module feature uploads the new one.
This is much simpler from a code perspective and is just this:
using (SPSite site = new SPSite("http://yoursiteurl"))
{
using (SPWeb web = site.RootWeb) //or find your web by some other method if it is not the root web
{
web.GetFile(web.Url +"/the full path to your file").Delete();
}
}
Not saying this is a 'better' solution, the one outlined here is very good and comprehensive, just an alternative is all.
Regards
I was suffering from the same issue with page layouts not getting updated. A coworker mentioned that he saw a post saying that page layouts with a Page tag that has the following attributes is automatically considered customized as soon as it is installed:
ReplyDeletemeta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full"
I thought there was no way it would work and I'd be writing code to update the page layouts, but when they were removed, at least based on my initial testing, they actually update now.
Now if you already have a bunch of pages using the page layout you want to change and it was installed with those tags still in them, it seems like it might be a catch22 (there's no way to get the updated page layout into those sites since those pages are considered customized, in which case it might take code to unwind it--but more as a one time adminstrative thing). That's just a theory though. I'm not sure if there are any bad side effects to removing those tags, but I'll be on the watch for them.
Thanks, the code works great!
ReplyDeleteJust found this, exactly what I need. life saver!
ReplyDeleteI'm getting an error after running the activation: The base type 'xxx' is not allowed for this page. The type is not registered as safe.
ReplyDeleteANyone else receive this?
Link above is broken...
ReplyDeleteJohan Leino's blog entry on IgnoreIfAlreadyExists is at:
http://johanleino.wordpress.com/2009/04/22/howto-handling-file-updates-in-sharepoint/
Getting Object Reference not set error.. Any ideas?
ReplyDeleteVisual Studio 2010 does not create a Path attribute on the Module element of the Elements.xml file when creating a new Module. The attribute is optional and could be set on the File child element instead (which is also optional). You can update the code to set PhysicalPath for module to
DeletePhysicalPath = (m.Attribute("Path") == null) ? string.Empty : Path.Combine(featureDir, m.Attribute("Path").Value),
You can also add a PhysicalPath property to the Files property of Module in the code file and then set that to:
PhysicalPath = (f.Attribute("Path") == null) ? string.Empty : Path.Combine(featureDir, f.Attribute("Path").Value),
Then in the foreach loop for each of the files in the module,
physicalPath = string.IsNullOrEmpty(file.PhysicalPath) ? Path.Combine(module.PhysicalPath, file.Name) : Path.Combine(file.PhysicalPath, file.Name);
With the assumption being if there is not a Path property on the file element itself, there would be one set for the module.
This should resolve your exception.
Actually, the last line in my comment about the foreach loop, it should be this:
DeletephysicalPath = string.IsNullOrEmpty(file.PhysicalPath) ? Path.Combine(module.PhysicalPath, file.Name) : file.PhysicalPath;
Inside 'UpdateFilesInModule', when setting the module variable, you should really call FirstOrDefault() instead of First(). The way it is right now would throw an exception. FirstOrDefault would return null instead. You might want to do some additional checking inside it to see if those properties actually exist.
ReplyDeleteYes anonymous, we need adjust a lot of things there, but the principal is troubleshooting the module, because some files need a different approach.
ReplyDeleteSome files have some required properties that you need fill in module, that in code you will to get using linq-to-xml (Property).
In code, we need to analyze too. Let's think:
- Pages Library: Normally requires check-out and after needs a check-in. If content approval is enabled, we need approve it.
- MasterPages: You cannot delete a instance that is been used, then you need check if is a masterpage before. If doesn't, you can update (overwrite) the file.
For another types, you need check firstly if file is checked-out for another user. If yes, you need do a undo-checkout before anything.
I recommend that you log each step to understand what is happening.
Soon, when I finish my code here, I'll post the final version.
See ya!