Wednesday, June 9, 2010

Update 12 Hive Through A Feature

As we got our SharePoint environment up and running, we needed to have the ability to make changes to our 12-Hive. We also wanted to have the flexibility to undo the changes and restore the 12-Hive to the way it was originally.

This post discusses the scenario where you need to make changes to files that already exist in the 12-Hive. I call this solution HiveUpdates. If you have a need to add new files to the 12-Hive that do not already exist, such as new images or layouts, check out my other article Add New Files To 12-Hive Through A Solution. I call that solution HiveAdditions. We decided it would be best for us to separate these two types of Hive changes into two separate solutions.

This solution, HiveUpdates, allows us to modify existing Hive files. This solution contains a feature that, when activated, makes a backup copy of the original file, then overwrites the original with the new file. When the feature is deactivated, it copies the backup file back to the original filename.

The method I used is based on the article 12 Hive System-File Changes: One Feature to rule them all!. The above post has a routine that is called when the feature is activated or when its deactivated. The code that fires simply "swaps" newly changed 12-hive files with their corresponding original 12-hive files. This happens both when the feature is activated or deactivated. While the "swap" code seems like a good idea on the surface (the same code that deploys the changes will swap files back to undeploy them), I found in practice this seemed risky and even prone to error if something went wrong on the feature activate or feature deactivate. I sometimes got the files out of sync and would lose the original 12-Hive versions.

Therefore, I modified the code to first make a backup copy of a 12-Hive file I want to update, then overwrite the original 12-hive file with my changed version (this is an activate). On a deactivate, it overwrites my changed version with the original's backup file. The original's backup file always stays there as a backup, which makes me feel very safe. And I like feeling safe. I like my job and want to keep it.

1. In Visual Studio 2008, File -> New -> Project

2. Under Visual C#, pick SharePoint, then Empty

3. For Name, type: HiveUpdates

4. For Location, type: D:\development

5. Click OK

6. Select Full Trust (Deploy to GAC) and click OK

7. In WSP View (View -> Other Windows -> WSP View), click "Create new feature"

8. Change Feature Scope to Farm

9. Check Add feature receiver

10. click OK

11. In Solution Explorer, rename Feature1 to HiveUpdates

12. Rename Feature1Receiver.cs to HiveUpdatesReceiver.cs

13. Double-click HiveUpdatesReceiver.cs to open it

14. Rename class from FeatureReceiver1 to HiveUpdatesReceiver

15. Below that, you'll see the constructor "public FeatureReceiver1". Rename constructor from FeatureReceiver1 to HiveUpdatesReceiver.

16. In WSP View, hit refresh and notice FeatureReceiver1 is now HiveUpdatesReceiver

17. Rename Feature1 folder to HiveUpdates

Make the following code modifications/additions to HiveUpdatesReceiver.cs:

18. At the top of the code, add the following using statements:
using Microsoft.SharePoint.Administration;
using System.IO;

19. Above the HiveUpdatesReceiver constructor, add the following:
private DirectoryInfo localDirectory = new DirectoryInfo(@"C:\");

20. In the FeatureActivated method, add:
DeployToHive(properties);

21. In the FeatureDeactivating method, add:
RemoveFromHive(properties);

22. Below the FeatureUninstalling method, add the following 4 methods (DeployToHive, RemoveFromHive, GetHiveFile, GetLocalDirectory):
private void DeployToHive(SPFeatureReceiverProperties properties)
{
//loop through each server in the farm
foreach (SPServer server in properties.Definition.Farm.Servers)
{
//if the current server is a WebFrontEnd server
if (server.Role == SPServerRole.WebFrontEnd || server.Role == SPServerRole.Application
|| server.Role == SPServerRole.SingleServer)
{
string localFilePath = GetLocalDirectory(properties.Definition.RootDirectory,
server.Name);
localDirectory = new DirectoryInfo(localFilePath);

foreach (FileInfo fileinfo in localDirectory.GetFiles("*",
SearchOption.AllDirectories))
{
//if current file in feature exists in 12 hive on current server
if (File.Exists(GetHiveFile(fileinfo.FullName, server.Name))
&& (!fileinfo.FullName.Contains(".hivebackup")))
// backup file from 12 hive to feature
File.Copy(GetHiveFile(fileinfo.FullName, server.Name),
fileinfo.FullName + ".hivebackup", true);

//if current file in feature exists
if (File.Exists(fileinfo.FullName) &&
(!fileinfo.FullName.Contains(".hivebackup")))
// copy local file to the 12 hive
File.Copy(fileinfo.FullName, GetHiveFile(fileinfo.FullName, server.Name),
true);
}
}
}
}



private void RemoveFromHive(SPFeatureReceiverProperties properties)
{
//loop through each server in the farm
foreach (SPServer server in properties.Definition.Farm.Servers)
{
//if the current server is a WebFrontEnd server
if (server.Role == SPServerRole.WebFrontEnd
|| server.Role == SPServerRole.Application
|| server.Role == SPServerRole.SingleServer)
{
string localFilePath = GetLocalDirectory(properties.Definition.RootDirectory,
server.Name);
localDirectory = new DirectoryInfo(localFilePath);

foreach (FileInfo fileinfo in localDirectory.GetFiles("*",
SearchOption.AllDirectories))
{
//if current backup file in feature exists in 12 hive
if (File.Exists(fileinfo.FullName + ".hivebackup"))
// copy hivebackup file back to the 12 hive
File.Copy(fileinfo.FullName + ".hivebackup",
GetHiveFile(fileinfo.FullName, server.Name), true);
}
}
}
}



string GetHiveFile(string fileName, string serverName)
{
if (fileName.StartsWith(localDirectory.FullName))
{ // file is local
string p = @"\\" + serverName
+ @"\C$\Program Files\Common Files\Microsoft Shared\web server extensions\12"
+ fileName.Remove(0, localDirectory.FullName.Length);
return p;
}
throw new Exception("Filepath doesn't point to correct local hive!");
}



string GetLocalDirectory(string filePath, string serverName)
{
return @"\\" + serverName + @"\" + @filePath.Replace(":", "$") + @"\12";

//write a class that writes exceptions to the event log
throw new Exception("Filepath doesn't point to correct local hive!");
}

Now we need to create the appropriate 12-Hive file structure for the feature in Solution Explorer. Any 12-Hive files we want to update will have their newer versions placed in the file structure we are about to create.

23. In Solution Explorer, right-click project HiveUpdates, then click Add -> New Item... -> SharePoint -> Template.

24. You will notice a folder called Templates was created in the Solution Explorer. Delete the default file TemplateFile1.txt.

25. Under Templates, create a folder called FEATURES.

26. Under FEATURES, create a folder called HiveUpdates.

So far, we have created 12/TEMPLATE/FEATURES/HiveUpdates. This is the location in the 12-Hive where that feature's feature.xml gets deployed to. But now, we must create another 12-Hive structure under this folder. This is where our newer files will be deployed to when the solution is initially deployed. When the feature is activated, this is where the above code will store backups of the original files from the 12-Hive, as well as the source from which to overwrite the 12-Hive files with.

27. Under HiveUpdates, create a folder called 12.

28. Under 12, create a folder called TEMPLATE.

29. Under TEMPLATE, create a folder called LAYOUTS.

30. Copy application.master in here. Either make some small change to it or at the very least change the date on the file so its different from what it is in the 12-Hive. Be sure to right-click application.master and click Include In Project if it is not already included.

31. In WSP View, double-click feature.xml.

32. Change the value of Title from Feature1 to HiveUpdates.

33. Verify Scope is set to Farm.

33. After the title or scope, add: ActivateOnDefault="false"

34. Verify your manifest was created correctly. In WSP View, Under HiveUpdates, you should see manifest.xml. Double-click manifest.xml and verify your file path for application.master is FEATURES\HiveUpdates\12\TEMPLATE\LAYOUTS.

35. click Build -> Rebuild Solution

36. click Build -> Package Solution

The solution package that you just created in the above step, HiveUpdates.wsp, was created in your project's bin\debug folder.

To deploy the solution:

37. create folder d:\deploy

38. copy HiveUpdates.wsp from project's bin\debug folder to d:\deploy

39. In d:\deploy, create two batch files, add_hiveupdates.bat and remove_hiveupdates.bat.

add_hiveupdates.bat:
c:

cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN

stsadm -o addsolution -filename d:\deploy\HiveUpdates\HiveUpdates.wsp

stsadm -o deploysolution -name HiveUpdates.wsp -immediate -allowGacDeployment

pause

stsadm -o activatefeature -name HiveUpdates

d:

remove_hiveupdates.bat:
c:

cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN

stsadm -o deactivatefeature -name HiveUpdates

stsadm -o retractsolution -name HiveUpdates.wsp -immediate

pause

stsadm -o deletesolution -name HiveUpdates.wsp

d:

40. in d:\deploy, type add_hiveupdates.wsp and hit enter

41. verify solution was deployed in Central Administration -> Operations -> Solution Management

42. verify files were added to 12-Hive!

Saturday, June 5, 2010

Add New Files To 12-Hive Through A SharePoint Solution

As we got our SharePoint environment up and running, we needed to have the ability to make changes to our 12-Hive. We also wanted to have the flexibility to undo the changes and restore the 12-Hive to the way it was originally.

This post discusses the scenario where you need to add new files to the 12-Hive that do not already exist, such as new images or layouts. I call this solution HiveAdditions. If you have a need to update files that already exist in the 12-Hive, check out my other article Update 12 Hive Through A Feature. I call that solution HiveUpdates. We decided it would be best for us to separate these two types of Hive changes into two separate solutions.

This solution, HiveAdditions, simply allows us to add files into the 12-Hive upon a deployment. When we retract this solution, it simply removes all those files. We liked this approach because it allows us to have this solution as the one place for all new 12-Hive files. If we needed to add new files, we retract this solution, add the new files to the solution, then deploy it again.


1. In Visual Studio 2008, File -> New -> Project

2. Under Visual C#, pick SharePoint, then Empty

3. For Name, type: HiveAdditions

4. For Location, type: D:\development

5. Click OK

6. Select Full Trust (Deploy to GAC) and click OK

7. In Solution Explorer, right-click project HiveAdditions, then click Add -> New Items... -> SharePoint -> Root File

8. Leave Name alone and click Add

9. Under RootFiles (which now appears in Solution Explorer), delete RootFile1

Under RootFiles, we must mirror the 12-Hive locations where we want to add the new files.

Let's add image files under TEMPLATE/IMAGES.

10. Right-click RootFiles, click Add -> New Folder and name it TEMPLATE.

11. Right-click TEMPLATE, click Add -> New Folder and name it IMAGES.

12. Drag and drop your images into IMAGES folder. Be sure to right-click newly added files and click Include In Project if they are not already included.

13. Verify your manifest was created correctly. Click View -> Other Windows -> WSP View. Under HiveAdditions, you should see manifest.xml, then under that, RootFiles -> TEMPLATE -> IMAGES followed by the files you added. Double-click manifest.xml and verify your file paths are under TEMPLATE\IMAGES\file1.jpg, etc.

14. click Build -> Rebuild Solution

15. click Build -> Package Solution

The solution package that you just created in the above step, HiveAdditions.wsp, was created in your project's bin\debug folder.

To deploy the solution:

16. create folder d:\deploy

17. copy HiveAdditions.wsp from project's bin\debug folder to d:\deploy

18. In d:\deploy, create two batch files, add_hiveadditions.bat and remove_hiveadditions.bat.

add_hiveadditions.bat:
c:

cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN

stsadm -o addsolution -filename d:\deploy\HiveAdditions\HiveAdditions.wsp

stsadm -o deploysolution -name HiveAdditions.wsp -immediate -allowGacDeployment

d:
remove_hiveadditions.bat:
c:

cd\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN

stsadm -o retractsolution -name HiveAdditions.wsp -immediate

pause

stsadm -o deletesolution -name HiveAdditions.wsp

d:
19. in d:\deploy, type add_hiveadditions.wsp and hit enter

20. verify solution was deployed in Central Administration -> Operations -> Solution Management

21. verify files were added to 12-Hive!