Thursday, December 12, 2013

SharePoint Web Properties

I like to make use of web properties to make my solutions more flexible and configurable. For instance, a client of mine likes to request automated emails from SharePoint. But experience has taught me that these emails can get overwhelming and it’s not uncommon for a customer to come back later and request that I stop the emails or reduce their frequency. If the sending and frequency of emails is hard coded into a deployed solution, then the process to change the emails involves a code change and a redeployment which takes time and always has an element of risk to it. Therefore, in cases like these I will code my solution to read web properties to drive these behaviors.

For example, I might create a web property named “Customer_SendEmails_OnOff” which can have a value of “On” or “Off”, and another one named “Customer_Email_FrequencyHours” which would have a value of the number of hours between emails, like “6”. My deployed solution reads in these property values and behaves accordingly.  If it reads the “Customer_SendEmail_OnOff” property and the value is “Off” then it sends no emails, if it is “On” then it reads the “Customer_Email_FrequencyHours” property value to determine the minimum number of hours between emails.  This way, I can use a few PowerShell commands to alter these property values and correspondingly change the behavior of my deployed solution immediately.

Here are some useful code samples related to this process:

PowerShell (add or update a web property)
# Make sure the SharePoint namespace is loaded
Add-pssnapin Microsoft.SharePoint.PowerShell –erroraction silentlycontinue;

# Set some sample variables
$yourservername = "contoso.local";
$yoursitepath = "sites/Accounting/Audits";
$yourpropname = "Customer_Email_OnOff";
$yourpropvalue = "On";

# Get an SPWeb object
$web = get-spweb ("http://{0}/{1}" –f $yourservername, $yoursitepath);

# Get a hashtable of all the properties on the SPWeb
$props = $web.AllProperties;

# Add or update the property
# NOTE: Add "" to ensure PowerShell interprets value as a string
if ($props.ContainsKey($yourpropname)
{
    $props[$yourpropname] = "" + $yourpropvalue;
}
else
{
    $props.Add($yourpropname, "" + $yourpropvalue);
}

# Commit updates to the SPWeb
$web.update();

# Release SPWeb from memory
$web.dispose();

PowerShell (iterate all existing properties)
# Make sure the SharePoint namespace is loaded
Add-pssnapin Microsoft.SharePoint.PowerShell –erroraction silentlycontinue;

# Set some sample variables
$yourservername = "contoso.local";
$yoursitepath = "sites/Accounting/Audits";

# Get an SPWeb object
$web = get-spweb ("http://{0}/{1}" –f $yourservername, $yoursitepath);

# Get a hashtable of all the properties on the SPWeb
$props = $web.AllProperties;

# Iterate the property names and values
foreach ($p in $props.Keys)
{
    write-host –f green "PropertyName: " -nonewline;
    write-host –f yellow $p;
    write-host –f green "       Value: " -nonewline;
    write-host –f yellow $props[$p];
    write-host;
}

# Release SPWeb from memory
$web.dispose();

PowerShell (cleanup/delete selected existing properties, full script)
param(
    [Parameter(Mandatory=$true,HelpMessage='Target subweb for properties (e.g. http://contoso.local/sites/accounting/audits)')][System.String]$weburl)

# Get our web object
$web = get-spweb $weburl;

# Create an empty array to hold the keys to be deleted
$deleteKeys = @();

# Iterate through each property
$props = [System.Collections.HashTable]$web.allproperties;
foreach ($p in $props.keys)
{
    write-host -f green " PropertyName: " -nonewline;
    write-host -f yellow $p -nonewline;
    write-host -f red " <- Delete? " -nonewline;
   
    # Read key input until "Y" or "N" pressed
    do {
        $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");
    }
    until (($key.Character -eq "Y") -or ($key.Character -eq "N"))
       
    # Echo the key pressed
    write-host -f White $key.Character;
       
    # Since we cannot update a collection we are iterating through,
    # add the keys to delete into our array
    if ($key.Character -eq "Y")
    {
        $deleteKeys += $p;
    }

}

# Delete the property keys that were marked for deletion
write-host;
foreach ($propkey in $deleteKeys)
{
    $web.allproperties.remove($propkey);
    write-host -f red "      Deleted: " -nonewline;
    write-host -f yellow $propkey;
}
write-host;

# Commit our changes
$web.update();

# Release the SPWeb object from memory
$web.dispose();

C# (read a property value)
// Set some sample variables
string yourservername = "contoso.local";
string yoursitepath = "sites/Accounting/Audits";
string yourpropname = "Customer_Email_OnOff";
string yourpropvalue = string.Empty();
object property = null;

// Access the site object
using (SPSite site = new SPSite(string.Format("http://{0}/{1}", yourservername, yoursitepath)))
{

    // Access the web object
    using (SPWeb web = site.OpenWeb())
    {

        // Attempt to get the property
        property = web.AllProperties[yourpropname];

        // Check for null
        if (property != null)
        {

            // Populate your variable with the value that was read
            yourpropvalue = property.ToString();

        }
        else
        {

            // Handle error
            throw new ArgumentNullException(yourpropname, "Property returned null.");

        }

   } // end using web

} // end using site


Tuesday, December 10, 2013

Cancel SharePoint 2010 Deployment

Sometimes I get a SharePoint 2010 deployment job stuck deploying. When I attempt to re-deploy I get message indicating that another deployment job cannot be started while one is running. The only way I have found to resolve this is to use stsadm (located in 14\bin folder) to;
   
   a)   List current deployment jobs and their Ids
   
   b)   Cancel a running deployment job by Id

stsadm
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN>stsadm -o enumdeployments

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN>stsadm -o canceldeployment –id <the id to cancel>




Monday, December 2, 2013

SharePoint Managed Metadata Fields

The steps and rules for getting and setting Managed Metadata fields in SharePoint are not so straightforward. I created this post to help me remember what I have figured out and what works for me. I would like to give a shout out to Matthew Yarlett and his excellent post on various SharePoint field types with PowerShell. I have bookmarked his post for future reference as he covers all field types and some variations on Managed Metadata that I have not covered here.

C# (Console Application)
// <>Partial namespace listing
using Microsoft.SharePoint.Taxonomy; // Must add this reference

// Set some sample variables
string yourservername = "contoso.local";
string yoursitepath = "sites/Accounting/Audits";
string yourlistname = "Contracts";
int youritemid = 34;
string yourtaxfieldcolname = "Client";
string yourtaxsessionname = "Managed Metadata Service";
string yourtaxgroupname = "Accounting Term Store";
string yourtaxsetname = "Client Term Set";
string yournewclient = "Ford";

// <>Code omitted, insert code into Main

// Access the site object
using (SPSite site = new SPSite(string.Format("http://{0}/{1}", yourservername, yoursitepath)))
{

    // Access the web object
    using (SPWeb web = site.OpenWeb())
    {

        // Get taxonomy session variables
        TaxonomySession session = new TaxonomySession(site);
        var store = session.TermStores[yourtaxsessionname];
        var group = store.Groups[yourtaxgroupname];
        var set = group.TermSets[yourtaxsetname];

        // To get all the clients from the term set
        foreach(var term in set.Terms)
        {
            Console.WriteLine(string.Format(" – Term: {0}", term.Name));
        }
        Console.WriteLine();

        // Get a reference to a listitem
        SPList list = web.Lists.TryGetList(yourlistname);
        SPListItem item = list.GetItemById(youritemid);

        // Get the current taxonomy field value
        var taxFieldValuePrev = item[yourtaxfieldcolname] as TaxonomyFieldValue;
        Console.WriteLine(string.Format(" Previous: {0}", taxFieldValuePrev.Label));

        // Update the taxonomy field value to the new value
        var term = set.Terms[yournewclient];
        var taxField = item.Fields[yourtaxfieldcolname] as TaxonomyField;
        taxField.SetFieldValue(item, term);
        item.Update();

        // Get the updated taxonomy field value
        var taxFieldValueNew = item[yourtaxfieldcolname] as TaxonomyFieldValue;
        Console.WriteLine(string.Format(" New: {0}", taxFieldValueNew.Label));

   } // end using web

} // end using site

// Let the user terminate the program
Console.Write("Press return to quit... ");
Console.ReadLine();