As mentioned in this post, files provisioned into SharePoint using Feature Modules, do not get removed when the feature is deactivated. This may or may not be a problem to you, except that, my own experience has shown that, say, when upgrading features, existing files provisioned by a previous version of a feature, sometimes do not get overwritten, or new files in a new version of the feature sometimes get ignored if the file already exists. Meaning that you may not get the right files for your feature installed into SharePoint.
To work around this issue, what you can do is remove files programmatically when the feature is deactivated, and obviously for this you need a feature receiver.
The code to remove files from SharePoint is pretty generic since it invariably involves removing SPListItem files from a Document Library or Folder. Presented below is some code which looks at the Feature elements definitions and selects the Name of provisioned files (the URL attribute of a Module File element).
It then uses those file names to select the associated SPListItem‘s from the Site Collection Web Part Gallery in order to delete them.
// get the top level site collection var spSite = properties.Feature.Parent as SPSite; if (spSite == null) throw new Exception( "Feature scope mismatch, Feature was not a Site scoped feature"); // get the Feature elements definitions var fdElements = properties.Definition.GetElementDefinitions(); // select the URL attribute values of File elements (Web Part files) var fdWebparts = fdElements.Cast<SPElementDefinition>() .SelectMany(el => el.XmlDefinition.ChildNodes.Cast<XmlElement>() .Where(fe => fe.Name.Equals("File")) .Select(fe => fe.Attributes["Url"].Value) ).ToList(); // grab the Web Part Gallery var wpGallery = spSite.RootWeb.Lists["Web Part Gallery"]; // select SPListItems from the Web Part Gallery where the associated file name is one of // the previously selected Web Part file names var wpToDelete = wpGallery.Items.Cast<SPListItem>() .Where(wp => fdWebparts.Contains(wp.File.Name)) .ToList(); // and delete them from the document library (NOTE: the reverse processing order) for (var idx = wpToDelete.Count - 1; idx >= 0; idx--) { var item = wpToDelete[idx]; item.Delete(); }
Another option, depending on your needs, is much simpler.
Maintain a list of the files to be removed during deactivation as an array of name/value pairs, then some simple code to run over the list removing the files.
// provisionedFiles is an array of name/value pairs, Name contains the folder name, Value contains the filename foreach (var provisionedFile in provisionedfiles) { // get the SPFolder var spfolder = spWeb.Folders[provisionedFile.Name]; if (spfolder == null) continue; // format the URL of the file, find the file and delete it. var url = string.Format("{0}/{1}", provisionedFile.Name, provisionedFile.Value); var spFile = spfolder.Files[url]; if (spFile != null) { spfolder.Files.Delete(url); spfolder.Update(); } else { // file not found in sharepoint!! } }
Nice moves Phil. Love the digging it out of the elements.xml. Thanks for making the web a better place!