Fix broken Managed metadata service and more after CU update

After installing the September CU for SharePoint 2013 Server I have several problems.

The two most annoying things:

  • The Managed Metadata Service went down, giving me no managed navigation and no access to term store.
  • The User Profile Synchronization Service stopped and refused to start.

After trying numerous things (including recreation of the Managed Metadata Service application…) I found this message in the ULS logs:

w3wp.exe (0x1D78) 0x21A0 SharePoint Server Taxonomy ca3r Monitorable Error encountered in background cache check System.Security.SecurityException: Requested registry access is not allowed.     at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)

Googling this error gave me some input! There is a command in psconfig.exe with the purpose of setting correct permissions on SharePoint registry keys.

Running

psconfig -cmd secureresources

resolved all my issues.

Here is the blog post that guided me to the correct solution: http://sharepointologic.blogspot.se/2014/02/managed-metadata-service-not-working.html

Advertisements

Targeted Provisioning of Document Templates with XML

I had a request to provision Document Templates to Document Libraries in SharePoint 2013. The requirement included the ability to relatively easy add or update the Document Templates, as well as target a Document Template to all Site Collections, or only to Site Collections under a specific Managed path.

The solution is based on an XML file, that was added to the CONFIG folder in the 15 hive, template office documents added to the Layouts folder and an Event Receiver that parses the XML and creates appropriate Content Types for the Document Templates.

The XML looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<DocumentTemplates Prefix="Baseic" ParentContentType="0x01010055DCB6CC4FB44A548DEE1113E5000917">
  <DocumentTemplate Target="collaboration">
    <ContentType Name="Collaboration Guideline" Group="Collaboration" Description="Create a Collaboration Guide" />
    <Template Location="/_layouts/15/Baseic.2013/DocumentTemplates/CollabGuide.docx" />
  </DocumentTemplate>
  <DocumentTemplate>
    <ContentType Name="Protocol" Group="Common" Description="Create a protocol" />
    <Template Location="/_layouts/15/Baseic.2013/DocumentTemplates/Protocol.docx" />
  </DocumentTemplate>
</DocumentTemplates>

As you can see, I specify a Parent Content Type that all the created ones will inherit from. I do this because in my scenario all documents in the solution must have a couple of fields added to them. I also specify a Prefix that will be added before the actual Content Type name.

For each Document Template we specify a Content Type and a Template.

  • The Content Type Node contains information about what the Content Type will be called, to which group it will be added and a description.
  • The Template Node specifies the location of the document to add as a template to the new Content Type

This file will be uploaded to a subfolder of the CONFIG directory (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\CONFIG).

The template files will, as you can see in the XML be added to a subfolder of the Layouts folder (C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\LAYOUTS)

Now to the magic: The feature event receiver that makes it all happen!

The feature is Site Scoped, since we will really just want to add our Content Types to the root Web.

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace Baseic.2013.Features.ProvisionDocTemplates
{
    [Guid("9798f1d6-4e4a-407d-8989-ad91e9f04e03")]
    public class ProvisionDocTemplatesEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            // Get Rootweb
            var site = new SPSite((properties.Feature.Parent as SPSite).ID);
            var web = site.OpenWeb();

            // Locate XML file with definitions
            var definitionsFile = SPUtility.GetVersionedGenericSetupPath("CONFIG\\Baseic\\DocTemplatesDefinition.xml", 15);
            
            // Parse file
            XDocument definitionsDocument;
            using (StreamReader oReader = new StreamReader(definitionsFile))
            {
                definitionsDocument = XDocument.Load(oReader);
            };

            var contentTypePrefix = (from c in definitionsDocument.Descendants("DocumentTemplates") select c).First().Attribute("Prefix").Value;
            var parentContentTypeId = (from c in definitionsDocument.Descendants("DocumentTemplates") select c).First().Attribute("ParentContentType").Value;
            var documentTemplates = from c in definitionsDocument.Descendants("DocumentTemplate") select c;

            // Get the parent content type from web
            var parentContentType = web.AvailableContentTypes[new SPContentTypeId(parentContentTypeId)];

            if (parentContentType == null)
            {
                // Logger.WriteError(this.ToString(), "Failed to apply content type: Failed to find parent content type");
                web.Dispose();
                site.Dispose();
                return;
            }

            // Call provisioning method for each CT to deploy 
            foreach (var docTemplate in documentTemplates)
            {
                var target = docTemplate.Attribute("Target");

                // Depending on the Target, provison or not
                if (target == null)
                {
                    CreateContentTypeAndAddDocTemplate(web, contentTypePrefix, parentContentType, docTemplate);
                }
                else if (site.ServerRelativeUrl.Split('/')[1].ToLowerInvariant().Equals(target.Value.ToLowerInvariant())) // the split gives us the managed path of the current sitecollection
                {
                    CreateContentTypeAndAddDocTemplate(web, contentTypePrefix, parentContentType, docTemplate);
                }
            }

            // Make sure to dispose
            web.Update();
            web.Dispose();
            site.Dispose();
        }

        private void CreateContentTypeAndAddDocTemplate(SPWeb web, string contentTypePrefix, SPContentType parentContentType, XElement docTemplate)
        {
            try
            {
                // Parse node data
                var contentTypeNode = (from c in docTemplate.Descendants("ContentType") select c).First();

                var newContentTypeName = string.Concat(contentTypePrefix, " ", contentTypeNode.Attribute("Name").Value);
                var newContentTypeGroup = contentTypeNode.Attribute("Group").Value;
                var newContentTypeDescription = contentTypeNode.Attribute("Description").Value;

                var docTemplateLocation = (from c in docTemplate.Descendants("Template") select c).First().Attribute("Location").Value;

                // Add or update Content Type
                var contentType = web.AvailableContentTypes[newContentTypeName]; 
  
                if (contentType == null) // Does not exist
                {
                    var newContentType = new SPContentType(parentContentType, web.ContentTypes, newContentTypeName);
                    web.ContentTypes.Add(newContentType);

                    newContentType.DocumentTemplate = docTemplateLocation;
                    newContentType.Group = newContentTypeGroup;
                    newContentType.Description = newContentTypeDescription;

                    newContentType.Update(true);
                }
                else // Does exists, update
                {
                    contentType = web.ContentTypes[contentType.Id];

                    contentType.DocumentTemplate = docTemplateLocation;
                    contentType.Group = newContentTypeGroup;
                    contentType.Description = newContentTypeDescription;

                    contentType.Update(true);
                }
            }
            catch (Exception e)
            {
                // Logger.WriteError(this.ToString(), e.Message);
            }
        }
    }
}

So basically, as you can see, it all comes down to using some nice Linq statements to get the correct stuff out of the XML file and then create or update Content Types on the root Web.

You can activate the feature through e.g. the WebTemplate’s ONET.XML or PowerShell.

A heads up though, this code requires that the specified Parent Content Type does already exist on the root Web.

For part two in this blog post I will show you how to bind these new Content Types to a Document Library, using the same XML file!

Best of luck
Robert

The First Post

In this blog I will write about findings, problems and solutions related to SharePoint development