Thursday, July 24, 2014

Getting Values from SharePoint 2013 Property Bags

SharePoint 2013 has a hierarchical set of property bags. At each of the following levels in a SharePoint farm, one can store properties and their corresponding values:
  • Farm
  • Web Application
  • Site
  • Web
  • List
This can be extremely handy when writing code. For example, if you have four different environments (Development, QA, Staging, Production), you could store properties in each  environment's farm property bag that specifies things like connection strings, debugging constants, or host URLs.

It's even handier when you have a simple means to extract these properties from the property bag in your code. I generally make a class (FarmProperty in the example below) with a "Get" method that extracts the string value for the given property.


    public class FarmProperty
    {
        public static string Get(string name)
        {
            string value = null;
            SPSecurity.RunWithElevatedPrivileges(() =>
            {
                var farm = SPFarm.Local;
                foreach (var prop in farm.Properties.Cast<DictionaryEntry>().Where(prop => prop.Key.Equals(name)))
                {
                    value = prop.Value.ToString();
                }
            });
            return value;
        }
    }


To use this code, just call the Get method on the FarmProperty class. If the property doesn't exist, it will return null.


    var reportServerHost = FarmProperty.Get("ReportServerHost");



Wednesday, June 11, 2014

How to configure People Picker in SharePoint 2013 for a remote domain with a one-way trust

I came across a situation today that required use of the STSADM command on SharePoint 2013 (Yes, it still lives)...

If SharePoint is installed on a domain that has a one-way trust with other domains and you want the People Picker on SharePoint 2013 to show users and groups from those other domains, you have to use the STSADM command to configure the People Picker. Basically, the People Picker needs an identity that it can use to authenticate against the remote trusted domains to query them.

Step 1 

First, you have to set an encryption key on all web servers in your farm so that the stored credentials can be encrypted:

Just to be clear, you have to run this on all web servers in your farm and the "AnyKey" value has to be set to the same value on all of those servers.

STSADM.exe -o setapppassword -password AnyKey

where "AnyKey" is any value. For example,

STSADM.exe -o setapppassword -password B1gL0ngPassw0rd

Step 2

Next, you have to give SharePoint the credentials to authenticate against the remote domain and tell it which domain you would like to authenticate against. You only have to run this on one of your SharePoint web servers

STSADM.exe -o setproperty -propertyname peoplepicker-searchadforests -propertyvalue <Valid list of domains> -url <URL of the Web application>

where <Valid list of domains> is in the following format:

domain:DnsName,LoginName,Password

For example, a sample command might look like this:

STSADM.exe -o setproperty -propertyname peoplepicker-searchadforests -propertyvalue "domain:microsoft.com,microsoft\UserX,Ne@tP@ssw0rd" -url https://intranet.doughemminger.net

You can add more than one domain. Just delimit them with a semicolon and a space. For example:

STSADM.exe -o setproperty -propertyname peoplepicker-searchadforests -propertyvalue "domain:microsoft.com,microsoft\UserX,Ne@tP@ssw0rd; domain:bing.com,bing\UserY,L@meP@ssw0rd" -url https://intranet.doughemminger.net

Other Notes

There are lots of other things you can do with the People Picker control. For example:
  • You can run a custom query to search active directory and only display those users or groups.
  • You can also tell the people picker control to restrict to users in the site collection.
  • You are not restricted to remote domains. You can run the same commands I showed above for forests.

References

There is actually good documentation on this here:
http://technet.microsoft.com/en-us/library/gg602075(v=office.15).aspx

Friday, May 30, 2014

Nintex User Defined Action to get Emails from a SharePoint Group


Intro

Nintex has a neat little thing in SharePoint 2013 called a User Defined Action (UDA). It takes in inputs, does some stuff, and then spits out outputs. It's a handy way to create an action that you can reuse within your workflows.

In this guide, I walk you through how to create a UDA for grabbing e-mail addresses from a SharePoint group. Let's get started.

Build


You will start by creating a new User Defined Action.
  1. On your site collection administration page, under the "Nintex Workflow" Heading, select Managed User Defined Actions


Then Select "Create" from the "Manage User Defined Actions" tab on the ribbon.


Then create one input parameter and one output parameter. From the ribbon, select "Parameters" from the "UDA Settings" button on the "Manager User Defined Actions" tab on the ribbon. It looks like this:


Add the following parameters:
  1. Input: GroupName, Text
  2. Output: UserEmailCollection, Output

Then add two Actions:
  1. Call Web Service
  2. Query XML

Configure "Call Web Service" action as follows:

  1. URL is the location of the UserGroup.asmx web service. I use web URL here, but you can use a site collection URL as well.
  2. Workflow Credentials for the web service. In this case, I use a variable that stores the credentials.
  3. Hit refresh and the drop down will populate with a list of methods. Choose "GetUserCollectionFromGroup"
  4. You can choose either Editor mode, but, in this case, "SOAP builder" makes it easier
  5. "groupName (string)" is the input parameter to the Web Service. In this case, we are going to pass the UDA parameter "GroupName" (we created this in a previous step) to the web service parameter.
  6. Select "Specify elements"
  7. When you select "Specify elements" you will be presented with a dialog. Make sure expand the nodes until you get to GetUserCollectionFromGroupResult. Then select it.
  8. Output your result to a workflow variable. In this case, I created one called, "UserXML"


Configure the "Query XML" action as follows

  1. Select your "XML" as your "XML source"
  2. Select "Insert Reference" and then put in your Workflow Variable that was output from the previous step. In this case, "UsersXML"
  3. Process using "XPath"
  4. Add the following XPath (without the quotes): "/defaultNS:GetUserCollectionFromGroup/defaultNS:Users/defaultNS:User/@Email"
  5. Return results as "Text"
  6. Store the result in the output parameter that you created earlier. In this case, "UserEmailCollection"


Don't forget to publish it!

Tuesday, January 7, 2014

Programmatically provisioning Term Sets, Terms, and Managed Metadata columns in SharePoint 2013

In this post I explain how to provision Term Sets, Terms, and Managed Metadata columns in SharePoint 2013 using C#.

Caveats

  • This is for SharePoint 2013 full trust solutions where the Managed Metadata Service is provisioned
  • The Terms and Term Sets  are provisioned in the default site collection term store, but they could just as easily be provisioned in another term store with the appropriate assignment of privileges.
  • For the purposes of this post, I created a site scoped feature and provision all of the terms in a feature receiver. This is not meant to be an explanation of feature receivers so I don't cover a lot of the details of how to setup a feature receiver.


Provisioning SharePoint 2013 Term Sets in Code


In order to provision term sets in code, you first need to get a reference to the term store. In the example below, I use a feature receiver to provision the term set and associated terms. The feature is a site scoped feature and I use the default site collection term store. When activated on a site collection, it will run the code below to provision the terms.

public class MyFeatureReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {  
        //Get a reference to the current site
        var site = properties.Feature.Parent as SPSite;

        //Get the Default Site Collection Term Store
        var taxonomySession = new TaxonomySession(site);
        var termStore = taxonomySession.DefaultSiteCollectionTermStore;
        var termStoreGroup = termStore.GetSiteCollectionGroup(site);

        //Create Department Term Set with associate Terms
        var termSet = termStoreGroup.CreateTermSet("Department");
        termSet.CreateTerm("Finance", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Accounting", CultureInfo.CurrentCulture.LCID);
        var humanResources = termSet.CreateTerm("Human Resources", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Recruiting", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Benefits", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Compensation", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Payroll", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Operations", CultureInfo.CurrentCulture.LCID);

        termStore.CommitAll();
    }
}

Note that I assign the Human Resources term to a variable ("humanResources") because I need to create child terms. Once I create "humanResources", I can create child terms by using the CreateTerm method on the newly created "humanResources" variable.

This is what the site collection term store looks like once the solution has been deployed and the feature has been activated:


 

Provisioning a Managed Metadata Site Column

In order to provision a managed metadata site column that uses the Human Resources term as an anchor, I just add a little code below "termStore.CommitAll();". Here is the full feature receiver code with the added managed metadata site column linked to the new Human Resources term.

public class MyFeatureReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {  
        //Get a reference to the current site
        var site = properties.Feature.Parent as SPSite;

        //Get the Default Site Collection Term Store
        var taxonomySession = new TaxonomySession(site);
        var termStore = taxonomySession.DefaultSiteCollectionTermStore;
        var termStoreGroup = termStore.GetSiteCollectionGroup(site);

        //Create Department Term Set with associate Terms
        var termSet = termStoreGroup.CreateTermSet("Department");
        termSet.CreateTerm("Finance", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Accounting", CultureInfo.CurrentCulture.LCID);
        var humanResources = termSet.CreateTerm("Human Resources", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Recruiting", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Benefits", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Compensation", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Payroll", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Operations", CultureInfo.CurrentCulture.LCID);

        termStore.CommitAll();

        var web = site.RootWeb;
        if (!web.Fields.ContainsFieldWithStaticName("HumanResourcesDepartment"))
        {
             var humanResourcesDepartment = web.Fields.CreateNewField("TaxonomyFieldType", "HumanResourcesDepartment") as TaxonomyField;
             if (humanResourcesDepartment != null)
             {
                 humanResourcesDepartment.StaticName = "HumanResourcesDepartment";
                 humanResourcesDepartment.Title = "Human Resources Department";
                 humanResourcesDepartment.SspId = termStore.Id;
                 humanResourcesDepartment.TermSetId = termSet.Id
                 humanResourcesDepartment.TargetTemplate = string.Empty;
                 humanResourcesDepartment.AnchorId = humanResources.Id;
                 humanResourcesDepartment.Group = "Custom Columns";
                 humanResourcesDepartment.AllowMultipleValues = true;
                 humanResourcesDepartment.Open = false;
             }
             web.Fields.Add(humanResourcesDepartment);
             web.Update();
        }
    }
}

Note that since I have the Human Resources term saved in the humanResources variable, I am able to grab it's id to set the anchor point for my managed metadata column.

Here's what the site column looks like after the feature has been deployed and activated:





Notice how the Term Set Settings are anchored to the Human Resources term in the Department Term Set. Here's how the properties on the new managed metadata field should be set:
  • The SspId should be set to the id of the Term Store to use. In this case I happen to have only one Managed Metadata Service and one Term Store to use. In the code above, I just grab the id from my termStore variable.
  • The TermSetId should be set to the id of the Term Set to use. In this case, my Term Set is "Department". In the code above, I just grab the id from my termSet vaiable..
  • The AnchorId should be set to the id of the term that you would like to anchor on. In a lot of cases, this may be set to null if you want to anchor on the Term Set itself. However, in this case, I want to create a column that allows me to select a department within Human Resources. So, I am going to anchor on the "Human Resources" Term. In the code above, I just grab the id from my humanResources variable.

Enjoy!