Trigger an event whenever the SharePoint 2010 ribbon changes

If you ever need to do things every time the ribbon is changed, here’s how to do it with jQuery events.

// Fires 'ribbontabselected' every time the ribbon selection changes
ExecuteOrDelayUntilScriptLoaded(function () {
    CUI.Ribbon.prototype.$L_old = CUI.Ribbon.prototype.$L;
    CUI.Ribbon.prototype.$L = function () {
        this.$L_old();
        $(document).trigger('ribbontabselected', [this.get_selectedTabCommand()]);
    };
}, 'cui.js');

// Fires 'ribbontabselected' after the ribbon has been initialized after load
ExecuteOrDelayUntilScriptLoaded(function () {
    var pm = SP.Ribbon.PageManager.get_instance();
    pm.add_ribbonInited(function () {
        $(document).trigger('ribbontabselected', [SP.Ribbon.PageManager.get_instance().get_ribbon().get_selectedTabCommand()]);
    });
}, 'sp.ribbon.js');

// Example code for binding to the event
$(document).on('ribbontabselected', function (e, selectedTabCommand) {
    if (selectedTabCommand != "ReadTab") {
        alert(selectedTabCommand);
    }
});

Creating a custom SharePoint 2010 Timer Job with persisted properties

MSDN has a how to article which is a pretty good walkthrough on creating custom timer jobs, but it leaves out the essential piece on how to keep persisted properties.

Once you’ve followed their guide and have your timer job class, here is how to add custom properties to it:
Step 1: Add a [Guid] attribute to your class. This step is crucial. I don’t remember having to do this in 2007, but I know for sure your properties will not work in 2010 without this. Took me a while to realize that.
Step 2: Add some public member variables and decorate them with the [Persisted] attribute.

That’s it! That’s all there is to it. The magic here is thanks to the fact that Microsoft.SharePoint.Administration.SPJobDefinition inherits from SPPersistedObject. SharePoint handles all the serialization/deserialization from there.

The article also recommends creating a feature receiver to create the timer job. However I found that when activating it from the Web UI, even as a farm admin account, it would sometimes throw an ‘Access Denied’ error. I could only activate my feature from command line, and that’s no good. 2010 introduces a new member on SPPersistedObject named HasAdditionalUpdateAccess. Just override this method in your class, and return true to allow users to activate your feature receiver from the web.

Lastly, I like to create install and uninstall methods on my timer job class as well.. so see the below for an example.

using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace MonkeyPoints.TimerJobs
{
    [Guid("6DD003C3-2861-4F63-B974-D2653E713A74")]
    public class CustomTimerJob : SPJobDefinition
    {
        [Persisted]
        public string WebUrl;

        [Persisted]
        public string ListUrl;

        public CustomTimerJob() : base() { }

        public CustomTimerJob(SPWeb web, string listUrl) : this(JobName(web), web, listUrl) { }

        public CustomTimerJob(string jobName, SPWeb web, string listUrl)
            : base(jobName, web.Site.WebApplication, null, SPJobLockType.Job)
        {
            this.WebUrl = web.Url;
            this.ListUrl = listUrl;
        }

        protected static string JobName(SPWeb web)
        {
            return "CustomTimerJob_" + web.ID;
        }

        protected override bool HasAdditionalUpdateAccess()
        {
            return true;
        }

        public override void Execute(Guid targetInstanceId)
        {
            try
            {
                using (var site = new SPSite(WebUrl))
                using (var web = site.OpenWeb())
                {
                    // process whatever you need to on this list
                }
            }
            catch (Exception ex)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory(this.Name, TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, ex.Message, ex.StackTrace);
            }
        }

        public static void Install(SPWeb web, string listUrl, SPSchedule schedule)
        {
            using (SPSite site = new SPSite(web.Site.ID, web.Site.SystemAccount.UserToken))
            using (SPWeb eweb = site.OpenWeb(web.ID))
            {
                site.AllowUnsafeUpdates = true;
                eweb.AllowUnsafeUpdates = true;
                Uninstall(eweb);
                var syncJob = new CustomTimerJob(eweb, listUrl);
                syncJob.Schedule = schedule;
                syncJob.Update();
            }
        }

        public static void Uninstall(SPWeb web)
        {
            var jobName = JobName(web);
            foreach (SPJobDefinition job in web.Site.WebApplication.JobDefinitions)
            {
                if (job.Name == jobName)
                    job.Delete();
            }
        }

        public static void RunNow(string webUrl, string listUrl)
        {
            using (SPSite site = new SPSite(webUrl))
            using (SPWeb web = site.OpenWeb())
            {
                var job = new CustomTimerJob(web, listUrl);
                job.Execute(Guid.Empty);
            }
        }
    }
}

Thanks!


Follow

Get every new post delivered to your Inbox.