Tuesday, November 27, 2012

Programmatically obtain an SPUser object from a SharePoint "Person or Group" field

Within a document library that's contained in our custom Team Site template, we have defined a custom content type (i.e. called Enterprise Document) in which we've specified a "Person or Group" field called "Owner" to store the person who has overall responsibility of the document and may be different from the person who created or last modified the document. The value of this field is set by the user who uploads the document to the library. Naturally, I would like to put this field to use and obtain information about the user that is defined in this field in one of our custom workflows.  In this sample case, I'm going to obtain the owner's email address:

SPList docLibrary = web.Lists["Enterprise Documents"];
SPQuery query = new SPQuery();
query.Query = "<where><eq><fieldref name="ContentType"><value type="Text">Enterpise Document</value></fieldref></eq></where>";
SPListItemCollection documents = docLibrary.GetItems(query);
foreach (SPListItem doc in documents)
{
    if (doc.Fields["Owner"] != null)
    {
        SPFieldUser ownerField = doc.Fields.GetField("Owner") as SPFieldUser;
        SPFieldUserValue ownerValue = ownerField.GetFieldValue(doc[ownerField.Id].ToString()) as SPFieldUserValue;
        SPUser owner = ownerValue.User;
        string ownersEmail = owner.Email;
        ...

Because we have multiple content types in this library, I added code to make certain that the content type of the document under review is based on our custom "Enterprise Document" content type since only that content type contains the "Owner" field mentioned above.  Anyway, I thought this might be useful to share since it took me several iterations before I figured it out.

Monday, November 26, 2012

Adding configuration settings to a custom SharePoint timer job

If you wish to add custom configuration settings to a custom SharePoint, web application scoped timer job, one possible option is to add those configuration settings into the <appSettings> node of the web.config file of the applicable SharePoint web application(s) and then reference those settings from your custom SharePoint timer job's code.

Your first step is to add your configuration settings into the <appSettings> node of your SharePoint web application.  The custom configuration settings can be added to your web.config file via the following steps:
  1. Log on to your SharePoint WFE server(s)
  2. Open Windows Explorer
  3. Navigate to the C:\inetpub\wwwroot\wss\VirtualDirectories folder
  4. Locate the applicable SharePoint web application listed here and open on that folder
  5. Right-click on the web.config file and select Open with -> Notepad
  6. Scroll down until you find the <appSettings> node
  7. Add a new element within the <appSettings> node to define the key and value of your configuration setting (i.e. "<add key="hoursToExpiration" value="10200" />")
  8. Click File -> Save
  9. Repeat steps 1 - 8 for all web applications that your timer job is activated on
Your next step is to add code to your timer job's class file, that will reference your custom <applicationSettings> elements.  The following code sample provides one possible option for obtaining these settings using my sample "hoursToExpiration" setting:

...
using System.Configuration;
using System.Web.Configuration;
...
public override void Execute(Guid contentDbId)
{
    int hoursToExpiration = 0;
    Configuration config = WebConfigurationManager.OpenWebConfiguration("/", this.WebApplication.Name);
    if (config.AppSettings.Settings["hoursToExpiration"] != null)
    {
        if (!Int32.TryParse(config.AppSettings.Settings["hoursToExpiration"].Value, out hoursToExpiration))
        {
            // Write code to handle that the custom appSetting couldn't be converted to the appropriate data type
        }
    }
    else
    {
        // Write code to handle that the custom appSetting couldn't be found
        ...

Once you've deployed the code update, be certain to cycle the SharePoint timer service to make certain that the change has been picked up (see my previous blog entry for instruction on how to do that).  At that point, your timer job will obtain the custom setting information upon execution of the timer job on that web application.

Thursday, November 22, 2012

Custom SharePoint timer job code doesn't seem to be updating

If you're running into a situation in which your making code updates to a custom SharePoint timer job and the code doesn't seem to be updating, you may want to try restarting the OWS Timer service to see if that helps resolve your issue.  To restart the OWS Timer service on SharePoint 2010, you can perform the following steps:
  1. Open the SharePoint 2010 Management Shell as an administrator 
  2. Enter “net stop SPTimerV4” and press enter. (The service will be stopped)
  3. Enter “net start SPTimerV4” and press enter. (The service will be started again)
In my particular example, I had just deployed a code update to one of the custom timer jobs on my SharePoint farm and, upon attaching the debugger to the OWSTimer, noticed that the debugger was simply skipping over the new lines of code that I had just added.  Anyway, refreshing the OWS Timer service made certain that the latest code I had just deployed was refreshed.  As a result, the debugger immediately started picking up the new lines associated with the code that I had previously deployed.

Wednesday, November 21, 2012

Adding a description to a custom SharePoint timer job

If you've ever dug through a bunch of SharePoint timer jobs installed on a SharePoint farm wondering what the purpose of a given job is as well as why the Description field always seems to be blank for the custom timer jobs, I feel your pain and would like to offer you a potential solution to this problem.


The most likely reason that the Description field is blank for custom timer jobs is because the property associated with it is read-only. To make a long story short, the property only has a "get", but no "set" operation; therefore, the developer of the custom timer job can't set the text of that property directly. To overcome this issue, you or your developer will need to do three things:
  1. Create a custom property, CustomDescription, in the custom timer job's class which will reference a private field, m_description
  2. Override the Description property and have it obtain the value of your custom description's private field, m_description
  3. Set the value of the CustomDescription property in each of the timer job's constructors
Here is some sample code that highlights all of the steps within my custom timer job called EmailNotificationTimerJob as follows:

class EmailNotificationTimerJob : SPJobDefinition
{
    private string m_description;
    public override string Description
    {
        get
        {
            return m_description;
        }
    }

    public string CustomDescription
    {
        get
        {
            return m_description;
        }
        set
        {
            m_description = value;
        }
    }

    public EmailNotificationTimerJob() : base()
    {
        this.Title = "CHCO Doc Retention - Email Notifications";
        this.CustomDescription = "This timer job will be used to send email notifications to users who are responsible for documents that are about to expire";
    }

    public EmailNotificationTimerJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)
        : base(jobName, service, server, targetType)
    {
        this.Title = "CHCO Doc Retention - Email Notifications";
        this.CustomDescription = "This timer job will be used to send email notifications to users who are responsible for documents that are about to expire";
    }

    public EmailNotificationTimerJob(string jobName, SPWebApplication webApplication)
        : base(jobName, webApplication, null, SPJobLockType.ContentDatabase)
    {
        this.Title = "CHCO Doc Retention - Email Notifications";
        this.CustomDescription = "This timer job will be used to send email notifications to users who are responsible for documents that are about to expire";
    }

    ...

Once you have deployed the timer job code and performed an IISReset on the farm, your SharePoint Admins will now be provided with a description of the custom timer job on the Edit Timer Job page as follows:




Monday, November 19, 2012

Adding time to a DateTime value obtained via a SQL SELECT query

If you're looking for a good way to add a fixed duration of time to a DateTime value obtained in a SQL SELECT query, the following example provides the Transact-SQL code that can do this:

DECLARE @DurationTillExpire INT
SET @DurationTillExpire = 1

SELECT 
   dbo.Documents.DocumentId,
   dbo.Documents.LastModifiedDateTime, 
   (SELECT DATEADD(hour, @DurationTillExpire, dbo.Documents.LastModifiedDateTime)) AS 'ExpirationDateTime'
FROM dbo.Documents

In this example, I'm obtaining values from a table called Documents and am simply adding one hour to the DateTime column called LastModifiedDateTime and placing that value in a new column called ExpirationDateTime.  Of course, you may require a different duration such as days, months, or years and can account for this by adjusting the first parameter of the DATEADD function appropriately (see http://msdn.microsoft.com/en-us/library/ms186819.aspx for more info).

Thursday, November 8, 2012

Hide/Display the form ribbon on an InfoPath 2010 form

On one of our SharePoint 2010 Team Sites, we're currently leveraging a custom InfoPath 2010 form to override the look and feel of the form that's used for creating/modifying items in one of our SharePoint lists as well as perform several custom actions during the form's Save operation which is controlled by button clicks.  As a result, we no longer need the ribbon and, in fact, would prefer to remove it since it's causing more problems and confusion.


In order to hide the ribbon, you may do so via InfoPath 2010 as follows:

  1. Using IE, navigate to your List view
  2. Under the List Tools tab, click on the List tab
  3. Click on the Customize Form ribbon item
  4. Once InfoPath Designer opens, click on File -> Info -> Form Options
  5. To completely remove the ribbon, uncheck the Show InfoPath command in Ribbon or toolbar checkbox
  6. Click OK
  7. Save the changes and publish your form
Once these steps are complete, the ribbon will no longer appear on the form whenever a user creates/modifies a list item.

Thursday, November 1, 2012

Programmatically creating a custom hyperlink within a content editor web part

Not certain how useful this is; however, I was recently asked if I could update the title of a web part hosting a view to a SharePoint list so that it would not only be larger, but would also incorporate the upcoming date of the next weekly meeting in the title's text.  The solution I decided to implement for this particular site consisted of the following activities:
  1. Set the Appearance -> Chrome Type of the list web part to "None" in order to hide the existing title of the list web part
  2. Added a new Content Editor Web Part immediately above the applicable list web part
  3. Used Notepad to create a javascript file, OutstandingRCATitle.txt, that would create the custom hyperlink within the content editor web part
  4. Uploaded the custom javascript file to the Site Assets library of the given SharePoint site
  5. Linked the content editor web part to this file via the Content Link parameter of the content editor web part
With that accomplished, I simply had to add the appropriate javascript code to the OutstandingRCATitle.txt file in the Site Assets library to provide the requested functionality.  This functionality is listed as follows:
  1. Increase the size of the link to make it stand out to the users (i.e. used <h1> for this purpose)
  2. Append the date of the next weekly team meeting to the link (i.e. the meeting always takes place on a Tuesday so I created code that would add additional days to the current date so that the upcoming Tuesday's date would be listed)
  3. Have the link reference the appropriate view to the list (i.e. I maintained the link to the original AllItems.aspx view of the list; however, you could specify another view)
Here is the javascript code that I used for meeting this particular user's needs:
<script language="javascript"> var now = new Date(); var todaysDay = new Date().getDay(); var nextTuesdaysDate = new Date(); var numDays; switch (todaysDay) { case 0 : numDays = 2; break; case 1 : numDays = 1; break; case 2 : numDays = 0; break; case 3 : numDays = 6; break; case 4 : numDays = 5; break; case 5 : numDays = 4; break; case 6 : numDays = 3; break; } nextTuesdaysDate.setDate(now.getDate() + numDays); var displayDate = "<h1><a href='../Lists/Outage/AllItems.aspx' class='customLink'>Outstanding RCAs to be reviewed on " + nextTuesdaysDate.toLocaleDateString() +"</a></h1>"; document.write(displayDate); </script>
Since this was a one-time request for a particular user (i.e. a priority one at that) and not something that would be applicable to multiple sites, I decided to use this solution; however, I would have used a radically different approach to solving this problem if this was something that was required across multiple sites.  Not certain how useful this might be to others, but I thought I'd share anyway.