Thursday, March 22, 2007

SharePoint Resources, Types, Use and Deployment

In the recent weeks one of my tasks was to work on the localization of a WSS solution and deployment of resources to WSS sites. I found that this topic is still not well explained and there are not too many sources that describe the specifics of using and deploying resources in WSS 3. In this post I'll explain the differences in the use and deployment of several types of resource files.

Types of WSS Resources

There are two groups of resource files in WSS. The first one includes files used during provisioning of sites and features. This group I'll call provisioning resources. The second group includes resource files used by ASPX pages and assemblies at run-time. This group respectively I'll name run-time resources.

The provisioning resources are located in the 12 hive in ..\12\Resources. In WSS the default provisioning resource file is core.resx. This file is used in site definitions (one.xml) files, features, list definitions etc. For example you can open any of the existing site definitions to see how this works. The picture below shows the first rows of the ONET.XML file of the Wiki site definition.

In a WSS feature there are three options for using localized strings from resource files. The default location for the feature's resources is ..\12\TEMPLATE\FEATURES\[FeatureName]\Resources\Resources.[Culture].resx and the strings can be referenced with $ Resources:resource-key, omitting the resource file prefix. Another option is to use the resources in the ..\12\Resources folder, which are available with the full explicit expression form $ Resources:[filename prefix,]resource-key, where the resource file name prefix is specified. The file name prefix can be omitted when the attribute DefaultResourceFile contains a name prefix of a file in ..\12\Resources folder. The attribute RequireResources of the feature element provides additional control over the activation of localized features (see WSS SDK for more info).

Nevertheless the provisioning resource files are not accessible from the ASP.NET web sites. This is where the run-time resources come to play. These files are used in vanilla ASP.NET sites and are located in the web application folder under ..\wss\VirtualDirectories\[port]\App_GlobalResources. They are compiled automatically by the .Net framework in the Resources namespace. These resources are available in assemblies and directly in ASPX pages. To access them from the code use the strongly-typed notation Resources.[file prefix].[resource name]. In ASPX pages use the explicit expression <%$ Resources:[filename prefix,]resource-key %>. The default WSS resource file in this category, used by all WSS sites, is wss.resx.

Deployment of Provisioning Resources

The deployment of provisioning resources can be accomplished using WSS solutions. Unfortunately neither of these deployment facilities support the deployment of run-time resource files directly to the App_GlobalResources folder. In the following samples I'll demonstrate how to access and deploy both types of resource files.

The WSS solution manifest contains two elements with names that imply to have something to do with the deployment of RESX file. They are <ApplicationResourceFiles> and <Resources>. Unfortunately they are not really helpful and basically can be ignored.

To deploy provisioning resources available to all site definitions, features, list definitions etc. use the <RootFiles> element in the WSS solution manifest. The target folder for this element is the 12 hive ..\12\. The files in the example below will be copied to ..\12\Resources\.

To deploy feature specific provisioning resource files, inside your feature's folder, create a sub folder named Resources and rename the resource files to Resources.[language-locale].resx. Remember to omit the file prefix in the string expression ( e.g. $Resources:resource-key; ).

Deployment of Run-Time Resources

Global WSS run-time resources reside in ..\12\CONFIG\Resources. This copy of the files is not used directly. At the time a new web application is created the content of this folder is copied over to ..\[port]\App_GlobalResources. If your organization has one set of localization files used in multiple applications this is the place to put them. You can use the <RootFiles> element in a similar way as described above to deploy such files using WSS solution. However it is very rare that a new web application will be created for a single site or solution. Another option is to use the use stsadm command with the option copyappbincontent to copy the content of ..\12\CONFIG\Resources (among other files and folders) to App_GlobalResources.

None of the above described approaches allows developers to deploy resource files at the time the site or the feature are provisioned. To circumvent the lack of such functionality I hooked up a custom SPJobDefinition to the FeatureActivated event of its event receiver. This is becoming common practice, when it comes to extending the WSS solutions and WSS features. (I got inspired by Vincent Rothwell and his post about extending breadcrumb for pages in _layouts). The job copies the files from the feature's folder to the App_GlobalResources folder of the current web application. Here is how it works.

First let's create a feature to deploy the resource files. The feature defines the event receiver and the assembly.

Next let's create our custom job definition. The constructors look like this:

The first constructor is required and used internally. In the second constructor we simply initialize the source path using the feature's physical path.

The next step is to define the overloaded Execute method, which will copy the files from the feature's folder to the App_GlobalResources.

Once we have the custom job definition, lets hook it up to the events of the feature's event receiver. On the FeatureActivated event we are going to create one new task from our newly defined type and submit it for one-time immediate execution.

As Andrew Connell suggests in this post, before we create the job, we make sure to delete existing instances of the same job type for the current web application. This way we do not end up with competing jobs performing the same action. In a similar train of thought it is nice to clean up the job when the feature is deactivated. So let's add some code to the FeatureDeactivating event.

One future addition to the custom job class will be to delete the deployed resource files, when the feature is deactivated. Since the feature is related to the WSS solution, when the solution gets retracted, the files will be cleaned up nicely as well. The feature can also be extended to be locale aware and deploy only specific resources.

Update 9/21/2007: In this post I write about slightly different way to deploy RESX files using feature stapling, which is a bit simpler to implement.

Click here to download a complete sample project.

Got localized?