I've got a WebApplication which should contain a publishing site collection and a custom masterpage for that site collection. I wanted to achieve, if this site collection is created during deployment, the custom masterpage should be automatically applied to it. So the following subsites, that will be created after that during deployment, will automatically inherit the masterpage of the site collection on creation.
In the meantime I solved several errors myself - the major ones can be found at the bottom. Currently I've got a new problem:
I can activate the provisioning feature manually and it works without throwing exceptions. But it doesn't activate automatically on sitecollection creation, although the stapler feature at WebApplication level is active...
I tried to create a feature stapling. So I created a feature called PortalMasterPageStapler.feature and set the scope to WebApplication.
After that I created an empty element called PortalMasterStapler and inside the Elements.xml
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<FeatureSiteTemplateAssociation Id="8e0965b6-2128-44d9-bcf6-98fc3b1cac98" TemplateName="CMSPUBLISHING#0" />
</Elements>
I've got a module called PortalMaster (inside a folder "Modules") including the masterpages Portal.master and PortalSystem.master. It also includes an Elements.xml (at the moment it also includes another masterpage, but I wanted to test the feature with just one masterpage, first).
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="PortalMaster" List="116" Url="_catalogs/masterpage" Path="PortalMaster" RootWebOnly ="false">
<File Path="PortalMaster\Portal.master" Url="Portal.master" IgnoreIfAlreadyExists="TRUE" Type="GhostableInLibrary">
<Property Name="UIVersion" Value="4" />
<Property Name="ContentTypeId" Value="0x010105" />
<Property Name="Title" Value="Jasmin Publishing Master Page (Portal)" />
</File>
<File Path="PortalMaster\PortalSystem.master" Url="PortalSystem.master" IgnoreIfAlreadyExists="TRUE" Type="GhostableInLibrary">
<Property Name="UIVersion" Value="4" />
<Property Name="ContentTypeId" Value="0x010105" />
<Property Name="Title" Value="Jasmin Publishing System Master Page (Portal)" />
</File>
</Module>
</Elements>
Then there's the feature PortalMasterProvisioning for the masterpage activation itself with an event receiver (set to scope site):
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
namespace abc.MasterPages.Features.PortalMasterProvisioning
{
/// <summary>
/// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
/// </summary>
/// <remarks>
/// The GUID attached to this class may be used during packaging and should not be modified.
/// </remarks>
[Guid("f6224605-7edf-42cf-9eef-c2a5159fee21")]
public class PortalMasterProvisioningEventReceiver : SPFeatureReceiver
{
// Uncomment the method below to handle the event raised after a feature has been activated.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
using (SPSite site = web.Site)
{
string masterPageUrl = string.Format("{0}/_catalogs/masterpage/Portal.master", site.ServerRelativeUrl.Replace("//", "/"));
web.CustomMasterUrl = masterPageUrl;
web.MasterUrl = masterPageUrl;
web.Update();
}
}
}
catch (Exception ex)
{
string errorMessage = string.Format("Error at activation of feature: {0}", ex.Message);
throw new SPException(errorMessage);
}
}
// Uncomment the method below to handle the event raised before a feature is deactivated.
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
try
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
using (SPSite site = web.Site)
{
string masterPageUrl = string.Format("{0}/_catalogs/masterpage/v4.master", site.ServerRelativeUrl.Replace("//", "/"));
web.CustomMasterUrl = masterPageUrl;
web.MasterUrl = masterPageUrl;
web.Update();
}
}
}
catch (Exception ex)
{
string errorMessage = string.Format("Error at activation of feature: {0}", ex.Message);
throw new SPException(errorMessage);
}
}
}
}
And here's the event receiver to apply the masterpage to the childsites:
using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using System.IO;
using System.Linq;
namespace abc.MasterPages.ChildSiteInit
{
/// <summary>
/// Web Events
/// </summary>
public class ChildSiteInit : SPWebEventReceiver
{
//Search Template IDs
private static readonly Int32[] SearchTemplatesIds = {
(Int32)WebTemplateId.BasicSearchCenter,
(Int32)WebTemplateId.EnterpriseSearchCenter,
(Int32)WebTemplateId.FastSearchCenter
};
/// <summary>
/// A site was provisioned.
/// </summary>
public override void WebProvisioned(SPWebEventProperties properties)
{
SPWeb childSite = properties.Web;
SPWeb topSite = childSite.Site.RootWeb;
//set Search Master
if (SearchTemplatesIds.Contains(childSite.WebTemplateId))
{
childSite.MasterUrl = "/_catalogs/masterpage/Search.master";
childSite.CustomMasterUrl = childSite.MasterUrl;
childSite.AlternateCssUrl = "";
}
else
{
childSite.MasterUrl = topSite.MasterUrl;
childSite.CustomMasterUrl = topSite.CustomMasterUrl;
childSite.AlternateCssUrl = topSite.AlternateCssUrl;
childSite.SiteLogoUrl = topSite.SiteLogoUrl;
childSite.Update();
}
}
}
}
But when I try to deploy the WSP, I get this error:
Install-SPSolution : Failed to create receiver object from assembly "abc.MasterPages, Version=0.0.0.0, C
ulture=neutral, PublicKeyToken=0bc15e792b3fcfaf", class "$SharePoint.Type.f6224605-7edf- 42cf-9eef-c2a5159fee21.FullName
$" for feature "abc.MasterPages_PortalMasterProvisioning" (ID: bd6aee09-9381-4f66-9997- 23bd2a3dfc4c).: S
ystem.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject()
At line:1 char:19
+ Install-SPSolution <<<< -Identity abc.masterpages.wsp -GACDeployment -Local
+ CategoryInfo : InvalidData: (Microsoft.Share...InstallSolution:SPCmdletInstallSolution) [Install-SPSolu
tion], InvalidOperationException
+ FullyQualifiedErrorId : Microsoft.SharePoint.PowerShell.SPCmdletInstallSolution
#### Edit ####
In the meantime I found out, that I have to use the SharePoint Management Shell instead of the normal PowerShell. It seems that this caused the mentioned error. After I used the SP Management Shell I could deploy the WSP without errors. But now I'm facing new problems. After deploying the WSP the feature stapler is active at web application level but if I try to activate the MasterPageProvisioning Feature for the SiteCollection manually, I get this error:
Feature receiver assembly 'abc.MasterPages, Version=0.0.0.0, Culture=neutral, PublicKeyToken=0bc15e792b3fcfaf', class 'abc.MasterPages.Features.PortalMasterProvisioning.PortalMasterProvisioningEventReceiver', method 'FeatureActivated' for feature 'bd6aee09-9381-4f66-9997-23bd2a3dfc4c' threw an exception: Microsoft.SharePoint.SPException: Error
at activation of feature: Object reference not set to an instance of an object.
at abc.MasterPages.Features.PortalMasterProvisioning.PortalMasterProvisioningEventReceiver.FeatureActivated (SPFeatureReceiverProperties properties)
at Microsoft.SharePoint.SPFeature.DoActivationCallout(Boolean fActivate, Boolean fForce)
Sounds like the problem lies inside FeatureActivated of the PortalMasterProvisioning feature, but I can't tell where exactly.
#### Edit 2 ####
Again I could solve the problem myself. I replaced the FeatureActivated Code with this one:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
SPWeb site;
if (properties.Feature.Parent is SPWeb)
{
site = (SPWeb)properties.Feature.Parent;
}
else if (properties.Feature.Parent is SPSite)
{
site = ((SPSite)properties.Feature.Parent).RootWeb;
}
else
{
throw new Exception("Error: Unable to retrieve SPWeb - this feature is not Site or Web-scoped.");
}
string masterPageUrl = string.Format("{0}_catalogs/masterpage/Portal.master", site.ServerRelativeUrl.Replace("//", "/"));
site.CustomMasterUrl = masterPageUrl;
site.MasterUrl = masterPageUrl;
site.Update();
}
catch (Exception ex)
{
string errorMessage = string.Format("Error at activation of feature: {0}", ex.Message);
throw new SPException(errorMessage);
}
}
But it would be boring, if it worked now... So the current problem is, that I can activate the provisioning feature manually and it works without throwing exceptions. But it doesn't activate automatically on sitecollection creation, although the stapler feature at WebApplication level is active...