Feature deploying files into SharePoint such as web parts or master pages is something that most developers and admins of any caliber should be familiar with. Also called "provisioning", it's the bread and butter of customizing SharePoint, if you will. Part of the basics. However, there are some serious issues with how SharePoint accomplishes this out of the box.
The first issue I found a year or so ago. I was deploying some web parts through a feature, and went to deactivate the feature. After deactivation, much to my dismay, I found the web parts still deployed in the web part gallery. Not a huge deal, but an annoyance to be sure. So I rolled up my sleeves, dove into the SharePoint object model, and developed a custom event receiver that removed the web parts previously deployed when deactivated.
I found the second issue I found a few weeks ago. I was working on deploying a custom master page and I started hitting problems. Aside from hitting the first problem again, I found that overwriting a master page (or page layout) was impossible using the out of the box SharePoint file provisioning. At first I was trying to override default.master, but then realized the errors of my ways (may the SharePoint gods forgive me). Never overwrite a default master page. Just copy it, rename it, and build your new one from there. You will save yourself future headaches and pain. But I digress, this did get me thinking of the problems with upgrading the master page in the future. If I can't overwrite a page that's already deployed, if I make a change to the master page and upgrade my solution, that means the upgraded master page won't get deployed. Seems rediculous, but it's true. I tested it thoroghly to make sure. I even tried using the IgnoreIfAlreadyExists flag, but found later that that flag doesn't do what I thought it did anyway. So what to do...
Once again I had to roll up my sleeves and dive into the SharePoint object model and create a feature receiver to solve my woes. And with the trusty Google at my side, I was able to pound out a good solution. Thanks to Johan Leino's post on handling the IgnoreIfAlreadyExists attribute, I found most of the code I needed. He wrote a feature receiver that uses LINQ to parse the elements.xml of a normal file provisioning feature. Then if the IgnoreIfAlreadyExists attribute is there, his feature receiver will overwrite the current file. I took that, then added code to deploy a new file, as Johan's code only handled updating a file that already exists. Finally I added the deactivating functionality so that the files that are deployed on feature activation are removed after feature deactivation1. The beauty of this solution is that it supports all the file properties that a normal SharePoint provisioning feature supports. Just use the normal elements.xml files that you would with any other file provisioning feature, but in your feature.xml, point to the feature receiver assembly instead. And voila, you have a beautiful, upgradable file provisioning solution!
I've attached my final .cs file as well as sample feature.xml and elements.xml files for your SharePoint developing pleasure.
The only issue I have is that when you don't specify the path attribute in the module element of the elements.xml the feature throws an error on activation. Obviously to solve this you just add Path="" into the module element, but I'd like to fix the .cs file so it checks that the path attribute exists before it tries to read from it. Unfortunately I'm a newbie when it comes to LINQ, so any help on that would be appreciated. Just let me know in the comments.
UPDATE: I just came across this post by Becky Bertram about updating page layouts. So apparently it should work, you just have to watch out for rogue SharePoint Designer tags.
1. This turned out to be a pain because there's a bug when deleting a master page from the master page gallery. I wrote a post on the issue and the fix a few weeks ago. You just have to create a folder, move the files-to-be-deleted into that folder, then delete the folder. In my case I had to do that through the object model, but if you are just testing and need a one time fix, you can do all that through SharePoint designer.