HowTo: Handling updates to files in SharePoint – truly handling the IgnoreIfAlreadyExist attribute

Here´s one of those really annoying things in SharePoint. When you provision a file through a module,

image

in this example it´s a webpart file, and add the IgnoreIfAlreadyExist attribute you would expect it to be overwritten (upgraded) when you reactivate the feature but that´s not working.
There are numerous blogs about this problem, some of them are here, here and here.

So, let´s first see how it works and then see if we can get around it somehow.
In this little example I have made a feature that provisions a webpart file to the _catalogs/wp library. After doing that the xml looks like this when I open it in IE from the library in SharePoint.

image

To prove that the attribute (IgoreIfAlreadyExist) doesn´t work I’ll make a small change in the webpart file and reactivated the feature.

image

As expected (or maybe not expected) nothing happens in the webpartfile in SharePoint. So what now?

UPDATE: Since I posted this code on Spaces, Vivek has made some changes to it that I will include in my post.

UPDATE AGAIN: Since I posted this code on Spaces, I have made a lot of changes to it (among other to include multiple module elements). I have also decided to make an extension method out of it instead.

Doing it by code

So I´ll make a featurereceiver for my webpart feature and try to code my way around this problem instead:

class WebPartFeature : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
properties.UpdateFilesInModule();
}

UpdateFilesInModule extension method



public static class SPFeatureReceiverPropertiesExtensions
{
public static void UpdateFilesInModule(this SPFeatureReceiverProperties instance)
{
var modules = (from SPElementDefinition element in instance.Feature.Definition.GetElementDefinitions(CultureInfo.CurrentCulture)
where element.ElementType == "Module"
let xmlns = XNamespace.Get(element.XmlDefinition.NamespaceURI)
let module = XElement.Parse(element.XmlDefinition.OuterXml)
select new
{
Url = module.Attribute("Url").Value,
Path = Path.Combine(element.FeatureDefinition.RootDirectory, module.Attribute("Path").Value),
Files = (from file in module.Elements(xmlns.GetName("File"))
select new
{
Url = file.Attribute("Url").Value,
Properties = (from property in file.Elements(xmlns.GetName("Property"))
select property).ToDictionary(
n => n.Attribute("Name").Value,
v => v.Attribute("Value").Value)
}).ToList()
}).ToList();

using (SPWeb web = (instance.Feature.Parent as SPSite).OpenWeb())
{
modules.ForEach(module =>
{
module.Files.ForEach(file =>
{
string hivePath = Path.Combine(module.Path, file.Url); // in 12-hive
string virtualPath = string.Concat(web.Url, "/", module.Url, "/", file.Url); // in MOSS

if (File.Exists(hivePath))
{
using (StreamReader sr = new StreamReader(hivePath))
{
SPFile virtualFile = web.GetFile(virtualPath);
bool CheckOutEnabled = virtualFile.Item.ParentList.ForceCheckout;
bool NeedsApproval = virtualFile.Item.ParentList.EnableModeration;

if (CheckOutEnabled)
{
virtualFile.CheckOut();
}

virtualFile = web.Files.Add(virtualPath, sr.BaseStream, new Hashtable(file.Properties), true);

if (CheckOutEnabled)
{
virtualFile.CheckIn("Updated", SPCheckinType.MajorCheckIn);
}

if (NeedsApproval)
{
virtualFile.Approve("Updated");
}

virtualFile.Update();
}
}
});
});
}

}
}

So, what is actually happening here?
First up is to get the element.xml file from the SPElementDefinition. I´m using LinqToXml to parse it to an anonymous  “Module” class with “Files” and “Properties”. It´s just a neat C# representation of the xml content that is easier to work with (or at least I think so).
Each file in each module is then uploaded into MOSS with the help of a  StreamReader to first open the file in the 12-hive and then using the content of that file to overwrite the file in the SharePoint database.

And now when I reactivate the feature:

image

By the way this works for all sorts of files (not just webpart files)!

UPDATE 2010-02-20: The code for the extension method can now be found at codeplex.

About these ads

, , , , ,

  1. #1 by Charlie Holland on August 21, 2009 - 16:44

    I couldn’t get your NeedsCheckOut method to behave as expected.

    I’ve found that:

    return file.Item.ParentList.ForceCheckout;

    does the trick though!

    • #2 by Johan Leino on August 21, 2009 - 16:53

      Great! I actually wrote that method outside of VS just updating my post with the stuff that I got feedback on beforehand. I have updated the code with your corrections. Once again..thanks!

  2. #3 by Perry SharePoint on January 7, 2010 - 04:09

    You said that you posted the code somewhere (“Spaces”?)? Would you mind giving a hyperlink to where the code is posted?

    • #4 by Johan Leino on January 7, 2010 - 11:16

      Ah, you misunderstood me. I first posted this code at my old blog at spaces but since then I have made a couple of changes to it. All the code you need is in this post, though I haven´t made a zip file of it and posted it somewhere.

  3. #5 by Vignesh on June 20, 2011 - 21:38

    Fantastic, thanks a bunch.

  4. #6 by mandar on September 17, 2013 - 07:24

    is it possible to update only specific or in other words, Escape certain files form upgrade?

  1. Changing the disk location of uncustomized (ghosted) pages « The SI SharePoint Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 307 other followers

%d bloggers like this: