Manipulating the ribbon in SharePoint 2010 with JavaScript: Part 1

This is part 1 of a series I plan to write that covers how to manipulate the ribbon in SharePoint 2010 with JavaScript. I took my inspiration from this post by Chris O’Brien, which also focuses on manipulating the ribbon in SharePoint (but doesn’t cover it from a JavaScript-centric perspective).

This post will introduce the series, explain why I think this topic is important, and give you some quick code samples to get you started.

Why JavaScript?

First, in case you’re wondering about the rationale for messing with the ribbon through JavaScript, I have two words for you: sandboxed solutions. If you weren’t already aware, the SPRibbon class is not available to us in the sandbox. That means if your solution targets SharePoint Online (Office 365), you need an alternative. And so far, almost every blog post and article I’ve found about manipulating the ribbon – especially for adding contextual tabs – relies on using SPRibbon in some capacity.

What about the Declarative/XML Approach?

Many customizations can be done to the ribbon using CustomAction elements in your declarative feature XML. This series on manipulating the ribbon with JavaScript is not intended to replace the declarative approach. Rather, it’s intended to supplement it and give you more options where the declarative approach falls short.

One example is adding a contextual tab that’s associated with a custom web part. Normally this can be done using a combination of declarative XML and some server-side calls to SPRibbon in your web part. However, as I mentioned earlier, SPRibbon isn’t available in the sandbox. The declarative approach alone isn’t enough to get a contextual tab working, so JavaScript can help us fill the gap.

Side note: As a general rule, I try to minimize declarative XML in my SharePoint features anyway. That’s a little tougher to do in sandboxed solutions since you can’t always do everything through code, but even then I’ve found workarounds in most cases by using the client object model or other means. One reason I prefer code over XML is I’ve run into many bugs – as I’m sure many of you have – with how SharePoint processes declarative XML. Another reason is a code-based approach makes feature upgrades easier and more predictable.

The JavaScript Ribbon API

I use the term “API” somewhat loosely because the objects and functions you can use to manipulate the ribbon in JavaScript are not all packaged into a nice clean API like most of the client object model. Finding what you need and using it correctly can be a chore, and that’s why I’ve tried to do a lot of the up-front discovery work for you.

Let’s start with files. The core API is spread throughout the following files in the {SharePointRoot}\TEMPLATE\LAYOUTS folder:

  • Core.js
  • CUI.js
  • Init.js
  • SP.Ribbon.js

As usual, replace “.js” with “.debug.js” to get the non-minimized version of the file that’s more readable for debugging purposes.

It’s also important to know that part of the API is actually embedded directly within the page you’re viewing. To see what I mean, navigate to a page in the SitePages library and view its source. Search for “_ribbonStartInit,” and you’ll see some ribbon initialization code embedded directly within the page. Sometimes you’ll need to make use of that, and if your page doesn’t automatically emit it, you’ll need to add similar code yourself.

It’s also good to know the phrase “Command UI” is often used to refer to the ribbon and its components. Some of the functions, like RefreshCommandUI() in Core.js, don’t use the word “ribbon” in their name.

IMPORTANT: When you’re exploring these files, you’ll see a lot of obfuscated/mangled function and variable names like $K and $10_2. To be safe, always assume that anything mangled is not part of the public API. In my discussion, I’ll stick to the non-mangled items. It’s still possible the non-mangled items may change someday, but it’s less likely… especially because they’re used by many of the OOB web parts.

So far in my testing, I’ve only had to reference “sp.js” and “CUI.js” in my pages to use the API.

One way to include the API is to use ScriptLink tags in the placeholder for additional page head content like this:

<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">
   <SharePoint:ScriptLink Name="sp.js" LoadAfterUI="true"
                          OnDemand="false" Localizable="false"
                          runat="server" />
   <SharePoint:ScriptLink Name="CUI.js" LoadAfterUI="true"
                          OnDemand="false" Localizable="false"
                          runat="server" />
</asp:Content>

Getting a Reference to the Ribbon

The last thing I’m going to talk about in this first post is getting a reference to the ribbon. This is tougher than it might seem. The ribbon uses an on-demand/lazy loading approach to initialize itself, and that often impacts exactly when you can use certain objects and methods in the API. If you don’t use them at the right time, they’ll be null or will contain invalid data.

For now, let’s assume I’m working on a sandboxed solution for Office 365 and I want to manipulate the ribbon from a web part page in the SitePages library.

Here’s a block of code that will get you a reference to the ribbon and get you started:

<asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server">

   <!-- ScriptLink tags for sp.js and CUI.js could go here if you wish -->

   <script language="javascript" type="text/javascript">

      function DoSomethingWithRibbon() {
         // Gets a reference to a CUI.Ribbon object (CUI.js)
         var ribbon = SP.Ribbon.PageManager.get_instance().get_ribbon();

         // Show me which tab is selected - will show
         // 'Ribbon.Read' if the Browse tab is selected.
         alert(ribbon.get_selectedTabId());
      }

      // Note: 'SOD' is an abbreviation for "Script on Demand"
      SP.SOD.executeOrDelayUntilScriptLoaded(function() {

         var pm = SP.Ribbon.PageManager.get_instance();

         pm.add_ribbonInited(function() {
            DoSomethingWithRibbon();
         });

         var ribbon = null;
         try
         {
            ribbon = pm.get_ribbon();
         }
         catch (e) { }

         if (!ribbon) {
            if (typeof(_ribbonStartInit) == "function")
               _ribbonStartInit(_ribbon.initialTabId, false, null);
         }
         else {
            DoSomethingWithRibbon();
         }
      },
      "sp.ribbon.js");
   </script>
</asp:Content>

If you’re thinking that seems like a lot of code just to get a reference to the ribbon, you’re right. But it’s necessary. Let’s break down what’s being done here…

First, the code is wrapped in an executeOrDelayUntilScriptLoaded() call to ensure SP.Ribbon.js has been loaded. Nothing too fancy there.

The next piece you should look at is the try/catch block. I copied this code from one of SharePoint’s OOB web parts for the sake of consistency. Essentially we’re checking whether the ribbon has been loaded and is available to us to manipulate. If not, we cause it to load by calling the _ribbonStartInit() function. In case we had to cause it to load, we added a handler for the PageManager’s ribbonInited event above. Up until the point that event is fired, calling get_ribbon() would return null.

Wondering where the _ribbonStartInit() function is defined? The answer is in the page itself (along with the _ribbon.initialTabId variable). I’ve only tested this with pages in the SitePages library so far, but if you view the source on any page in that library, you’ll see that function defined in the page. And the OOB web parts rely on it being there, so I assume it’s there pretty consistently. If you delve into the details of it, you’ll see that it sets some options on a “ribbon builder” object and then dynamically constructs the ribbon. More on that in another post.

And if you’re wondering why the ribbon wasn’t already loaded when the page was first rendered, there’s a reason. Assuming your page comes up with the Browse tab selected first, the ribbon isn’t really loaded. Even if other tabs are present (like the Page tab), if they haven’t been shown yet, the ribbon has had no reason to load any of its controls or page components. Remember everything the ribbon does is on demand. The default behavior is the ribbon isn’t loaded until something happens that forces it to (like clicking the Page tab).

Conclusion

That’s it for this first post. Hopefully you found it helpful. The next one in the series will cover the most common API objects and methods you’ll use in manipulating the ribbon (and a lot of them aren’t documented on MSDN… hence the reason I’ll be doing it here).

SharePoint 2010: How to hide End Time field on a calendar list form

This may seem like a strange topic to blog about, but I believe if it’s happened to me, it could happen to someone else. So here goes…

I was recently working on a commercial product for SharePoint 2010, and one of the requirements in the product was for events to be created in a calendar list where the start and end times were the same. In essence, the events on the calendar were designed to serve as “due date” reminders (zero-duration events).

My first thought was, “Hey, no big deal. I’ll just mark the End Time column as hidden. Then I can use a list item event receiver to set its value equal to the Start Time every time an item is created or modified.” So I went with that approach. Then the problems began.

I went to create a new event, and the ribbon was disabled in the new event dialog window. I also saw a JavaScript error in the status bar of my browser. On top of that, when adding an event from the calendar view using the hover-activated ‘Add’ link, the Start Time was always set to today’s date – it didn’t honor the date I’d clicked on.

Long story short, hiding the built-in End Time column breaks the default ‘New’ and ‘Edit’ list forms for a calendar list.

In the end, since I had a custom list definition I was deploying anyway, I just added custom ‘New’ and ‘Edit’ forms for my list. In both forms, I added the following jQuery code into the ‘PlaceHolderAdditionalPageHead’ content region:

$(document).ready(function() {
    // Hide End Time field
    $('nobr:contains("End Time")').closest('tr').hide();

    // In my case, I also needed to add this call - it initializes the values
    // of the date/time picker fields. Without this, the form would sometimes
    // not submit properly (seemed to be a client-side timing issue... not sure).
    ExecuteOrDelayUntilScriptLoaded(function() {
        SP.UI.ApplicationPages.DateTimeFields.ensureInstance().onChange();
    }, 'sp.ui.applicationpages.calendar.js');
});

That solved the problem nicely. The other change was in my custom list definition, I added a ShowInDisplayForm=’FALSE’ attribute to the <Field> tag for the “EndDate” field. That took care of hiding the field on the display form. The display form doesn’t have the ribbon/JavaScript problem, so hiding the field in a normal way works fine in that case.

(Note: The version of jQuery I used was 1.6.2)