Iteration 10 is complete and the drop can be downloaded here.
In iteration 10 we mainly concentrated on refactoring, code cleanup and reusable assets. We will continue refactoring and scrubbing the code in the remaining iterations but please take a look at what we've done so far and provide us feedback.
- Hierarchical configuration: the ability to set a value on an element in the site map and have that value accessible by child elements in the site map. (ConfigManager.cs)
- Global Navigation driven by config (HierarchicalConfigSiteMapProvider.cs)
- Generic Service locator (ServiceLocator.cs)
- Applied V1 theme and use HierarchicalConfigSiteMapProvider in global navigation
- Subsite creation workflow activities (Microsoft.Practices.SPG.SubSiteCreation.Workflow project)
Not to be confused with Hierarchical Object Store which uses SPPersistedObject to build a hierarchy of elements to store configuration data, our notion of “Hierarchical Configuration” describes the ability to retrieve a configuration setting and not know where in the hierarchy that information was persisted. The information could be set at the SPFarm and therefore be available to all code running in that scope. Same goes for the SPWebApplication, SPSite, and SPWeb.
We also support the ability to over-ride a value set higher in the hierarchy. For example, a config setting could be persisted in the SPWebApplication, thus making it available to all SPSites and SPWebs underneath it. However, you could also persist a setting using the same key in an SPSite or SPWeb.
We implemented “Hierarchical Configuration” with Microsoft.Practices.SPG.Common.Configuration.ConfigManager and the various implementations of IPropertyBag (SPFarmPropertyBag, SPWebAppPropertyBag, SPSitePropertyBag, and SPWebPropertyBag).
For an example of how we utilize Hierarchical Configuration, take a look at the Contoso.PSS.GlobalNavigation project. In WebAppFeatureReceiver we use ConfigManager to persist the sitemap xml into the SPWebApplication.
configManager.SetByKey("Microsoft.Practices.SPG.Common.Navigation.NavigationXml", Resources.DefaultSiteMapXml, webApp);
This value is retrieved by the HierarchicalConfigSiteMapProvider:
string siteMapXml = configManager.GetByKey<string>("Microsoft.Practices.SPG.Common.Navigation.NavigationXml");
I’ll go into more detail on the HierarchicalConfigSiteMapProvider later, but you can see from this example that a config setting is persisted in the WebApp and then retrieved by our business logic. The ConfigManager.GetByKey method starts at the current SPWeb and queries the property bag with the key. If the key is not found, the SPWeb’s parent SPSite is queried. If the key is not found in the SPSite, the parent SPWebApplication is queried, and so forth up to the SPFarm.
Global Navigation through HierarchicalConfigSiteMapProvider
In a previous iteration, we implemented global navigation through deploying modifications to layouts.sitemap and merging those modifications by calling SPWebService.ApplyApplicationContentToLocalServer() or the corresponding STSADM command copyappbincontent. This approach mainly demonstrated what could be done out of the box to configure navigation in SharePoint. We then used the out-of-the-box XmlSiteMapProvider which is typically already configured in web.config to read from layouts.sitemap. The downside to this approach was that stsadm –o copyappbincontent or SPWebService.ApplyApplicationContentToLocalServer() would need to be executed on every web front end in the farm to update all layouts.sitemap files. In order to move to an approach that is easier to deploy across a farm, we went with a custom site map provider.
The HierarchicalConfigSiteMapProvider extends StaticSiteMapProvider and loads site map nodes on initialization, thus this custom site map provider is appropriate for cases where navigation changes very infrequently.
As mentioned above, the HierarchicalConfigSiteMapProvider leverages Hierarchical Configuration and retrieves sitemap xml from the ConfigManager using the key: “Microsoft.Practices.SPG.Common.Navigation.NavigationXml“. When first initialized, the HierarchicalConfigSiteMapProvider creates a sitemap structure based on the sitemap xml retrieved from the ConfigManager.
The Contoso.PSS.GlobalNavigation WebAppFeatureReceiver adds the appropriate nodes to web.config, saves Contoso PartnerPortal specific sitemap xml to the SPWebApplication, and adds a delegate control that will get picked up by default.master.
Generic Service Locator
In SPG v1, the ServiceLocator could self initialize by knowing about Contoso services that would be necessary for the application to run. In this drop, we’ve made the ServiceLocator much more generic. Now, feature receivers can register their own service implementations, adding to or potentially over-writing previous service registrations.
There are many examples throughout the Contoso.PSS reference implementation for how to use the ServiceLocator to get an instance of a service. This functionality has changed little since SPG v1. However take a look at the Contoso.PSS.Portal project’s WebFeatureReceiver FeatureInstalled method for an example of how this feature is registering type mappings using ServiceLocatorConfig.
Applying V1 Theme
In a previous iteration, we implemented a custom look and feel via custom masterpage and CSS. We decided to use the ContosoTheme feature we developed in SPG v1 (Contoso.RI.UpdateTheme) instead of the custom masterpage and CSS. One advantage of using a theme is that system and application pages can also be branded.
SubSite Creation Workflow Activities
In a previous iteration, we implemented the subsite creation process but realized that the process of creating subsites may need to be customized or extended. This iteration, we refactored the subsite creation process into several workflow activities (Microsoft.Practices.SPG.SubSiteCreation.Workflow). We also provide a workflow that stitches these workflow activities together (SubSiteCreation.cs). The workflow activities are:
- ResolveSiteTemplate: This activity determines the appropriate site template to use when the sub site is created.
- CreateSubSite: This activity creates the top level or root site based on the business event type (e.g. /Incidents or /OrderExceptions). This activity then uses the site template resolved previously and creates a site based on this site template and locates the subsite under the top level site. This activity then sets an identifier on the site that relates the subsite to the business event and value (e.g. IncidentId=123 or OrderExceptionId=321).
- SynchronizeStatus: This activity sets the status of the business event onto the newly created subsite.
The Microsoft.Practices.SPG.SubSiteCreation SharePoint solution project packages the above worflow and activities into features.