SharePoint 2010 custom ribbon actions project

One of the things I’ve found myself building in several different SharePoint projects are customized ribbon actions. The ribbon in SharePoint is a really useful tool, and the fact that it’s completely customizable makes it exceptionally powerful. I’ve put together some of the more universally useful ribbon actions that I’ve worked on into one generic solution which could be a great benefit to anyone looking to provide their SharePoint authors with more functionality.

[Skip all this and go straight to the code on CodePlex]

If you’re interested in what you see here and want a little more behind-the-scenes look at how it works, check out my post on creating custom actions for ribbon buttons.

The Buttons

This solution contains nine custom ribbon buttons, grouped into eight different features which are activated at the site collection level. Once this solution is deployed to a farm, these features can be activated one-by-one in each available site collection. This allows a lot of flexibility in deciding whether you want to use all of the included custom actions or just a selection.

If all of the features are activated, you should end up with one new button on the “Editing Tools > Format Text” tab, and eight new buttons on the “Editing Tools > Insert” tab:



The Actions

The buttons all do pretty much what you expect, and you can check the project home page on CodePlex for more individual details on each one.

The three embed buttons (Audio, YouTube, and generic) all insert placeholder content that’s visible in edit mode and replaced by the appropriate embed code in display mode.

Edit mode:

Display Mode:

This is accomplished by checking a global SharePoint variable, g_disableCheckoutInEditMode, which is true in edit mode and false in display mode. This allowed me to write JavaScript that runs selectively depending on what the user is doing.

Using jQuery

Much of the code that handles embedded content in this solution relies on jQuery. Unfortunately there isn’t really a good way to include jQuery in this solution and make sure it gets called before the custom RibbonActions.js, so I had to get a little creative.

First, I created a recursive function that waits until the DOM is loaded and checks to see if the jQuery object is undefined. If it is, it creates a script element that points to an external jQuery library and appends it to the <body> tag.

This alone was not quite enough, because I wanted to account for the possibility that some sites would have a different 3rd party library that jQuery could conflict with. To prevent any problems there, at the top of the file I added:

Which, in conjunction with line 4 of the above function allowed me to store whether or not I needed to call jQuery.noConflict() for use in a later function. And that function is another recursive function that waits for jQuery to load and then runs all of my jQuery dependencies:

You can see that the if statement starting on line 6 checks to see if jQuery.noConflict() is needed, and calls it if necessary. I had also experienced some problems calling a $(document).ready(...) event handler after the DOM had already loaded, so I used jQuery.isReady to check whether the DOM is already loaded. If it is, I call the dependencies immediately, if not, I add them to a $(document).ready(...) handler.

One more point of interest, I wrapped every use of jQuery in a (function($) { ... })(jQuery); wrapper so that I could still use “$” to represent jQuery without worrying about interfering with another library.

The Embed Action

The jewel of this project is without a doubt the generic Embed action. You may be aware that SharePoint generally prevents the adding of dynamic content to rich text fields. If you edit the HTML directly and insert some dynamic content, SharePoint will strip it out. This behavior is what initially led to the development of the Audio and YouTube Embed actions.

Eventually I got the idea that maybe I could build a ribbon action that would preserve any HTML, not just specified Audio or YouTube links, and then render them dynamically in display mode. I accomplished this by escaping the dynamic HTML and storing it as content in a hidden <div>. In display mode, the content of that hidden <div> would be unescaped and inserted as HTML content on the page. Once I got that idea, it was pretty easy to implement. But I wanted it to do more.


The one major drawback to this idea was lack of editability. In order to edit anything, I would essentially have had to delete the embedded content and replace it with updated content. I wanted to be able to pull up the embedded content as unescaped HTML and update it, and then re-escape it and insert it back into the content where it had been.

I figured out that I could do this by inserting a button into the placeholder content, and adding a click event to that button that matched the click event that pulls up the Embed dialog in the first place. I did have to make a couple of changes to the function call, adding in three optional arguments:

code is the escaped HTML embed code of the current item, caption is the caption of the current item, if it has one, and index is the zero-based index of the current Embed item in an array of all Embed items on the page.

If the “Embed” button in the ribbon is clicked, this function is called with no arguments.

If the “Edit Content” button in the placeholder content of an Embed item is clicked, the function is called with code and index given their proper values, and caption given a value if the item has a caption, set as undefined if it doesn’t.

Adding a regular $(...).click() event to an item in a SharePoint RTE doesn’t work (my guess it that the DOM inside the RTE is either unavailable at, or changes after $(document).ready()), so I had to use a persistent event handler. Since I couldn’t be sure what version of jQuery would be loaded on sites that used this solution, I built it to check for jQuery.fn.on and jQuery.fn.delegate before falling back to if necessary.

The Dialog

When the dialog page loads, it runs a simple function to check if the code or caption properties of the window.frameElement.dialogArgs object are populated, and if they are it pushes them as the value of the appropriate text area.


When the “Insert Content” button is clicked, a new object is created, and populated with properties for the code and caption. It then checks to see if the index property of window.frameElement.dialogArgs is defined. If it isn’t, we know that is is the creation of a new Embed item. If it has a value then we know that we’re updating an existing item, so we make sure to pass the index into the return object. Then, just for simplicity, we create a boolean property on our object and set it to true if we’re creating a new item and false if we’re not.

The Callback

By the time we get to this point it kind of takes care of itself. We check the newEmbed property, and then continue on to the appropriate half of the function, either updating or creating. If we’re updating an existing item, we use jQuery to create an array of all Embed items and use the index property of our return value to get the appropriate Embed item.

Rendering in Display Mode

Once a page containing an Embed item is viewed in display mode, I needed to take the escaped HTML embed code and turn it into actual HTML code that could be inserted into the page. This ended up being fairly easy, with a couple of if tests to deal with the possibility of a caption being the most complicated piece.

The end result of all this is the ability to add any HTML content into a SharePoint rich text field, without any limitations imposed by the SharePoint system. I have used this to embed Google Maps, Vimeo and a variety of other types of video, <style> blocks that I only needed on one page, and <script> blocks and references that were only needed on one page. It has proved itself to be an extremely versatile solution that could be useful on just about any SharePoint 2010 site that.

Tagged with: , , ,
Posted in CodePlex, HTML & CSS, JavaScript, jQuery, SharePoint, SharePoint Frontend

Leave a Reply

Your email address will not be published. Required fields are marked *