Hiding the out-of-the-box Document Library in SharePoint 2013

Recently I was tasked with investigating the possibility of removing the out of the box Document Library from the ‘add an app’ page so that users wouldn’t accidentally select that option instead of a specific list template containing the organisation’s meta data they want included in the library. It’s been refreshing exploring Intranet-style scenarios after having focussed on public facing web sites for so long and as such this is the type of problem I was eager to get my head around and solve.

In days gone by one would generally turn to creating a custom site definition to achieve such a goal – or god forbid modify one of the out of the box site definitions. We’ve all known for some time that the latter is a terrible idea, but the former isn’t particularly better when considering future migrations. It was no surprise however that it was still one of the first options I came across when researching solutions, such as on Salaudeen Rajack’s post Hide Site templates & List Templates in SharePoint. Back to the drawing board.

Another more promising option I pursued was using PowerShell to disable the feature which specifically provisions the Document Library as per Renan Rocha’s Hide “list template” disabling its feature or this StackOverflow question: Hide Listtemplate from Create Dialog in PowerShell. This worked, however it also removed a custom document library template which I had created for testing purposes to mirror the desired result I’d want to end up with. It also only worked at the web layer, which means that we’d first need to run it recursively across the whole site, then would also need to run it each time a new sub site was created – not exactly practical even if it did suit our purpose.

There were also a few hacky options I discovered along the way which I was obviously keen to avoid, such as that listed on the MSDN question: Is it possible to delete the default document library template?. I figured there had to be a better way.

I decided to change tack a little bit and investigate adding our custom templates as ‘Noteworthy’ apps to at least give them some prominence on the screen. Unfortunately it turned out that while you can set Apps as noteworthy by making them featured apps, you can’t set list templates as featured or noteworthy. There are some good posts on this topic including Stephen Brown’s SharePoint 2013 and the Noteworthy section and Shereen Qumsieh’s Making Changes to the Noteworthy Section of a SharePoint Site. It was these posts that gave me all the information I needed to solve my problem.

I ended up creating a Delegate control targeting AdditionalPageHead (yes this was a full trust solution – I’m sure someone can come up with an alternative way of getting this to work in Office 365!) to run the following JavaScript:

<script type="text/javascript">
    function AlterStorefront() {
        if (SP.Storefront != undefined) {
            var listedApps = SP.Storefront.StorefrontApp.get_currentView();
            if (listedApps == undefined || listedApps.$2i_3 == null || listedApps.$L_3 == null) { setTimeout(AlterStorefront, 750); return; }
			
            listedApps.$2i_3.length = 0;

            for (i = 0; i < listedApps.$L_3.length; i++) {
                if (listedApps.$L_3[i].$2Q_0.Title == "Document Library") {
                    listedApps.$L_3.splice(i, 1);
                }
                if (listedApps.$L_3[i].$2Q_0.Title.indexOf("Insite") > -1 || listedApps.$L_3[i].$2Q_0.Title == "Icon Links Grid") {
                    listedApps.$2i_3.push(listedApps.$L_3[i]);
                }
            }

            SP.Storefront.StorefrontApp.get_currentView().updateUI();
        }
    }

    SP.SOD.executeFunc("sp.js", "SP.Storefront", AlterStorefront);
</script>

While AdditionalPageHead exists on all pages, checking that SP.Storefront exists means that it won’t run on every page.

So the first point of note is that we wait until SP.js has loaded before calling our function given it relies on it. Once we’ve confirmed the Storefront does exist on the current page, we attempt to get the current view of the Storefront. Because this loads asynchronously itself, it is highly probable that it hasn’t loaded before this code executes. As such I performed a few checks to ensure the properties of note had completely loaded and re-called the function after sleeping for a period of time (0.75 seconds I found to be roughly the most optimal on my development server).

The next step was clearing out the existing Noteworthy apps by setting the array’s length to 0. This perhaps isn’t ideal given your ‘Featured’ apps would also be wiped rather than just the default ones that populate that area, however if this is a concern you can more specifically target their removal similar to how I did it in the next step.

That next step is looping through the array which stores all the app items. We look for the one corresponding with ‘Document Library’ and remove it using splice. We then look for a particular string (or multiples) to identify the templates we want to promote to Noteworthy, and add them to that array. The final step is to update the UI to reflect those changes.

One important thing to note is that the indexOf function is not compatible with IE7 and IE8 – if this is a problem then you may want to ‘find’ the apps for promotion another way.

So there you have it, a semi (not-so?) elegant way of achieving the desired result – removing the Document Library list template from the ‘add an app’ page and replacing the noteworthy items with your custom templates. The solution isn’t perfect – unfortunately there is a visual delay between loading the initial page (which you see for a moment) and when the updateUI kicks in – but overall it does the job.

Harnessing SignalR in SharePoint 2013 (Office 365)

Over a year ago I wrote a post on Harnessing SignalR in SharePoint. Back then, SignalR was fairly new and the amount of information out there was limited. Getting SignalR to work with SharePoint posed some challenges, all of which are outlined in that post, however eventually we got it working. Since then I’ve had little to do with the technology however the colleague I worked with on that proof of concept, Elliot Wood, has continued to keep on top of the changes to the library and the implications that may have for SharePoint integration. While i’m posting this to my blog, i’m doing so on behalf of Elliot who is responsible for all of the magic soon to be displayed.

Even though there was a fair amount of excitement in regards to SignalR, and particularly the work we did around getting it working in SharePoint, the number of subsequent posts along the same lines was fairly limited. One person who carried the torch was Maximilian Melcher – his post SignalR in SharePoint 2013 – the real-time web is coming! outlined that integrating SignalR in SharePoint 2013 wasn’t as easy as one might expect – even though we were now on the .NET 4.0 framework which should have made life much easier. Max ended up getting his solution working and subsequently posted a codeplex solution, however the post was enough to pose the challenge to Elliot to get it working as easily as possible.

The solution presented uses an autohosted App for SharePoint 2013 hosted on Office 365. With Microsoft’s direction seemingly steering away from full-trust and sandboxed solutions and straight towards the App model, this can be considered the most viable and future-proof solution to the ‘SharePointR’ dream.

To ensure the focus of this post remains focussed on integrating SignalR with SharePoint i’m going to avoid the specifics about SignalR and how to program with it. I’m also going to avoid specifics on how to create and deploy SharePoint Apps. We’ve come a long way with SignalR and the information out there is much better – I’d recommend reading ASP.NET SignalR Hubs API Guide – Server (C#) if you need an overview of programming with SignalR. For those that need a primer on SharePoint Apps, particularly in relation to provider-hosted Apps on Office 365 (the more ‘production-safe’ way of hosting Apps on Office 365), then head over to Chris O’Brien’s post Deploying SP2013 provider-hosted apps/Remote Event Receivers to Azure Websites (for Office 365 apps) for a quick read.

Now that you’re comfortable with the theory, it’s on to the solution. There are 2 pieces to the autohosted App puzzle – the SharePoint App and the SharePoint App Web.

SharePoint App

The purpose of this project is to define the Remote Event Receiver (RER) and also configure the Installed Event Endpoint for the App. The former essentially sets the types of events that will trigger the remote service and points the App to that service, the latter points to the service which will trigger when the App is installed.

Adding the RER is as simple as right-clicking the project and adding a new Remote Event Receiver and selecting the events you want to listen out for. Everything else will be taken care of for you including creating the Package, Feature and tying the RER to its class which will be hosted in the SharePoint App Web.

Configuring the Installed Event Endpoint requires you to edit the properties of the SharePoint App project and set the Handle App Installed property to true. Once again Visual Studio takes care of the rest.

The final step is to access the Permissions tab in the AppManifest.xml editor and add the necessary permissions to the Web and List to ensure your App has the required levels of access to your site.

That’s all there is to it.

SharePoint App Web

Setting up the SharePoint App Web project is a little more involved than the above however is still relatively simple. The first step is to add a SignalR Hub Class to the project – having this native in Visual Studio is fantastic and greatly simplifies the process of getting up and running with SignalR ensuring all necessary references are added to the solution (note that you must have installed the ASP.NET and Web Tools 2012.2 update before you can add this class natively). Alternatively you can add SignalR via the Package Manager Console.

Elliot has also decided to implement the hub using a Singleton instance for optimum performance. For this you’ll need to add another class and insert the following code:

    public class SharePointR
    {
        // Singleton instance
        private readonly static Lazy<SharePointR> _instance = new Lazy<SharePointR>(() =>
            new SharePointR(GlobalHost.ConnectionManager.GetHubContext<SharePointRHub>().Clients));

        public SharePointR(IHubConnectionContext clients)
        {
            Clients = clients;
        }

        public static SharePointR Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private IHubConnectionContext Clients
        {
            get;
            set;
        }

        public void NotifyDataChanged(string ListName, string Event)
        {
            Clients.Group(ListName).dataChanged(Event);
        }
    }

Once that’s been created we can edit the Hub we added earlier and insert:

    [HubName("SharePointRHub")]
    public class SharePointRHub : Hub
    {
        private readonly SharePointR _sharePointR;

        public SharePointRHub() : this(SharePointR.Instance) { }

        public SharePointRHub(SharePointR sharePointR)
        {
            _sharePointR = sharePointR;
        }

        public void Subscribe(string ListName)
        {
            Groups.Add(Context.ConnectionId, ListName);
        }

        public void UnSubscribe(string ListName)
        {
            Groups.Remove(Context.ConnectionId, ListName);
        }

        public void NotifyDataChanged(string ListName, string Event)
        {
            _sharePointR.NotifyDataChanged(ListName, Event);
        }
    }

One final piece to the SignalR puzzle is to add a Global.asax file which should include the following:

        protected void Application_Start(object sender, EventArgs e)
        {
            var hubConfiguration = new HubConfiguration();
            hubConfiguration.EnableCrossDomain = true;
            hubConfiguration.EnableDetailedErrors = true;
            RouteTable.Routes.MapHubs("/signalr", hubConfiguration);
        }

Now we have our SignalR plumbing we can set up our event receivers. The first cab off the rank is the AppEventReceiver which will fire when the App is installed. What we want to achieve here is to manually hook up the item event receivers to a list in the host web, for instance an Announcements list.

    public class AppEventReceiver : IRemoteEventService
    {
        private string ReceiverName = "RemoteEventReceiver";
        private List<EventReceiverType> EventType = new List<EventReceiverType>()
        {
            EventReceiverType.ItemAdded,
            EventReceiverType.ItemAdding,
            EventReceiverType.ItemUpdated,
            EventReceiverType.ItemUpdating,
            EventReceiverType.ItemDeleted,
            EventReceiverType.ItemDeleting
        };
        private string ListTitle = "Announcements";

        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
        {
            SPRemoteEventResult result = new SPRemoteEventResult();

            using (ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties, false))
            {
                Web hostWeb = clientContext.Web;
                clientContext.Load(hostWeb);

                List docLib = clientContext.Web.Lists.GetByTitle(ListTitle);

                string opContext = OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.Substring(0,
                   OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri.LastIndexOf("/"));
                string remoteUrl = string.Format("{0}/RemoteEventReceiver.svc", opContext);

                if (properties.EventType == SPRemoteEventType.AppInstalled)
                {
                    foreach (var eventType in EventType)
                    {
                        EventReceiverDefinitionCreationInformation newEventReceiver = new EventReceiverDefinitionCreationInformation()
                        {
                            EventType = eventType,
                            ReceiverAssembly = Assembly.GetExecutingAssembly().FullName,
                            ReceiverClass = "SignalRProxyService.Services.RemoteEventReceiver",
                            ReceiverName = ReceiverName + eventType.ToString(),
                            ReceiverUrl = remoteUrl,
                            SequenceNumber = 1000
                        };
                        docLib.EventReceivers.Add(newEventReceiver);
                    }
                    clientContext.ExecuteQuery();
                }
                else if (properties.EventType == SPRemoteEventType.AppUninstalling)
                {
                    IEnumerable<EventReceiverDefinition> receivers = clientContext.LoadQuery(docLib.EventReceivers
                        .Where(e => e.ReceiverName == ReceiverName));

                    foreach (var rec in receivers)
                    {
                        rec.DeleteObject();
                    }
                    clientContext.ExecuteQuery();
                }
            }
            return result;
        }

        public void ProcessOneWayEvent(SPRemoteEventProperties properties)
        {
            // This method is not used by app events
        }
    }

We then want to code up the Remote Event Receiver to handle the events triggered on the site and push a message via SignalR to our page. This is a very simplistic example that shows that the item affected in the list we’ve subscribed to is what is causing the update on the page.

    public class RemoteEventReceiver : IRemoteEventService
    {
        private SharePointRHub client = new SharePointRHub();

        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
        {
            client.NotifyDataChanged(properties.ItemEventProperties.ListTitle, properties.EventType.ToString());
            return new SPRemoteEventResult();;
        }

        public void ProcessOneWayEvent(SPRemoteEventProperties properties)
        {
        }
    }

The final step is to code up the page. There are a number of aspects here which should be completed to get the branding right for the App – these are covered in greater detail in the how-to guide listed at the end of this post. For now i’ll highlight the main code needed to hook up SignalR on the page (note that references to scripts not yet mentioned in this post are also covered in that guide).

    <!--Script references. -->
    <script src="//ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"></script>
    <script src="/Scripts/jquery-1.8.2.min.js"></script>
    <script src="/Scripts/json2.js"></script>
    <script src="/Scripts/chromeLoader.js"></script>
    <script src="/Scripts/jquery.signalR-1.0.0.js"></script>
    <script src="/signalr/hubs"></script>

    <!--Add script to update the page and send messages.-->
    <script type="text/javascript">
        $(function () {
            var connection = $.hubConnection();
            proxy = connection.createHubProxy('SharePointRHub')
            proxy.on('dataChanged', function (eventType) {
                $('#MainContent').append('<li>Data changed - ' + eventType + '</li>');
            });
            connection.start()
                .done(function () {
                    //Subscribe to Announcements list
                    proxy.invoke('Subscribe', "Announcements");
                    $('#MainContent').append('<span>Now connected, connection ID=' + connection.id + '</span>');
                })
                .fail(function () {
                    $('#MainContent').append('<span>Could not Connect!</span>');
                });
        });
    </script>

And that’s about it! The image below gives a sneak peak into how an event triggered via a SharePoint list can be reflected on a separate page in real time. It is far more impressive however seeing it in person, so I’d encourage you to follow the guide provided below and get ‘SharePointR’ up and running for yourself – the possibilities this technology opens up is limitless and it will be exciting to see the kinds of solutions designed which leverage SignalR within SharePoint in the near future.

PreviewDownload: Walkthrough-Deploying a SignalR autohosted App to Office 365

Harnessing Client-Side Technologies to Enhance your SharePoint Site

Yesterday I had the privilege of presenting at the Perth SharePoint User Group. The experience was sensational; the session went off without a hitch, the crowd was large, attentive and full of questions. Feedback both immediately after the session and that which filtered through later was very positive. As promised and to wrap up the event I thought it was only fitting that I posted my slides and a summary of the presentation – this is that post.

The day started off with a little milestone – something I’ve yet to see myself previously and which I expect is relatively rare for the PSPUG

100 tickets – the last one snapped up only hours before the event. I’ve been told Microsoft (who I must thank for allowing us to use their excellent facilities every month) was contacted on the day to hunt down some more chairs for the room. At best guess I’d say there was about 70-80 people who made it – evident by the few chairs left empty and the speed at which 15 family sized pizzas were demolished.

But enough with the background, onto the presentation itself. I had set it up to be roughly 30 minutes of slides and discussion and 30 minutes of demo. In the end I cut the demo’s a bit short – I figured it was more important to answer a few questions than explain the detail of what was going on for each one. I’ll fill the blanks in this post pointing off to the resources you’ll need to replicate the demos yourself. Along with posting the slides I’ll also jot down some of the main points I was trying to get across in each one.

Who am I

You may think this slide needs no explanation but I want to stress the point I made in the session again. While i’ve only recently joined twitter, the little gems of information I’ve been able to garner in that time have been extremely valuable. I avoided twitter like the plague, mostly due to an impression it was predominantly noise. It is. But if you can get past that you can get to its true value and keep in touch with what’s going on in the wider SharePoint community.

Agenda

I was tossing up between deep diving into one of the technologies or doing a broad brush overview of the lot – clearly the latter approach was taken. The goal of the session was to inform and inspire – there is plenty of information out there to read up on at a later date – some of which i’ll outline in this post.

JavaScript

Not much to say here – bit of an obligatory slide to round out the presentation as a whole. We’ll move on.

jQuery & The basics

Aside from the information, the main point I want to highlight is that jQuery really isn’t that scary. It simplifies JavaScript a great deal and the learning curve really isn’t that steep compared to the benefits you’ll gain from using it. It’s quite performant and efficient when compared to some of the .NET based AJAX libraries out there, it’s become one of my favourite technologies to use when creating public facing websites and I hope I impressed at least a few in the audience enough to give it a shot. For a primer on some of the basics and functions jQuery provides, have a read of the documentation.

Tips and Tricks

There was a fair few tips and tricks to cover in the session – rather than explain each one i’ll attempt to link off to a resource which will do the job for me.

Putting jQuery into noConflict mode

Making use of jQuery(document).ready()

Leverage callbacks

Don’t write scripts directly into the Content Editor Web Part

(Keep in mind it may not be suitable for staged environments with content deployment – converts the link to an absolute url!)

Know your options for referencing jQuery

(Although I don’t have a problem with referencing on the master page)

Enable Intellisense in Visual Studio

Know your options for debugging

(Don’t forget about the console window in Firefox! Can be very useful. Also, remember that different browsers can often end up shedding the light on your issue, so don’t discount debugging in multiple browsers)

It would be remiss of me not to give a shout out to Chris O’Brien considering the last 2 links are from his blog. In fact, all the posts of that series are worth a read so I’d definitely encourage you to do just that.

Plugins!

The true power of jQuery lies in the multitude of plugins available to be used. They can be a huge time-saver and with a bit of knowledge you can often customise them to suit your needs. There are hundreds of quality resources out there explaining which plugins are great – i’ll leave it up to you to explore!

Knockout

Knockout is fantastic. I can’t speak of it highly enough – do yourself a favour and check out the documentation and live examples to get a taste of what it is capable of. I could have done a whole presentation on Knockout itself – John Liu does a great one in his session SSPUG retrospective: Creating Knockout user experiences in SharePoint with JavaScript for those Aussies amongst us, definitely check it out if you get a chance. I hope I did it justice to peak your interest enough to dive deeper into the technology. Keep in mind that Knockout works brilliantly with both the jQuery templating libraries (jquery-tmpl or jsRender) and jQuery itself. I mentioned that while I’ve used jquery-tmpl in the past, it has been discontinued so you may want to look into jsRender instead.

SignalR

This is another topic that could have had a session to itself. In fact, Christian Heindel already did – it was nice to see myself in someone elses slides half a world away! I didn’t dive too heavily into this, it was more of a primer to spark some interest and simply let the demo do the talking.

Retrieving Data on the Client Side

To date I had only discussed the tools available to present information on the client – this slide was all about how we got that information in the first place. Rather than link to resources here (it should be really easy to search for them yourself) what I’ll do is outline my main points discussed.

Client Object Model: handy tool provided in SP2010. Need to learn the syntax which is different from the standard OM and should definitely know some CAML. Important to consider performance when writing this code.

REST services: absolutely love them. Extremely valuable with their ability to return JSON data simply by structuring the URL with query string. Their ability to return JSON makes them great to combine with Knockout or jQuery.

SPServices: extremely valuable library written by a member of the SharePoint community Mark D Anderson. Perfect for those stuck on SP2007 but also valid for SP2010. Have a read of the debate going on regarding when you should use one or the other.

HTTP Handlers: another tool at your disposal in SP2007. To be honest the main reason I have used these is because I wasn’t truely aware of the power of SPServices. The general premise is you fire off to the HTTP Handler which runs some server side code, serializes the data into JSON and returns it back via Response. Still handy if you simply MUST have JSON in SP2007.

Content Query Web Part: not strictly a client side technology, but it is so powerful out of the box that it deserved a mention. Combined with a bit of XSL it is truely one of if not the best web parts provided to us in SharePoint.

Demos!

And then it was time for the fun stuff. I considered recording all of the demo’s to place here but it wouldn’t have been as relevant without the accompanying dialogue, and I didn’t really want to record my own voice. I’ve settled for links to the inspiration behind each demo – if you have any questions feel free to ask and I will go into more detail.

Demo 1: Client Object Model & jQuery

This one came from a tweet I noticed a week before my presentation. Take a look at Using the SharePoint Client Model to populate a jQuery AutoComplete box by Douglas Leung. The delay you get before the autocomplete kicks in is because we’re waiting for SharePoint to load the relevant scripts before we bind the event.

Demo 2: SPServices & jQuery

Thankfully I dropped the right name in my presentation! It was Mark Rackley responsible for the inspiration behind An Easy to Use Content Slider. Sure his version isn’t as good as the one which featured some of the best the mighty Fremantle Dockers have to offer, but the general vibe of the slider is the same 😉

Demo 3: Content Query Web Part + XSL + jQuery

I’ve said all I need to say about this one in my post Using the Content Query Web Part and jQuery to create a staff desk locator so if you’re interested pop over to that post and take a look at the nuts and bolts holding it together. One point I did mention in the presentation is that the solution kind of grinds to a halt in the older versions of IE due to the struggles it has with larger-scale manipulation of the DOM.

Demo 4: REST services + Knockout + TMPL + jQuery slider + jQuery validation + jqPlot

Yes, there was a lot to this one. I did create this demo specifically for the session but my post on Applying the MVVM pattern to create SharePoint list-driven interactive tools using Knockout covers the jist of it. If not, just drop me a line.

Demo 5: SignalR

Definitely a topic which gets me pretty excited. The demo I showed was the same one I posted in Harnessing SignalR in SharePoint – complete with video! So check it out if you want a look under the hood at how we made that happen.

And that pretty much covers it. There were some great questions to follow the session, enough so that all of the left over SP Saturday ‘limited edition’ USB key’s were given away. I truely appreciated the opportunity to present again at the user group and was humbled by the turnout. I hope a few people were inspired to go out and try some of the technologies presented in their own SharePoint environments.

I’ll sign off with a couple of tweets I received after the session – one in particular that made the whole session worth while!

Harnessing SignalR in SharePoint

Note: For those interested in the implementation of SignalR in SharePoint 2013, view the latest post Harnessing SignalR in SharePoint 2013 (Office 365)

On the 1st of April Bil Simser wrote an article Introducing SharePointR. I happened to read it on the 3rd and as such it wasn’t until I got to the ‘quote’ by David Fowler that I tweaked to what was going on. It was a clever April Fools post and likely got a lot of people excited. For me, it piqued my interest. I’d heard of SignalR in passing but had yet to delve into it. Once I had, I have to say I got pretty excited myself. It’s super cool. I, along with one of my extremely talented colleagues Elliot Wood, went about getting SignalR up and running in a SharePoint environment.

SignalR is relatively new and as such the information available isn’t extensive, but what is out there is pretty good. There are a few examples you should check out right off the bat: Scott Hanselman’s Asynchronous scalable web applications with real-time persistent long-running connections with SignalR and Justin Schwartzenberger’s Learn how to use SignalR and Knockout in an ASP.NET MVC 3 web application to handle real-time UX updates were the two I found to start with. Both link off to a number of other valuable examples.

So what is SignalR? Essentially, it’s real-time client-server communication on the web. Without having to constantly poll or perform any refreshes, data on the page will ‘magically’ update in front of your eyes. The library is maintained on Github which gives you access to the latest code, issues and documentation at a central source. I’m not going to go too far into what it is, because with the excitement that’s being generated around this, plenty of other people are doing a far better job of it than what I could. I’ll skip straight to the fun stuff.

So if the examples are already out there, why can’t we just plug this straight into SharePoint?

The majority of examples which currently exist tend to host the hub and the client in the same project, and hence are hosted on the same domain. This works great, you can spin up your .NET 4.0 web application and everything will work smoothly. Only problem being SharePoint runs on the .NET 2.0 framework – you won’t be able to add the SignalR DLLs to your SharePoint project.

This however is not a deal-breaker. As long as your hub is hosted on a .NET 4.0 web application you can leverage SignalR in a simple HTML file with JavaScript, so surely that would be easy enough to plug into a SharePoint web part?

Firstly; it’s not exactly simple. It requires something called Cross-Domain Calling which I’ve since found out is a bit of a pain to get working across different browsers. This is where the information fell down a little, or at least was scattered around. I’ve read more StackExchange and Github Issues than I care to dig back up and link to for this article so excuse my lack of referencing here. One page I did come across which got me most of the way there was Thomas Krause’s Making Cross-Domain Calls in SignalR which summed it up pretty nicely, but still didn’t work in all browsers for me. But more on this later.

Secondly; even with all those issues resolved we still want a way to trigger SignalR to broadcast an update from SharePoint, and unless you’re doing that purely with client interaction in the UI, chances are that’s going to mean handling create, update and delete events via event receivers. Which brings us back to our first issue – these will be on the .NET 2.0 framework and won’t be able to reference the SignalR DLLs. So how do we get around this?

Essentially what is required is a bridge between SharePoint’s .NET 2.0 environment and a .NET 4.0 one. I liked the way Elliot termed this better: breaking the .NET barrier. My initial thoughts were hosting a WCF service and passing the information from the event receiver to that service to be broadcast by SignalR, and I still think that would be the ideal solution. Elliot however beat me to the implementation by making the leap via HTTP and posting to a handler sending the information via Query String, and for the purposes of the proof of concept (minimal data being transfered) this did the trick nicely.

With all the pieces of the puzzle in place, it’s time to implement our SignalR in SharePoint proof of concept. The idea is pretty simple – consider it a mini task-tracking system. Tasks will get added to the pile and others will be completed. The service quality manager will have a dashboard on their monitor open all day receiving live information on whether performance targets are being hit on a day-to-day basis. Let this be a glimpse into the power of what SignalR can achieve and let your imagination run wild on possible real-world implementations.

Step 1: Create the Hub

The Hub needs to be a .NET 4.0 web application. The majority of examples on the net state the first step of implementing any SignalR application is to use nuget to retrieve the SignalR DLLs and scripts. This is fine in theory, but while we were investigating our cross-browser issues (non-performance in Firefox and Chrome) Elliot noticed that the nuget version of SignalR is not the latest, therefore downloading the latest ZIP and re-referencing the DLLs is the way to go. In fact the ‘latest’ version from Github at the time of writing included another required DLL that the nuget version didn’t bring across – SignalR.Hosting.Common.dll. Others that you’ll want the updated version for (or at least the ones we used) include SignalR.dll, SignalR.Hosting.AspNet.dll and Newtonsoft.Json.dll.

The next step is to add an entry into the web.config file to allow the cross-domain calls. This requires adding the following snippet to the system.webServer node

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>

The final step is to add a class to your project to house the Hub

using SignalR.Hubs;

namespace SignalRHub
{
  public class SharePointHub : Hub
  {
    public void Send(string message)
    {
      // Call the addMessage method on all clients
      Clients.addMessage(message);
    }
  }
}

At this point all the extraneous components of the project can be removed so you’re left with the class, the web.config and packages.config. Just a note – the Send implementation is somewhat redundant as we’ll be calling the client script from the handler below rather than the hub itself – but it’s useful for testing.

Step 2: Create the HTTP Handler

The handler can exist in the same project created above and will essentially be the tool to receive the Query String data from SharePoint and broadcast it via SignalR to the clients. All credit here goes to Elliot for the concept, I’ve adapted it to match my SignalR implementation (I used a Hub, he used a Persistent Connection).

One major thing to point out is that the documentation currently offers this as the method of broadcasting over a Hub from outside of a Hub

using SignalR.Infrastructure;

IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<MyHub>();

This documentation however is only valid for the 0.4.0 build of SignalR you would get from nuget – the latest version establishes the IConnectionManager from another means

IConnectionManager connectionManager = Global.Connections;

The resulting code for the ProcessRequest is as follows

public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "text/plain";

    IConnectionManager connectionManager = Global.Connections;
    dynamic clients = connectionManager.GetClients<SharePointHub>();

    var payload = new
    {
        TasksOpenedToday = context.Request.Params["TasksOpenedToday"],
        TasksCompletedToday = context.Request.Params["TasksCompletedToday"],
        LightStatus = context.Request.Params["LightStatus"]
    };

    JavaScriptSerializer jss = new JavaScriptSerializer();
    var payloadJSON = jss.Serialize(payload);
    clients.addMessage(payloadJSON);
}

Step 3: Create the Event Receiver

There’s nothing particularly fancy going on here aside from posting to our HTTP handler we created in step 2. The rest of the code is just a simple event receiver bound to a task list. If you need help at this step then take a look at Walkthrough: Deploying a Project Task List Definition. We’ll need to override the ItemAdded, ItemUpdated and ItemDeleted events and add the following code

Broadcast(getPayload(properties));

private string getPayload(SPItemEventProperties properties)
{
    SPList list = properties.List;
    SPQuery query = new SPQuery();
    query.Query = "<Where><Eq><FieldRef Name='Created' /><Value Type='DateTime'><Today /></Value></Eq></Where>";
    SPListItemCollection items = list.GetItems(query);
    double tasksOpenedToday = items.Count;

    double tasksCompletedToday = 0;
    foreach (SPListItem item in items)
    {
        if (item["Status"].ToString() == "Completed") tasksCompletedToday++;
    }

    string colour = "RED";
    int percentage = (int)Math.Floor(tasksCompletedToday / tasksOpenedToday * 100);
    if (percentage >= 70) colour = "GREEN";
    else if (percentage >= 50) colour = "ORANGE";

    return string.Format("?TasksOpenedToday={0}&TasksCompletedToday={1}&LightStatus={2}",
        tasksOpenedToday.ToString(),
        tasksCompletedToday.ToString(),
        colour);
}

private void Broadcast(string Payload)
{
    WebRequest request = HttpWebRequest.Create(string.Concat("http://server:port/SharePointRProxyHandler.ashx",Payload));
    WebResponse response = request.GetResponse();
}

Step 4: Create the Web Part

We’re on the home stretch. We’ve got an event receiver posting data to an HTTP handler, which is in turn broadcasting that via SignalR. The only thing left to do is create the client to listen out for that broadcast. This can essentially be HTML and JavaScript and as such doesn’t really need to be a web part at all, but I’ll be creating a visual one in the interests of effective deployment.

It’s here that we need to think back to when we retrieved the latest files from Github for SignalR. You’ll now need to grab the latest JavaScript files from that package and store them in SharePoint so we can reference them in our web part. You’ll also need to grab and store Knockout.

There’s a few differences you’ll need to consider compared to the generic example provided for implementing the SignalR client for hub communication. Firstly, instead of referencing /signalr/hubs in your script src, you’ll need to reference it in the location in which it exists

<script src="http://server:port/signalr/hubs" type="text/javascript"></script>

Secondly, you’ll need to force jQuery to support cross-site scripting

jQuery.support.cors = true; //force cross-site scripting

Third, your hub URL will also need to point to the relevant location

$.connection.hub.url = 'http://server:port/signalr';

Your Hub name will need to be in camel-case (which isn’t clear in the example seeing it’s all lower case)

var sharePointHub = $.connection.sharePointHub;

Your client handler will need to parse the JSON sent in seeing it’s not a simple string

sharePointHub.addMessage = function(json) {
var data = JSON.parse(json);
};

And finally you’ll need to pass a couple of options into the call to start the connection to your Hub

$.connection.hub.start({ transport: 'longPolling', xdomain: true });

That pretty much covers it. Add in some initial population of the viewModel server side and the necessary Knockout bindings and the web part is ready to be deployed. You can see the final web part (minus the basic server side population of variables) below

<style type="text/css">
    .dashboard-item { margin-bottom: 10px; }
    .dashboard-label { font-weight: bold; }
    #LightStatus { width: 50px; height: 50px; }
    .RED { background-color: Red; }
    .ORANGE { background-color: Orange; }
    .GREEN { background-color: Green; }
</style>

<script src="/SiteAssets/jquery-1.7.min.js" type="text/javascript"></script>
<script src="/SiteAssets/json2.min.js" type="text/javascript"></script>
<script src="/SiteAssets/jquery.signalR.min.js" type="text/javascript"></script>
<script src="/SiteAssets/knockout-2.0.0.js" type="text/javascript"></script>
<script src="http://server:port/signalr/hubs" type="text/javascript"></script>

<div class="dashboard-item"><span class="dashboard-label">Tasks Opened Today: </span><span data-bind="text:tasksOpenedToday"></span></div>
<div class="dashboard-item"><span class="dashboard-label">Tasks Completed Today: </span><span data-bind="text: tasksCompletedToday"></span></div>
<div id="LightStatus" data-bind="attr: { class: lightStatus }"></div>

<script type="text/javascript">
    var viewModel = {
        tasksOpenedToday: ko.observable(),
        tasksCompletedToday: ko.observable(),
        lightStatus: ko.observable()
    };

    $(document).ready(function () {
        jQuery.support.cors = true; //force cross-site scripting

        $.connection.hub.url = 'http://server:port/signalr';

        // Proxy created on the fly
        var sharePointHub = $.connection.sharePointHub;

        // Declare a function on the chat hub so the server can invoke it
        sharePointHub.addMessage = function (json) {
            // Update viewModel values here
            var data = JSON.parse(json);
            viewModel.tasksOpenedToday(data.TasksOpenedToday);
            viewModel.tasksCompletedToday(data.TasksCompletedToday);
            viewModel.lightStatus(data.LightStatus);
        };

        //Populate viewModel via ASP.NET and bind to Knockout
        viewModel.tasksOpenedToday("<%= InitialTasksOpenedToday %>");
        viewModel.tasksCompletedToday("<%= InitialTasksCompletedToday %>");
        viewModel.lightStatus("<%= InitialColour %>");
        ko.applyBindings(viewModel);

        // Start the connection
        $.connection.hub.start({ transport: 'longPolling', xdomain: true });
    });
</script>

So how does it look in the end? You be the judge..

So there you have it. I’d be surprised if there’s anyone out there that didn’t think this was pretty awesome. With any luck this will inspire you to go forth and make your own SharePoint implementations with SignalR – I’m looking forward to seeing what will be achieved with this excellent technology. Already Elliot has taken up the challenge to bring Bil’s ‘April Fools’ concept to life, it’s the kind of inspiring functionality that makes you want to go out and experiment. Just one last note – for anyone fearful of how this somewhat new technology would go in an enterprise environment, take a look at the video C#5, ASP.NET MVC 4, and asynchronous Web applications.

Using the Content Query Web Part and jQuery to create a staff desk locator

I recently took it upon myself to provide an alternative to the staff desk locator that was due to go to production in the near future. The back-story is that a requirement existed to locate staff on a floor map to assist with the move to a new office complex. The original concept was created by another developer, but with multiple requirement changes and multiple developers tweaking the original solution, the end result was far from perfect. It only worked completely in IE7 standards browser mode and some of the functionality performed in a less than ideal fashion – think scroll bars to navigate around a zoomed in map amongst other imperfections.

Now the solution I present here is also far from perfect, I have no illusions about that. I debated whether to post about it at all until I had a chance to optimse and refine it seeing that what exists here is essentially a protoype, however I believe it is in a complete enough state to provide some benefit or interest to people out there. If anyone can propose some better ways of achieving some of the functionality demonstrated here I’d be more than interested in hearing it. The first half of the post will explain the journey, so if you’re not interested in what drove the implementation decisions along the way, feel free to skip down to the solution.

The Journey

The concept I was going for was something similar to Google Maps; your standard zoom, move, mouse-wheel and drag functionality. The most attractive option I could find was a paid solution called MapBox which can be seen in their various featured map demos. I didn’t think a paid solution would be an option and besides, I wanted to see if I could create something similar myself. I found a jQuery plugin also called MapBox documented at Mapbox: Zoomable jQuery Map Plugin which definitely appeared to get me half way there. The only remaining challenge was to plot the points on the map and get it driven via SharePoint.

Plotting the points definitely proved challenging. I went through a few iterations as I lead myself down some wrong paths – the joys of experimentation. I went so far as to get everything adjusting correctly from a movement point of view (thinking I was better off starting easy and leaving the complicated functionality for later) only to have it breaking during zoom. It wasn’t until I further investigated the zoom requirements and changed the way in which I was approaching the challenge that I realised the work I had done to adjust for movement was redundant and was handled by the plugin. Lovely.

I eventually got the locations remaining in place during zooming however this exposed its own issues; the pins representing each location didn’t adjust in size as the map did, resulting in a very strange looking experience. It is here where my solution is least elegant – I essentially created multiple CSS classes and multiple sized images and dynamically changed the class of the elements as I zoomed in and out (using the width of the parent div to differentiate). I couldn’t simply scale the image as I was using background images and divs rather than image elements (for reasons based on issues with the previous solution and brief experimentation in this one). This exposed even more gotchyas – it appears different browsers round decimal values in different ways (there were variations around rounding, floor or ceiling). I also noticed that while on most occassions the plugin would zoom to fixed width sizes, occassionally it would result in completely unexpected values which I needed a safety net CSS definition for. I love Internet Explorer. The CSS is hence a pretty ugly beast, but for the most part it works.

The next step was adding some hover over functionality to display the staff members information. This was relatively simple and just leveraged the onMouseOver event and some jQuery for each plotted element. The search functionality was a little more complex. I had witnessed the functionality I desired during my research, present on the Rock ‘n’ Roll Metro Map, which I dug deeper into and discovered it was being achieved via the animate function of the jQuery UI plugin. A couple of equations and experimentation later to determine how I could establish my scroll-to points and it was all humming along nicely.

The final challenge was getting it all into SharePoint. This functionality was to sit on the intranet which had been created as a codeless solution, therefore rather than rendering what I needed via the API in a web part I decided to stick with the theme and leverage the Content Query Web Part (CQWP), drawing out of a custom list. The previous solution had done the same so it wasn’t too difficult with a few tweaks to achieve what I desired. A couple of the gotchyas encountered included ensuring the CommonViewFields were set in the CQWP (exporting and importing the web part was used to achieve this) and rediscovering that for a column to exist to filter on in the web part it needed to be a site column, not just a list column. One last little hurdle that needed to be jumped included how the parts sat on the page with the master page being used – and this differed between browsers (Did I mention how much I love IE?). Seeing this probably had a lot to do with the custom master page being used I’ll leave the specifics out in the solution below, however just know that it is something that may need to be considered.

The Solution

Part 1: The list to hold the data

This was a custom list with some custom columns. Note that the IsAResource column is a site column. The -1000 X and Y values reflect a staff member that does not exist in the building.

Part 2: Header.xsl

The templates in Header.xsl map to the Group Style settings selected in the Content Query Web Parts. They render once to open and close the items which are rendered in between. The below templates were defined.

  <xsl:template name="DeskfinderSelectName" match="*[@GroupStyle='DeskfinderSelectName']" mode="header">
    <script type="text/javascript">document.write('<![CDATA[<div id="SearchByName"><span class="SearchLabel">Search by Name:</span><select onchange="FindUser(this.value);"><option value="">Please select an option...</option>]]>')</script>
  </xsl:template>
  <xsl:template name="DeskfinderSelectNameClose" match="*[@GroupStyle='DeskfinderSelectName']" mode="footer">
    <script type="text/javascript">document.write('<![CDATA[</select></div>]]>')</script>
  </xsl:template>
  <xsl:template name="DeskfinderSelectResource" match="*[@GroupStyle='DeskfinderSelectResource']" mode="header">
    <script type="text/javascript">document.write('<![CDATA[<div id="SearchByResource"><span class="SearchLabel">Search by Resource:</span><select onchange="FindUser(this.value);"><option value="">Please select an option...</option>]]>')</script>
  </xsl:template>
  <xsl:template name="DeskfinderSelectResourceClose" match="*[@GroupStyle='DeskfinderSelectResource']" mode="footer">
    <script type="text/javascript">document.write('<![CDATA[</select></div>]]>')</script>
  </xsl:template>
  <xsl:template name="DeskfinderPin" match="*[@GroupStyle='DeskfinderPin']" mode="header">
    <script type="text/javascript">document.write('<![CDATA[<div class="StaffLocations">]]>')</script>
  </xsl:template>

The most important part to note here is that the DeskfinderPin doesn’t have an associated footer. This was a bit of a gotchya – the browsers closed off the div anyway but if I included the footer, the

<script type="text/javascript">document.write('<![CDATA[</div>]]>')</script>

node was appearing within the div which caused issues when I went to extract the HTML via jQuery and pump it into other locations. It worked fine without the footer so it was removed.

Part 3: ItemStyle.xsl

The templates in ItemStyle.xsl map to the Item Style settings selected in the Content Query Web Parts. They’re rendered for each item in the query. The below templates were defined.

  <xsl:template name="DeskfinderSelectOption" match="Row[@Style='DeskfinderSelectOption']" mode="itemstyle">
    <xsl:variable name="DisplayTitle">
      <xsl:call-template name="OuterTemplate.GetTitle">
        <xsl:with-param name="Title" select="@Title"/>
        <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
      </xsl:call-template>
    </xsl:variable>
    <option value="{@ID}">
      <xsl:value-of select="$DisplayTitle"/>
    </option>
  </xsl:template>
  <xsl:template name="DeskfinderPin" match="Row[@Style='DeskfinderPin']" mode="itemstyle">
    <xsl:variable name="DisplayTitle">
      <xsl:call-template name="OuterTemplate.GetTitle">
        <xsl:with-param name="Title" select="@Title"/>
        <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="starts-with(@Location,'L2')">
		<div class="pin2" style="left:{@X}px; top:{@Y}px;" onMouseOver="DisplayDetails(this);" data-id="{@ID}" data-xpos="{@X}" data-ypos="{@Y}" data-name="{$DisplayTitle}" data-phone="{@Phone}" data-email="{@Email}"></div>
      </xsl:when>
      <xsl:otherwise>
        <div class="pin3" style="left:{@X}px; top:{@Y}px;" onMouseOver="DisplayDetails(this);" data-id="{@ID}" data-xpos="{@X}" data-ypos="{@Y}" data-name="{$DisplayTitle}" data-phone="{@Phone}" data-email="{@Email}"></div>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

It is important to note that all of the custom columns referenced in the template (X, Y, Email, Phone) need to appear in the CommonViewFields element of the Content Query Web Part to be recognised. This can be done by exporting the web part, manually editing the .webpart file to add those columns and importing it back onto the page.

Part 4: The resources

CSS – the CSS used was extended from that used for the MapBox demo with some slight tweaks to those definitions. A little more was added to style the drop down menus and staff information. The majority of CSS was to style the pins mapping to the locations of staff members. As mentioned before, this was a very bloated solution and far too much to include in this post. I’ll try and summarise.

There were essentially 4 groups of styles – level 2 pins, level 3 pins, level 2 ‘located’ pins and level 3 ‘located’ pins. These were seperated into whether they appeared in the firstLayer, secondLayer, thirdLayer or fourthLayer. They were further seperated into sizes mapping to the width of the parent div (the reason for 2 sizes 1 pixel apart is due to the rounding issue I mentioned earlier). Here are a few randomly selected examples:

#firstLayer .pin2
{
	position: absolute;
	width: 10px;
	height: 9px;
	background: url(../PublishingImages/pin2-size709.gif);
}

#secondLayer .pin2.found.size974, #secondLayer .pin2.found.size975
{
	width: 16px;
	height: 16px;
	background: url(../PublishingImages/found2-size975.gif);
}

#fourthLayer .pin3 {
	position: absolute;
	width: 30px;
	height: 27px;
	background: url(../PublishingImages/pin3-size2126.gif);
}

#fourthLayer .pin3.size1505, #fourthLayer .pin3.size1506 {
	width: 21px;
	height: 19px;
	background: url(../PublishingImages/pin3-size1506.gif);
}

You get the idea. Perhaps look into an alternative solution!

Images – The images included the 4 different sized floor map images, the map control image and 100 different pin images to match the CSS styles.

JavaScript – The javascript leveraged that provided with the MapBox demo with the following adjustments.

Functionality to populate the staff information:

function DisplayDetails(person)
{
	$("#staffName").text($(person).attr("data-name"));
	$("#staffPhone").text($(person).attr("data-phone"));
	$("#staffEmail").html('<a href="mailto:' + $(person).attr("data-email") + '">' + $(person).attr("data-email") + '</a>');
}

Functionality to find a given user or resource:

function FindUser(id)
{
	var limitX = 0, limitY = 0, mapWidth = $(".mapwrapper").width(), mapHeight = $(".mapwrapper").height(),
	nodeWidth = $(".current-map-layer").width(), nodeHeight = $(".current-map-layer").height();

	if(mapWidth < nodeWidth) limitX = mapWidth - nodeWidth;
	if(mapHeight < nodeHeight) limitY = mapHeight - nodeHeight;

	var element = $(".current-map-layer div[data-id='" + id + "']");
	var left = (parseInt($(element).css("left")) * -1) + (parseInt($(".mapwrapper").css("width")) / 2);
	var top = (parseInt($(element).css("top")) * -1) + (parseInt($(".mapwrapper").css("height")) / 2);

	left = (left > 0) ? 0 : left;
	left = (left < limitX) ? limitX : left;
	top = (top > 0) ? 0 : top;
	top = (top < limitY) ? limitY : top;

	var width = parseInt($(".current-map-layer").css("width"));
	if ($("div[data-id='" + id + "']").attr("class").indexOf("pin2") == -1)
		$("div[data-id='" + id + "']").attr("class", "pin3 found size" + width);
	else
		$("div[data-id='" + id + "']").attr("class", "pin2 found size" + width);

	$(".current-map-layer").animate({top: top,left: left}, {easing: "easeOutQuart"});
	DisplayDetails(element);
}

The important thing to note here is that extra code was required to ensure the map wasn’t moved ‘out of viewport’. Thankfully, the MapBox JavaScript already included that logic for its drag functionality so I just leveraged that.

Document Ready functionality:

$(document).ready(function() {
	$("#firstLayer,.mapcontent").html($(".StaffLocations").html());

	$("#viewport").mapbox({
		mousewheel: true,
		layerSplit: 8//smoother transition for mousewheel
	});
	$(".map-control a").click(function() {//control panel
		var viewport = $("#viewport");
		//this.className is same as method to be called
		if(this.className == "zoom" || this.className == "back") {
			viewport.mapbox(this.className, 2);//step twice
		}
		else {
			viewport.mapbox(this.className);
		}
		return false;
	});
})

The first line is the most important – this is where the HTML is taken from the hidden div rendered by our Content Query Web Part and injected into the necessary locations. The rest of the code is the same that existed for the MapBox demo.

Custom changes to the _zoom function (insert above return movement; line)

/* Custom changes */
var xChange = (totalWidth == undefined) ? 1 : totalWidth / $("#firstLayer").width();
var yChange = (totalHeight == undefined) ? 1 : totalHeight / $("#firstLayer").height();

$(".pin2,.pin3").each(function(index, value) {
	$(this).css("left", ($(this).attr("data-xpos") * xChange));
	$(this).css("top", ($(this).attr("data-ypos") * yChange));

	var width = parseInt($(".current-map-layer").css("width"));
	if ($(this).attr("class").indexOf("pin2") == -1)
	{
		if ($(this).attr("class").indexOf("found") == -1)
			$(this).attr("class", "pin3 size" + width);
		else
			$(this).attr("class", "pin3 found size" + width);
	}
	else
	{
		if ($(this).attr("class").indexOf("found") == -1)
			$(this).attr("class", "pin2 size" + width);
		else
			$(this).attr("class", "pin2 found size" + width);
	}
});
/* End Custom changes */

Part 5: Referencing the resources

This was a chromeless Content Editor Web Part referencing the required resources:

<link href="../Documents/master.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script>
<script type="text/javascript" src="../Documents/mousewheel.js"></script>
<script type="text/javascript" src="../Documents/mapbox.js"></script>

Take note the version of jQuery referenced – it’s not the latest, but was required for MapBox to function correctly.

Part 6: The search box

This was a chromeless Content Query Web Part pointing to the list in Part 1. It was set to show items of type ‘Custom List’ and filtered where ‘IsAResource is equal to 0’. The Presentation was grouped by <Site> (to ensure the group element was rendered) and sorted by ‘Title ascending’. The ‘Limit the number of items to display’ was turned off. The Group Style was set to ‘DeskfinderSelectName’ and Item Style set to ‘DeskfinderSelectOption’.

To include a Search by Resource box as well as a Search by Name box, the steps above can be followed, however the filter value should be 1 and the Group Style should be set to ‘DeskfinderSelectResource’.

Part 7: The map, associated controls and staff information

This was a chromeless Content Editor Web Part which essentially rendered the HTML which existed in the MapBox demo with a few minor tweaks.

<div class="mapwrapper">
	<div id="viewport" style="overflow: hidden; position: relative;">
		<div id="firstLayer" class="current-map-layer" style="background: url('Images/FloorImage1.png') no-repeat scroll 0% 0% transparent; width: 709px; height: 631px; position: absolute; display: block; left: 0px; top: 0px;">
		</div>
		<div id="secondLayer" style="height: 947px; width: 1063px; position: absolute; display: none;">
			<img style="width: 100%; position: absolute; left: 0pt; top: 0pt;" src="Images/FloorImage.png" alt="">
			<div style="position: absolute; left: 0pt; top: 0pt; background: none repeat scroll 0% 0% white; opacity: 0;" class="map-layer-mask"></div>
			<div style="position: absolute; top: 0pt; left: 0pt;" class="mapcontent">
			</div>
		</div>
		<div id="thirdLayer" style="height: 1262px; width: 1417px; position: absolute; display: none;">
			<img style="width: 100%; position: absolute; left: 0pt; top: 0pt;" src="Images/FloorImage.png" alt="">
			<div style="position: absolute; left: 0pt; top: 0pt; background: none repeat scroll 0% 0% white; opacity: 0;" class="map-layer-mask"></div>
			<div style="position: absolute; top: 0pt; left: 0pt;" class="mapcontent">
			</div>
		</div>
		<div id="fourthLayer" style="height: 1893px; width: 2126px; position: absolute; display: none;">
			<img style="width: 100%; position: absolute; left: 0pt; top: 0pt;" src="Images/FloorImage.png" alt="">
			<div style="position: absolute; left: 0pt; top: 0pt; background: none repeat scroll 0% 0% white; opacity: 0;" class="map-layer-mask"></div>
			<div style="position: absolute; top: 0pt; left: 0pt;" class="mapcontent">
			</div>
		</div>
	</div>
	<div class="map-control">
		<a href="#left" class="left">Left</a>
		<a href="#right" class="right">Right</a>
		<a href="#up" class="up">Up</a>
		<a href="#down" class="down">Down</a>
		<a href="#zoom" class="zoom">Zoom</a>
		<a href="#zoom_out" class="back">Back</a>
	</div>
	<div class="staff-information">
		<div><span class="staff-info-label">Name: </span><span id="staffName"></span></div>
		<div><span class="staff-info-label">Phone: </span><span id="staffPhone"></span></div>
		<div><span class="staff-info-label">Email: </span><span id="staffEmail"></span></div>
	</div>
</div>

Note that the images and sizes were adjusted respectively and each layer div was given an id so I could reference them via both jQuery and CSS. The staff-information div was also included to present the staff information.

Part 8: The staff locations

This was a chromeless Content Query Web Part pointing to the list in Part 1. It was set to show items of type ‘Custom List’, grouped by <Site> and the ‘Limit the number of items to display’ was turned off. The Group Style was set to ‘DeskfinderPin’ and Item Style set to ‘DeskfinderPin’.

Note that the contents rendered here were within a div hidden by the CSS – it’s main purpose was to render the data once, then jQuery injected it into the locations required.

The End Result

An image doesn’t really do it justice, but for the purposes of completing the article, the end result looks something like that depicted below.

Either that or you can watch a video below on how the functionality works.

Applying the MVVM pattern to create SharePoint list-driven interactive tools using Knockout

MVC, MVP, MVVM. They’re common acronyms that any developer worth their salt would have at least a vague understanding of what they mean and entail. I remember doing an MVC hands-on lab at Tech-Ed 2-3 years ago in ASP.NET for my own curiosity, see what all the fuss was about. Having been in the SharePoint realm for the past 3 and a half years though it’s something that I never thought would have had much application in my day to day job. Surely it was just left to the ASP.NET crowd, nothing to do with SharePoint. How wrong I was.

To be honest this article could go on forever with explanations and definitions, examples and possibilities. What I’ve since found when researching for this post however is that the information available on the net is actually pretty comprehensive. Unsure what the difference is between the various view-model design patterns? Take a look at Niraj Bhatt’s post on MVC vs. MVP vs. MVVM. In fact somewhat to my surprise there are even a number of quality SharePoint related posts in regards to implementing MVVM. The majority of them relate to MVVM in Silverlight hosted in SharePoint – you can read up on a great article series by Andrew Connell regarding Silverlight, MVVM & SharePoint. There’s a quality post by Shailen Sukul regarding Applying MVVM Principles to SharePoint 2010 Development. There’s even a post on Using MVVM with Office365 and SharePoint 2010 REST API by Sahil Malik.

Sahil’s post is somewhat similar to my own. The reason being, we’ve both implemented the MVVM pattern using Knockout – a library to ‘Simplify dynamic JavaScript UIs by applying the Model-View-View Model (MVVM) Pattern’. There is a difference however – where Sahil had the luxury of using the SharePoint 2010 REST APIs which go hand in hand perfectly for this purpose, my task was to implement it in MOSS 2007.

The tools created using this method are a part of the suite of tools that exist on the Career Centre Portal including the Resume Builder, Slider tools, Choices and Hands On, Career Possibility Generator, Card Sort tools and multiple forms throughout the site. A number of developers have played a large part in the creation of these tools and forms including Paul Maddox, Elliot Wood, Lay Hooi Tan and myself. Some are SharePoint list-driven and others are CRM-driven. For the purpose of this article I’ll focus on the Career Possibility Generator.

So how does Knockout work? It could really do with a post of its own. I’d suggest reading the documentation for a full explanation of how it works and its capabilities. Trying to keep it simple however, essentially you use data-binds to map UI elements to values stored in a data model. If you define the value as ‘observable’ you ensure that updates to the UI are reflected in the model and vice-versa. Knockout also harnesses the jQuery.tmpl template engine to render repeatable blocks of code.

It’s driven really well by using a JSON object as the underlying data model. This is why it ties in so nicely with the SharePoint 2010 REST services – you can quite easily pull back a JSON object instead of an Atom response via those services and use it within the Knockout framework. To get around this missing functionality an ASHX handler was created to return the JSON object for us. This is quite easy to do – the generic shell of the handler would look something like that shown below:

public class MyHandler : IHttpHandler
{
    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        List<CloudListItem> items = new List<CloudListItem>();
        // Retrieve items from SharePoint and populate into the generic list
        JavaScriptSerializer ser = new JavaScriptSerializer();
        string jsonObject = ser.Serialize(items);
        context.Response.Clear();
        context.Response.ContentType = "application/json";
        context.Response.Write(jsonObject);
    }

    #endregion
}

public class CloudListItem
{
    // Various properties to return in the JSON object
}

The next step is to bind the returned JSON object to Knockout and in turn allow Knockout to bind the values to the UI using applyBindings.

<script id="Occupations_Cloud_Tmpl" type="text/x-jquery-tmpl">
    <li class="cloud${Count}"><a target='_blank' href="${PageUrl}">
      <label data-bind="text:Title"></label>
    </a></li>
</script>

<div id="tag-cloud">
    <ul id="occupations-cloud"
        data-bind="template: {name: 'Occupations_Cloud_Tmpl', foreach: viewModel.Cloud}">
    </ul>
</div>

<script type="text/javascript">
    $.getJSON('/_controltemplates/ccosdportal/handlers
               /ListToJsonHandler.ashx?ListName=NormalisedKSA
               &Web=occupations', function(data){
        //viewModel.Cloud = ko.mapping.fromJS(data);
        viewModel.Cloud = data;
        $.each(viewModel.Cloud, function(index, value) {
            value.Count = ko.observable(0);
        });
        ko.applyBindings(viewModel.Cloud);
    });
</script>

Essentially what the above code will result in is an unordered list of Occupation profile links with a distinct class applied. I’ve left in the comment above to demonstrate one of the gotchyas I encountered using Knockout. By using ko.mapping.fromJS() you’re essentially ensuring that all properties within the JSON object are observable. This causes some memory issues if the object is too large and can result in the following warning in Internet Explorer browsers:

The only thing left to explain here is how the ‘observable’ functionality works. The tool itself includes a number of checkboxes which users can select which in turn drives how the cloud looks on the screen. The checkboxes are mapped in SharePoint lists to various occupations and thus when a checkbox is selected that maps to a given occupation, the class applied to the relevant list item is modified to reflect that link. As you may have noticed, the only observable property in the object is the ‘Count’ which is used in the list item class=”cloud${Count}”. By incrementing the Count value in the model, the UI is updated to reflect the new value and hence changes the class of the list item, changing the way it displays on the screen.

function AdjustCloud(checkbox) {
    var addValue = -1;
    if (checkbox.checked) addValue = 1;

    $.getJSON('/_controltemplates/ccosdportal/handlers
               /ListToJsonHandler.ashx?ListName=OccupationKSA
               &Web=occupations&KSAID=' + checkbox.value, function(data){
    if (data.length > 0)
    {
        $.each(data, function(ind, dtwd) {
            var dtwdVal = dtwd.DTWDANZSCO;
            $.each(viewModel.Cloud, function(index, value) {
              var clouddtwdVal = value.DTWDANZSCO;
              if (clouddtwdVal == dtwdVal)
                if (((viewModel.Cloud[index].Count() + addValue) >= 0) &&
                   ((viewModel.Cloud[index].Count() + addValue) <= 9))
                       viewModel.Cloud[index].Count(viewModel.Cloud[index].Count() + addValue);
            });
        });

         HideItems();
    }
    });
}

A visual example of the tool can be seen below (click for the full-sized image).

If you take the time to look at the Career Possibility Generator and all the tools throughout the Career Centre Portal you’ll get an appreciation of how powerful it is to leverage the MVVM pattern via Knockout to create highly interactive applications within SharePoint.