Chapter 6. Building a Quote of the Day Web Part in SharePoint 2010

Back in 2009, I wrote an article about how to create a simple Quote of the Day web part by leveraging SharePoint’s 2007 customization capabilities (see Figure 6-1).

Quote of the Day web part in SharePoint 2007

Figure 6-1. Quote of the Day web part in SharePoint 2007

Although the code behind the web part was very simple, the web part itself had a few limitations such as dependency on the jQuery library or use only in the same site as the location of the Quotes list.

One of the new pillars of SharePoint 2010 is the JavaScript Object Model (JSOM), which allows you to build dynamic web applications (http://msdn.microsoft.com/en-us/library/ee538253.aspx). In the first part of this chapter, you will learn how to build your own Quote of the Day web part using the new SharePoint 2010 JSOM.

Whenever you want to redistribute your SharePoint solution, you should create a SharePoint Solution Package (WSP). Unfortunately, in many environments, deploying WSPs is restricted and allowed only after passing reviews. This process might be very expensive for a simple solution such as the Quote of the Day web part. The concept of the Sandbox has been introduced in SharePoint 2010 specifically for such scenarios. Sandbox allows you to deploy SharePoint Packages in an isolated process without the involvement of IT and can be done by the site collection administrator.

In the second part of this chapter, we will create a Sandboxed Solution that will wrap the Quote of the Day web part with all of its assets, making it ready for redistribution.

Before we start, let’s take a moment to look at what we want to achieve. We want our Quote of the Day web part to display a randomly chosen quote and its author from a list of quotes. We want to pick one quote for each day instead of a random one each time the user opens the page. To simplify the maintenance of quotes and authors, we want to store them in a SharePoint list.

The first step in creating a working Quote of the Day web part is to create and configure a list we will use for managing quotes.

Now that we have the Quotes list created, let’s proceed with configuring its columns so that, for every quote, we can store the quote itself and its author.

First we will change the name of the Title column to Author.

  1. In the ribbon, activate the List tab and, in the Settings group, click the List Settings button (Figure 6-4).

  2. From the list of List Columns, choose the Title column. Change the name of the column to Author (#1 in Figure 6-5) and confirm your changes by clicking OK (#2 in Figure 6-5).

  3. The next step is to add a new column for storing quotes. On the List Settings page, in the Columns section, click the Create column link. Type Quote for the column name (#1 in Figure 6-6). From the list of available types, select the Multiple lines of text field type (#2 in Figure 6-6). In the Additional Columns Settings section, choose Plain text (#3 in Figure 6-6). Confirm your choices by clicking OK (#4 in Figure 6-6).

After adding the Quote column, it should appear in the list of available columns, as shown in Figure 6-7.

To finish configuration of the Quotes list, let’s add a couple of quotes. Figure 6-8 shows a sample Quotes list filled with a few quotes.

Now that the Quotes list is ready, let’s proceed with creating the Quote of the Day web part.

To keep the web part as simple as possible, in this part of the chapter we will use the Content Editor web part (CEWP) to store all of our JavaScript. Using the SharePoint 2010 JSOM, we will retrieve the Quote of the Day and display it on the screen. In the second part of this chapter we will consider a slightly more advanced scenario and prepare our solution for redistribution using a Sandboxed Solution.

  1. Let’s start off by adding the CEWP to the page. We’ll call it Quote of the Day (Figure 6-9).

  2. Click the Click here to add new content link. In the ribbon, from the Markup group, click the HTML drop-down control and choose the Edit HTML Source option. In the HTML Source dialog, enter the following code snippet:

    <style type="text/css">
    .message { text-align: center; }
    .message img { vertical-align: middle; }
    blockquote p { font: italic bold 1.6em/1.2 "Times New Roman" , serif; }
    blockquote p.author {
      font: normal 1em/1.2 sans-serif;
      color: #666;
      text-align: right;
    }
    </style>
    <div id="quoteOfTheDay"></div> 1
    <script type="text/javascript">
    Date.prototype.getFullDate = function () { 2
        var dateAsString = '';
        dateAsString += this.getFullYear();
        var month = this.getMonth() + 1;
        if (month < 10) {
            dateAsString += 0;
        }
    
        dateAsString += month;
    
        var day = this.getDate();
        if (day < 10) {
            dateAsString += 0;
        }
    
        dateAsString += day;
    
        return parseInt(dateAsString);
    }
    
    SP.SOD.executeOrDelayUntilScriptLoaded(function() { 3
        Type.registerNamespace('Mavention.QuoteOfTheDay');
        Mavention.QuoteOfTheDay.displayQuote = 4
    function (webUrl, listName, container) {
            container.innerHTML = '<div class="message">\ 5
    <img src="/_layouts/images/loading.gif" alt=""/>\
    Loading Quote of the Day...</div>';
    
            context = SP.ClientContext.get_current();
    web = context.get_site().openWeb(webUrl);
    list = web.get_lists().getByTitle(listName);
            query = new SP.CamlQuery();
            query.set_viewXml('<View><ViewFields><FieldRef Name="Title"/>\
    <FieldRef Name="Quote"/></ViewFields></View>');
            items = list.getItems(query); 6
            context.load(items); 7
            context.executeQueryAsync(Function.createDelegate(this, function () {
                numberOfQuotes = items.get_count(); 8 9
                if (numberOfQuotes > 0) {
                    now = new Date();
                    quoteNumber = now.getFullDate() % numberOfQuotes;
                    quoteItem = items.get_item(quoteNumber);
                    quote = quoteItem.get_item('Quote'); 10
                    author = quoteItem.get_item('Title');
    
                    container.innerHTML = '<blockquote><p>' + quote +
    '</p><p class="author">' + author + '</p></blockquote>';
                }
                else { 11
                    container.innerHTML = '<div class="message">\
    There are no quotes in the list yet</div>';
                }
            }), Function.createDelegate(this, function () { 7
                container.innerHTML = '<div class="message">\
    <img src="/_layouts/images/error16by16.gif" alt=""/>\
    An error has occured while loading Quote of the Day</div>';
            }));
        }
    
        Mavention.QuoteOfTheDay.displayQuote('/', 'Quotes', 12
    document.getElementById('quoteOfTheDay'));
    }, 'sp.js'); 3
    </script>
    1

    We start the snippet by defining a container where we will display the Quote of the Day.

    <div id="quoteOfTheDay"></div>

    B) Next we add the JavaScript code responsible for loading the Quote of the Day. To avoid naming conflicts, we define our custom code in a namespace.

    <script type="text/javascript">
    Type.registerNamespace('Mavention.QuoteOfTheDay');
    Mavention.QuoteOfTheDay.displayQuote = function () {
    }
    </script>
    4

    We want our web part to be reusable and work with Quotes lists no matter their location, so let’s add parameters that will allow us to specify which list we want to use. Since loading the data is asynchronous, we will also need a parameter to determine where on the page the quote should be displayed.

    <script type="text/javascript">
    Type.registerNamespace('Mavention.QuoteOfTheDay');
    Mavention.QuoteOfTheDay.displayQuote = function (webUrl, listName, container) {
    }
    </script>

    Now we’ll add the code responsible for loading quotes from the given Quotes List.

    5

    Because loading quotes can take a while, we start with displaying a message communicating that loading the quote is in progress.

    container.innerHTML = '<div class="message">\
    <img src="/_layouts/images/loading.gif" alt=""/>\
    Loading Quote of the Day...</div>';
    6

    Next, we start the process of loading all available quotes. Since there is no function available that would allow us to directly load a random quote, we will first retrieve all quotes from the Quotes list and pick a random one for today. To minimize the amount of data that will be downloaded, we specify in the CAML query that only two columns (Author and Quote) should be retrieved.

    context = SP.ClientContext.get_current();
    web = context.get_site().openWeb(webUrl);
    list = web.get_lists().getByTitle(listName);
    query = new SP.CamlQuery();
    query.set_viewXml('<View><ViewFields><FieldRef Name="Title"/>\
    <FieldRef Name="Quote"/></ViewFields></View>');
    items = list.getItems(query);
    7

    With that, we’re ready to load all available quotes. Because the process is asynchronous, we have to provide two delegate functions that will be executed: one for when the loading completes and one should there be any errors.

    context.load(items);
    context.executeQueryAsync(Function.createDelegate(this, function () {
        // success
    }), Function.createDelegate(this, function () {
        // error
    }));
    8

    After loading completes, we need to get the total number of quotes in the list. We will use it to calculate the number of the random quote for today.

    numberOfQuotes = items.get_count();
    2

    As I mentioned before, we don’t want to display a random quote on each page load. Instead, we want to pick one quote for today and display it during the whole day for all users. To do that, we have to calculate a random number based on today’s date, so we will need a function that will return today’s date as number (lines 13–31).

    Date.prototype.getFullDate = function () {
        var dateAsString = '';
        dateAsString += this.getFullYear();
        var month = this.getMonth() + 1;
        if (month < 10) {
            dateAsString += 0;
        }
    
        dateAsString += month;
    
        var day = this.getDate();
        if (day < 10) {
            dateAsString += 0;
        }
    
        dateAsString += day;
    
        return parseInt(dateAsString);
    }
    9

    By using the prototype notation, we add the getFullDate function to all JavaScript Date objects, which makes it easier for us to use the new function. With that, we can retrieve the quote for today.

    numberOfQuotes = items.get_count();
    if (numberOfQuotes > 0) {
        now = new Date();
        quoteNumber = now.getFullDate() % numberOfQuotes;
        quoteItem = items.get_item(quoteNumber);
    }
    10

    Once we have our quote for today picked, we can display it, replacing the progress message (lines 55–59).

    quote = quoteItem.get_item('Quote');
    author = quoteItem.get_item('Title');
    
    container.innerHTML = '<blockquote><p>' + quote +
    '</p><p class="author">' + author + '</p></blockquote>';
    11

    Should we choose an empty Quotes list, we can display an information message stating that no quotes have been entered yet.

    if (numberOfQuotes > 0) {
        // omitted for brevity
    }
    else {
        container.innerHTML = '<div class="message">\
    There are no quotes in the list yet</div>'
    }
    12

    With our code in place, it’s time to call our function passing the URL of the site, the name of our Quotes list, and where on the page the Quote of the Day should be displayed.

    Mavention.QuoteOfTheDay.displayQuote('/', 'Quotes', document.getElementById
     ('quoteOfTheDay'));
    3

    If you tried to run the script at this moment, you would end up with an error. Since SharePoint 2010 loads JSOM asynchronously, and because our code depends on it, we have to wait on the execution of our script until JSOM has been loaded. We can do this by using the SharePoint SOD (Script On Demand) capability provided with SharePoint 2010 JSOM and wrap our code in the SP.SOD.executeOrDelayUntilScriptLoaded function.

    SP.SOD.executeOrDelayUntilScriptLoaded(function() {
        // code goes here
    }, 'sp.js');
  3. This completes our code snippet. After you paste it in the CEWP’s HTML Source dialog, confirm the changes by clicking the OK button. Next, click the OK button in the CEWP’s Editor pane—you should see the Quote of the Day as shown in Figure 6-10. (You can ignore the HTML modification warning.)

With that, we have created a Quote of the Day web part that dynamically loads a quote for today using the SharePoint 2010 JSOM. In the next part of this chapter, we will prepare the Quote of the Day web part for redistribution by including it in a Sandboxed Solution.