SharePoint Branding Best Bets (SPSRIC2013)

Diving into the world of SharePoint branding can be a real headache. What is the best practice for deploying my branding? How do I select a design firm? What is the impact of mobile devices and how do I ensure cross-browser compatibility? What are the new branding tools available in SharePoint 2013? These are all common questions that must be answered during the course of branding efforts. In this session we’ll look at the various aspects of SharePoint branding, and common pitfalls to look out for during your next branding project.

User Profile Services via Client Object Model

I was recently thinking through ways to help drive adoption of an intranet and engage users to explore the various capabilities of the system. One thought was to alert users if they have not supplied a profile photo, or a short bio in their MySite. Sure we could do this with web services, but I thought I'd figure out how to do it with the client object model with ECMAScript. After a lot of searching, I kept finding blog posts that said I was out of luck and there was no way to query User Profiles via the client object model. At a high level, that assessment is correct, but what we can do is query the user info list of the current site.

When I first started this, none of the test users in my lab had a profile picture, so I added one and found that my script kept returning a null object instead of my picture. The hidden Easter egg here is that the site's user info list isn't updated in real time. If you head over to Central Administration -> Monitoring -> Review job definitions and dig down the list you'll find a job called "User Profile Service Application - User Profile to SharePoint Full Synchronization". This timer job will execute every hour and synchronize the properties of your User Profile Services to the site user info list, so the picture that was set on the MySite is now accessible via the user info list.

First things first, we need to find out the ID of the user that's currently viewing the page (this is the physical numerical record of the user in the site's user info list, not the login username). Credit to Mike Oryszak (@next_connect) and his blog post on using the status bar to display active workflows for getting me in the right direction on this piece. We'll start out by grabbing the current web context and fire off that query. If the query is successful in executing, it'll call the onUserSuccessMethod function.

var context = null;
var web = null;
var curUser = null;

function getUser() {
    context = new SP.ClientContext.get_current();
    web = context.get_web();
    curUser = web.get_currentUser();
    curUser.retrieve();
    context.load(web);
    context.executeQueryAsync(Function.createDelegate(this, this.onUserSuccessMethod), Function.createDelegate(this, this.onFailureMethod));
}
Now that we have our context we'll create a variable called user and assign it to the current user object, and call our loadProfile function to do the profile query.
function onUserSuccessMethod(sender, args) {
    var user = web.get_currentUser();
    loadProfile();
}
The loadProfile function defines the user info list, builds the CAML query to get the record for the current user, and fires that query off. If the query is successful in executing, it'll call the onProfileSuccessMethod function.
function loadProfile() {
    context = SP.ClientContext.get_current();
    web = context.get_web();
    userInfoList = web.get_siteUserInfoList();
    camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name='ID'/><Value Type='Number'>' + curUser.get_id() + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>');
    this.listItems = userInfoList.getItems(camlQuery);
    context.load(listItems);
    context.executeQueryAsync(Function.createDelegate(this, this.onProfileSuccessMethod), Function.createDelegate(this, this.onFailureMethod));
}
Now that we have the result set from our query (in the form of a listItems object), all we need to do is grab the first (and only item), this will be on index 0 in the array, and analyze the picture field to see if there's a value there or not. If there is a picture object, we'll grab the Url to it and save it in a new variable (pictureURL) just in case we want it. If there is no picture object, we'll fire off a call to SP.UI.Status to add a new status bar telling the user they don't have a photo, with a link to their profile where they can add one.
function onProfileSuccessMethod(sender, args) {
    var item = listItems.itemAt(0);
    var picture = item.get_item('Picture');
    if (picture) {
        var pictureURL = picture.get_url();
    } else {
        noPicture = SP.UI.Status.addStatus('Profile Photo', 'You have not added a profile photo to your account. <a href='http://mysites/person.aspx'>Add one now!</a>');
        SP.UI.Status.setStatusPriColor(noPicture, 'blue');
    }
}
You'll notice that both getUser() and loadProfile() have references to an onFailureMethod function, in the event that our query fails. This will be a simple function to just alert our error.
function onFailureMethod(sender, args) {
    alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace());
}
Now that we've got all of our functions written, all we need is a simple call to our getUser() function (after the core SharePoint JavaScript has loaded, of course).
ExecuteOrDelayUntilScriptLoaded(getUser, "sp.js");
Putting it all together: My "Michael Greene" account has a user profile image, so we just see normal SharePoint with no profile photo alerts. My "Setup Account" account does not have a profile image, so SharePoint prompts us that we should add a photo. Due to the fact that we have to wait on the timer job to run and update the user info list with the new photo, it's possible that the user could add a photo then still see the prompt telling them they haven't. In a true application of this, we could add a condition to check the Modified time of the user info list record to see if the record has been "updated" in the last hour, to avoid that false positive. Only if the record has been updated within the hour and has no photo, should we alert the user.

Update: June 13, 2012
Andrew made a great observation in the comment below, that the current user Id is available out of the box via the _spUserId JavaScript variable. This allows us to optimize this script by eliminating the call to the profile to get the current user's Id. This revised script is much more efficient and should supersede what's above. Thanks Andrew!
<script type="text/ecmascript" language="ecmascript">
    ExecuteOrDelayUntilScriptLoaded(loadProfile, "sp.js");

    var context = null;
    var web = null;

    function loadProfile() {
        context = SP.ClientContext.get_current();
        web = context.get_web();
        userInfoList = web.get_siteUserInfoList();
        camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name='ID'/><Value Type='Number'>' + _spUserId + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>');
        this.listItems = userInfoList.getItems(camlQuery);
        context.load(listItems);
        context.executeQueryAsync(Function.createDelegate(this, this.onProfileSuccessMethod), Function.createDelegate(this, this.onFailureMethod));
    }

    function onProfileSuccessMethod(sender, args) {
        var item = listItems.itemAt(0);
        var picture = item.get_item('Picture');
        if (picture) {
            var pictureURL = picture.get_url();
        } else {
            noPicture = SP.UI.Status.addStatus('Profile Photo', 'You have not added a profile photo to your account. <a href='http://mysites/person.aspx'>Add one now!</a>');
            SP.UI.Status.setStatusPriColor(noPicture, 'blue');
        }
    }

    function onFailureMethod(sender, args) {
        alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace());
    }
</script>

Delivering SharePoint Success

We just finished wrapping up a great event with the Triangle SharePoint User Group focused on the project management side of SharePoint adoption and deployment. Special thanks to Dux Raymond Sy (@meetdux) for coming down to Raleigh for this special event. The Triangle SharePoint User Group meets on the first Tuesday of every month, and additional details about meeting topics and group information can be found on our website, or via our LinkedIn group.

Dreaded Collaboration

There's an interesting article titled "Why Some People 'Dread' Collaboration" in the September 6th issue of Information Weekly. The author discusses the link between the slow adoption of collaboration technology and the inherent flaws in strategy and governance. The article cites a survey in which over 500 executives were polled on their collaboration strategy. 80% of those executives felt that enterprise-wide collaboration was key to the companies success, and 75% of them planned to increase use of collaboration and communications tools in the coming year. On the flip side, a similar survey showed that the actual adoption of collaboration platforms tends to lag behind the initial deployment by five to eight quarters-pretty staggering given the current emphasis and investment in collaboration technology. Do we really want to have to wait that long to realize the investments of our shiny new collaboration platform?

So what's the reason behind this lag? Enter the mystical uncontrollable beast of governance and strategy. The author runs through a particular example in which the corporate direction to utilize collaboration tools is so downright impractical that the end users develop a sort of hatred for the technology; the "put everything you do  in a wiki" strategy.

If people use wikis-or any other collaboration tool-just because they're compelled to do so, they'll get lost in the white noise of misplaced communications. If users back away, it's not necessarily because they're hidebound or anti-social, or because the technology is inherently flawed (wikis are terrific for many things), but because they're not realizing the value. Information Week, 9/6/2010, "Why Some People 'Dread' Collaboration"

We've all been part of deployments where the goals of the new tools aren't well communicated to the end users. Without that strategic component of a new system deployment, it's often an uphill battle to make your users see the potential and impact of a new tool set. It's not that you have to "sell" the tools to your users, but you have to do more than give everyone access and tell them to use it. Sometimes people in the IT world assume that everyone thinks like people in the IT world, and that's certainly not the case. We look at SharePoint as an example, and immediately start looking at antiquated mainframe systems and countless other business systems and tools that we can integrate into a single platform. That's great, that's system consolidation (and often virtualization), and that's a time and money saver for today's enterprise businesses. That said, it's also scary to the end user who's been trained and educated in the existing tools, and been using them for a good chunk of their career.

A clear governance model, appropriate user training, and appropriate communication are critical to the deployment of any new system (especially those in the collaboration domain). Instead of just pointing your users to a URL, help them understand how the new tools benefit the business, streamline workflow, and manage costs.

SharePoint Killed the Intranet Star?

There was an interesting Tweet earlier from Richard Harbridge regarding the effect of SharePoint on intranet design. The article pointed out several interesting points regarding the complexity of developing on an intranet optimized platform. But while the platform itself has certainly been optimized to support the scope and scale of intranets (both large and small) the platform itself really needs to be tailored to meet the specific needs of the business.

One of the things that a lot of SharePoint implementations lack is a true understanding of how the front end of the SharePoint intranet/portal is going to interact not only with the various backend systems you’re trying to bring together, but almost more importantly with the business process itself. Sure it's great to put the latest and greatest technologies in front of your end users, but until you truly understand how the end users are going to be interacting with the environment it’s virtually impossible to develop an effective, usable intranet.

When it comes to SharePoint, this is (in my opinion) where we see the biggest level of change; the shift in methodology in how we think about what we’re developing. We’re not “destroying” intranet design, but rather turning it on its end; looking at it from the point of view of the end users. Five years ago we'd be focused on where all of the data is and how to bring that data together, leveraging the power of technology. Today that power extends far beyond the bounds of simply displaying data, offering an environment rich in collaboration (let's not just show the user some data, let's let them interact with it). How does Joe Bag-of-donuts interact with this, how do we trim the waste out of the process he’s following, and how do we streamline the communication and flow of data from the publisher to the end user (let's not forget the power of lean thinking, especially when you're trying to justify the cost of your proposed shiny new portal to the bean counters).

In your traditional intranet environment that "publisher" is historically a few defined groups, maybe the communications department for example. In the SharePoint powered intranet world, Joe Bag-of-donuts himself can easily author content (still requiring authorization by the communications group before publishing), or create a rich dashboard to share with his specific team or work center. It's this fundamental shift of looking at things from this "new" perspective, and actually empowering the end user to make decisions that's ultimately redefining how you develop for the corporate intranet.