SharePoint 2010 Replaceable Parameter, some observations…
SharePoint Tools for Visual Studio 2010 provides a rudimentary mechanism for replaceable parameters that you can use in files that are not compiled, like ascx files and your project property settings. The basics on this can be found in the documentation at http://msdn.microsoft.com/en-us/library/ee231545.aspx.
There are some quirks however. For example:
My Package name is MacawMastSP2010Templates, as defined in my Package properties:
I want to use the $SharePoint.Package.Name$ replaceable parameter in my feature properties. But this parameter does not work in the “Deployment Path” property, while other parameters work there, while it works in the “Image Url” property. It just does not get expanded. So I had to resort to explicitly naming the first path of the deployment path:
You also see a special property for the “Receiver Class” in the format $SharePoint.Type.<GUID>.FullName$. The documentation gives the following description:The full name of the type matching the GUID in the token. The format of the GUID is lowercase and corresponds to the Guid.ToString(“D”) format (that is, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
Not very clear. After some searching it happened to be the guid as declared in my feature receiver code:
In other properties you see a different set of replaceable parameters:
We use a similar mechanism for replaceable parameter for years in our Macaw Solutions Factory for SharePoint 2007 development, where each replaceable parameter is a PowerShell function. This provides so much more power.
For example in a feature declaration we can say:
- <?xml version="1.0" encoding="utf-8" ?>
- <!-- Template expansion
- [[ProductDependency]] -> Wss3 or Moss2007
- [[FeatureReceiverAssemblySignature]] -> for example: Macaw.Mast.Wss3.Templates.SharePoint.Features, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6e9d15db2e2a0be5
- [[FeatureReceiverClass]] -> for example: Macaw.Mast.Wss3.Templates.SharePoint.Features.SampleFeature.FeatureReceiver.SampleFeatureFeatureReceiver
- -->
- <Feature Id="[[$Feature.SampleFeature.ID]]"
- Title="MAST [[$MastSolutionName]] Sample Feature"
- Description="The MAST [[$MastSolutionName]] Sample Feature, where all possible elements in a feature are showcased"
- Version="1.0.0.0"
- Scope="Site"
- Hidden="FALSE"
- ImageUrl="[[FeatureImage]]"
- ReceiverAssembly="[[FeatureReceiverAssemblySignature]]"
- ReceiverClass="[[FeatureReceiverClass]]"
- xmlns="http://schemas.microsoft.com/sharepoint/">
- <ElementManifests>
- <ElementManifest Location="ExampleCustomActions.xml" />
- <ElementManifest Location="ExampleSiteColumns.xml" />
- <ElementManifest Location="ExampleContentTypes.xml" />
- <ElementManifest Location="ExampleDocLib.xml" />
- <ElementManifest Location="ExampleMasterPages.xml" />
- <!-- Element files -->
- [[GenerateXmlNodesForFiles -path 'ExampleDocLib\*.*' -node 'ElementFile' -attributes @{Location = { RelativePathToExpansionSourceFile -path $_ }}]]
- [[GenerateXmlNodesForFiles -path 'ExampleMasterPages\*.*' -node 'ElementFile' -attributes @{Location = { RelativePathToExpansionSourceFile -path $_ }}]]
- [[GenerateXmlNodesForFiles -path 'Resources\*.resx' -node 'ElementFile' -attributes @{Location = { RelativePathToExpansionSourceFile -path $_ }}]]
- </ElementManifests>
- </Feature>
We have a solution level PowerShell script file named TemplateExpansionConfiguration.ps1 where we declare our variables (starting with a $) and include helper functions:
- # ==============================================================================================
- # NAME: product:\src\Wss3\Templates\TemplateExpansionConfiguration.ps1
- #
- # AUTHOR: Serge van den Oever, Macaw
- # DATE : May 24, 2007
- #
- # COMMENT:
- # Nota bene: define variable and function definitions global to be visible during template expansion.
- #
- # ==============================================================================================
- Set-PSDebug -strict -trace 0 #variables must have value before usage
- $global:ErrorActionPreference = 'Stop' # Stop on errors
- $global:VerbosePreference = 'Continue' # set to SilentlyContinue to get no verbose output
- # Load template expansion utility functions
- . product:\tools\Wss3\MastDeploy\TemplateExpansionUtil.ps1
- # If exists add solution expansion utility functions
- $solutionTemplateExpansionUtilFile = $MastSolutionDir + "\TemplateExpansionUtil.ps1"
- if ((Test-Path -Path $solutionTemplateExpansionUtilFile))
- {
- . $solutionTemplateExpansionUtilFile
- }
- # ==============================================================================================
- # Expected: $Solution.ID; Unique GUID value identifying the solution (DON'T INCLUDE BRACKETS).
- # function: guid:UpperCaseWithoutCurlies -guid '{...}' ensures correct syntax
- $global:Solution = @{
- ID = GuidUpperCaseWithoutCurlies -guid '{d366ced4-0b98-4fa8-b256-c5a35bcbc98b}';
- }
- # DON'T INCLUDE BRACKETS for feature id's!!!
- # function: GuidUpperCaseWithoutCurlies -guid '{...}' ensures correct syntax
- $global:Feature = @{
- SampleFeature = @{
- ID = GuidUpperCaseWithoutCurlies -guid '{35de59f4-0c8e-405e-b760-15234fe6885c}';
- }
- }
- $global:SiteDefinition = @{
- TemplateBlankSite = @{
- ID = '12346';
- }
- }
- # To inherit from this content type add the delimiter (00) and then your own guid
- # ID: <base>00<newguid>
- $global:ContentType = @{
- ExampleContentType = @{
- ID = '0x01008e5e167ba2db4bfeb3810c4a7ff72913';
- }
- }
- # INCLUDE BRACKETS for column id's and make them LOWER CASE!!!
- # function: GuidLowerCaseWithCurlies -guid '{...}' ensures correct syntax
- $global:SiteColumn = @{
- ExampleChoiceField = @{
- ID = GuidLowerCaseWithCurlies -guid '{69d38ce4-2771-43b4-a861-f14247885fe9}';
- };
- ExampleBooleanField = @{
- ID = GuidLowerCaseWithCurlies -guid '{76f794e6-f7bd-490e-a53e-07efdf967169}';
- };
- ExampleDateTimeField = @{
- ID = GuidLowerCaseWithCurlies -guid '{6f176e6e-22d2-453a-8dad-8ab17ac12387}';
- };
- ExampleNumberField = @{
- ID = GuidLowerCaseWithCurlies -guid '{6026947f-f102-436b-abfd-fece49495788}';
- };
- ExampleTextField = @{
- ID = GuidLowerCaseWithCurlies -guid '{23ca1c29-5ef0-4b3d-93cd-0d1d2b6ddbde}';
- };
- ExampleUserField = @{
- ID = GuidLowerCaseWithCurlies -guid '{ee55b9f1-7b7c-4a7e-9892-3e35729bb1a5}';
- };
- ExampleNoteField = @{
- ID = GuidLowerCaseWithCurlies -guid '{f9aa8da3-1f30-48a6-a0af-aa0a643d9ed4}';
- };
- }
This gives so much more possibilities, like for example the elements file expansion where a PowerShell function iterates through a folder and generates the required XML nodes.
I think I will bring back this mechanism, so it can work together with the built-in replaceable parameters, there are hooks to define you custom replacements as described by Waldek in this blog post.