Meeting workspaces are a discontinued feature in SharePoint 2013. They are still available but are hidden in the webtemp.xml file. In a previous post I described how to ​make meeting workspaces available in SharePoint 2013 by changing the Hidden attribute in this webtemp.xml file in the 15 HIVE. This works fairly well, but it’s always ugly to change files in the SharePoint 15 Hive as we never know if the changes will remain after a patch or update. This is why I tried another approach with some custom site definitions, a simple provisioning provider and a delegate control, which ensures that the correct meeting workspace templates can be selected.

For this I built a simple custom solution which I published on codeplex. The package can be downloaded here.

This is what I did:

Firstly I created a new Site Definition called SapiensMPS in my Visual Studio project. Then I deleted the onet.xml and the default.aspx. The webtemp_.xml file is all I need for this method.

The custom webtemp_.xml file contains the same configurations as the MPS configurations in the webtemp.xml file, with a few exceptions.

I set the Hidden attribute to FALSE I configured a provisioning provider with the original MPS site template name in the provisioning data. E.g. MPS#0, MPS#1, …

<Templates xmlns:ows = "Microsoft SharePoint" > <Template Name = "SapiensMPS" ID = "72742" > <Configuration ID = "0" Title = "Basic Meeting Workspace" Hidden = "FALSE" ImageUrl = "/_layouts/15/images/stmw.png?rev=23" Description = "A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda, meeting attendees, and documents." DisplayCategory = "Meetings" SupportsMultilingualUI = "FALSE" VisibilityFeatureDependency = "01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly = "$SharePoint.Project.AssemblyFullName$" ProvisionClass = "Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData = "MPS#0" > </Configuration > <Configuration ID = "1" Title = "Blank Meeting Workspace" Hidden = "FALSE" ImageUrl = "/_layouts/15/images/stbm.png?rev=23" Description = "A blank meeting site for you to customize based on your requirements." DisplayCategory = "Meetings" SupportsMultilingualUI = "FALSE" VisibilityFeatureDependency = "01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly = "$SharePoint.Project.AssemblyFullName$" ProvisionClass = "Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData = "MPS#1" > </Configuration > <Configuration ID = "2" Title = "Decision Meeting Workspace" Hidden = "FALSE" ImageUrl = "/_layouts/15/images/stdm.png?rev=23" Description = "A site for meetings that track status or make decisions. It provides lists for creating tasks, storing documents, and recording decisions." DisplayCategory = "Meetings" SupportsMultilingualUI = "FALSE" VisibilityFeatureDependency = "01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly = "$SharePoint.Project.AssemblyFullName$" ProvisionClass = "Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData = "MPS#2" > </Configuration > <Configuration ID = "3" Title = "Social Meeting Workspace" Hidden = "FALSE" ImageUrl = "/_layouts/15/images/stsm.png?rev=23" Description = "A site to plan social occasions. It provides lists for tracking attendees, providing directions, and storing pictures of the event." DisplayCategory = "Meetings" SupportsMultilingualUI = "FALSE" VisibilityFeatureDependency = "01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly = "$SharePoint.Project.AssemblyFullName$" ProvisionClass = "Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData = "MPS#3" > </Configuration > <Configuration ID = "4" Title = "Multipage Meeting Workspace" Hidden = "FALSE" ImageUrl = "/_layouts/15/images/stmm.png?rev=23" Description = "A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda and meeting attendees, in addition to two blank pages for you to customize based on your requirements." DisplayCategory = "Meetings" SupportsMultilingualUI = "FALSE" VisibilityFeatureDependency = "01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly = "$SharePoint.Project.AssemblyFullName$" ProvisionClass = "Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData = "MPS#4" > </Configuration > </Template > </Templates > <Templates xmlns:ows="Microsoft SharePoint"> <Template Name="SapiensMPS" ID="72742"> <Configuration ID="0" Title="Basic Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/15/images/stmw.png?rev=23" Description="A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda, meeting attendees, and documents." DisplayCategory="Meetings" SupportsMultilingualUI="FALSE" VisibilityFeatureDependency="01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly="$SharePoint.Project.AssemblyFullName$" ProvisionClass="Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData="MPS#0" ></Configuration> <Configuration ID="1" Title="Blank Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/15/images/stbm.png?rev=23" Description="A blank meeting site for you to customize based on your requirements." DisplayCategory="Meetings" SupportsMultilingualUI="FALSE" VisibilityFeatureDependency="01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly="$SharePoint.Project.AssemblyFullName$" ProvisionClass="Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData="MPS#1"></Configuration> <Configuration ID="2" Title="Decision Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/15/images/stdm.png?rev=23" Description="A site for meetings that track status or make decisions. It provides lists for creating tasks, storing documents, and recording decisions." DisplayCategory="Meetings" SupportsMultilingualUI="FALSE" VisibilityFeatureDependency="01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly="$SharePoint.Project.AssemblyFullName$" ProvisionClass="Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData="MPS#2"></Configuration> <Configuration ID="3" Title="Social Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/15/images/stsm.png?rev=23" Description="A site to plan social occasions. It provides lists for tracking attendees, providing directions, and storing pictures of the event." DisplayCategory="Meetings" SupportsMultilingualUI="FALSE" VisibilityFeatureDependency="01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly="$SharePoint.Project.AssemblyFullName$" ProvisionClass="Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData="MPS#3"></Configuration> <Configuration ID="4" Title="Multipage Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/15/images/stmm.png?rev=23" Description="A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda and meeting attendees, in addition to two blank pages for you to customize based on your requirements." DisplayCategory="Meetings" SupportsMultilingualUI="FALSE" VisibilityFeatureDependency="01dc7b09-3c49-4fb0-89c6-a0f52d965e12" ProvisionAssembly="$SharePoint.Project.AssemblyFullName$" ProvisionClass="Sapiens.at.SharePoint.MPSTemplate.ProvisioningProvider" ProvisionData="MPS#4"></Configuration> </Template> </Templates>

The solution has one webtemp in English (1033) and one in German (1031). If you need it for another language, copy the webtemp to the corresponding language folder and change the title, description and display category.

Then I created a provisioning provider, a handler which gets called when one of my custom site templates is applied. The only thing that the provisioning provider does, is to apply the original MPS template which is configured in the Provisioning Data.

public class ProvisioningProvider : SPWebProvisioningProvider { public override void Provision ( SPWebProvisioningProperties props ) { SPWeb web = props . Web ; //apply SharePoint Meeting Workspace template //the provistioning data contains the name of the site definition. E.g. MPS#0, MPS#1, ... //see webtemp_SapiensMPS.xml web . ApplyWebTemplate ( props . Data ) ; } } public class ProvisioningProvider:SPWebProvisioningProvider { public override void Provision(SPWebProvisioningProperties props) { SPWeb web = props.Web; //apply SharePoint Meeting Workspace template //the provistioning data contains the name of the site definition. E.g. MPS#0, MPS#1, ... //see webtemp_SapiensMPS.xml web.ApplyWebTemplate(props.Data); } }

The only thing remaining is something to ensure that my templates can be selected instead of the SharePoint Meeting workspace template (that are hidden anyways). For this I decided to use a delegate control that is registered in the AdditionalPageHead and is loaded on every page in the site collection where the feature is activated. After a calendar event is created or updated and the “Workspace” option is activated, the user is redirected to a page (newMWS.aspx) where the title, description, path, permissions and the language of the new meeting workspace can be configured. After clicking OK the next page is the Template Selection (templatepick.aspx). In the query string there is a parameter ID set to 2 (ID=2). 2 is the ID of the SharePoint Meeting Workspace templates. In this case, in SharePoint 2013, no template can be selected, because they are all hidden. What I did in the delegate control is to check the URL and if it’s the template picker with the ID set to 2 I redirect the user to the same template picker with the ID set to 72742, my meeting workspace templates.

public class MPSRedirect : UserControl { protected override void OnInit ( EventArgs e ) { if ( //template picker Page . Request . Path . ToLower ( ) . EndsWith ( "/_layouts/15/templatepick.aspx" ) && //2=> MPS Templates Page . Request . QueryString [ "ID" ] == "2" ) { //Redirect to 72742 (SapiensMPS) templates, see webtemp_SapiensMPS.xml Page . Response . Redirect ( Page . Request . RawUrl . Substring ( 0 , Page . Request . RawUrl . IndexOf ( "?" ) ) + "?" + string . Join ( "&" , Page . Request . QueryString . AllKeys . Select ( //change ID to 72742, the rest stays the same k => k + "=" + ( k . ToLower ( ) == "id" ? "72742" : Page . Request . QueryString [ k ] ) ) . ToArray ( ) ) ) ; } base . OnInit ( e ) ; } } public class MPSRedirect : UserControl { protected override void OnInit(EventArgs e) { if (//template picker Page.Request.Path.ToLower().EndsWith("/_layouts/15/templatepick.aspx") && //2=> MPS Templates Page.Request.QueryString["ID"] == "2") { //Redirect to 72742 (SapiensMPS) templates, see webtemp_SapiensMPS.xml Page.Response.Redirect(Page.Request.RawUrl.Substring(0, Page.Request.RawUrl.IndexOf("?")) + "?" + string.Join("&", Page.Request.QueryString.AllKeys.Select( //change ID to 72742, the rest stays the same k => k + "=" + (k.ToLower() == "id" ? "72742" : Page.Request.QueryString[k]) ).ToArray())); } base.OnInit(e); } }

What’s also included in the solution is a custom calendar list template with the “Meeting Workspace” field visible. In the SharePoint 2013 calendar template the required fields are defined in the schema.xml, but are set to hidden and it’s not possible to change the hidden attribute in SharePoint Manager or the SharePoint Management shell. This is why I decided to also include my own calendar template.

If the solution is installed and you activate the feature “Sapiens.at.SharePoint Meeting Workspaces” in the site collection features, a new list template named “Calendar with meeting workspaces” should be available. If you then create an event in a “Calendar with meeting workspace” list you will be able to create a meeting workspace just like in SharePoint 2010.

This solution is also included in the Employee Training Management and the Calendar E-Mail Extension. In the Employee Training Management you can use Meeting Workspaces to, for example, publish documents and other training material. Using the Calendar E-Mail Extension it is even possible to create meeting workspaces automatically if you send a meeting request to the calendar. This can be configured in the calendar settings.

Get the source code on codeplex

Download the package

Please note that this also works with SharePoint 2016.

I changed the ID of the web template to 2. This is also the id of the SharePoint MPS templates. This allows you to modify meeting workspace sites and save them as custom template. Because it’s the same id than the SharePoint Meeting Workspace template, we don’t need the delegate control to redirect to the other id anymore.

If you want to upgrade, download the latest wsp and run the following command in the SharePoint Management shell:

Update - SPSolution - Identity Sapiens . at . SharePoint . MPSTemplate . wsp - LiteralPath c : \ < deploymentpath > \Sapiens . at . SharePoint . MPSTemplate . wsp - GACDeployment Update-SPSolution -Identity Sapiens.at.SharePoint.MPSTemplate.wsp -LiteralPath c:\<deploymentpath>\Sapiens.at.SharePoint.MPSTemplate.wsp -GACDeployment

If you want to make the Workspace field visible on an existing SharePoint 2013 calendar, here is how it works:

Make the workspace field visible in an existing SharePoint 2013 calendar

When using languages other than English and German, it was not possible to see the meeting workspace templates when creating a meeting workspace. This has now been dealt with in a new blog post.

Workspace templates missing