Peruvian Fiends

Sunday, February 08, 2015

0

I spent two weeks exploring the areas around Cusco and Machu Picchu in Peru earlier this year.  It was an awesome experience.  As a city dweller, it was humbling and inspiring to witness for myself such sheer scale.  I could not truly grok the weight of the mountains before I stood beneath their shadow, nor the vastness of the earth without looking across its infinite plains.

One memory from the trip, however, sticks out like a longsword in the kidney.  Perhaps writing about it will exorcise my demons.

PSA: DO NOT, UNDER ANY CIRCUMSTANCES, VISIT MACHU PICCHU WEARING SHORTS

I was surprised to see many people at Machu Picchu wearing wide brimmed hats, long sleeve shirts and pants, under the mercilessly blazing sun. Not me, I thought. Hats are for tools...

Unlike the areas around Cusco, Machu Picchu is a rainforest zone.  As such, it's home to sandflies, which at first glance resemble miniature house flies.

Do not be deceived. THEY ARE OF THE DEVIL. They will rape and pillage your body, and leave behind nothing but a shriven, pain-filled husk.

The bite itself is hardly noticeable, and initially leaves a small red spot, often with a drop of blood in the center.  I counted about 40 bites, but surprisingly, the first two days were not unbearably itchy.

The next two days were a time now known as The Scourging.

Each bite expanded to about an inch across and turned blue, like big bruises.  My legs had become instruments of torture.  Two big lumps that existed for no purpose other than to be the source of terrible, terrible pain.

Beware the flies.

The rest of the trip was amazing.



Search Driven Design in SharePoint 2013

Sunday, February 08, 2015

0

Search has seen huge improvements in stability and ease of use in SharePoint 2013.  Along with the new Search-based content webparts, it's a complete game changer for content authoring and presentation.

This article is more of an elevator pitch, than an in-depth examination of search driven architecture.  If you've been building search based solutions since SP13 CTP came out, this isn't for you.  I, however, was until very recently a firm believer in the Old Ways.

The idea is simple.  Data is stored in lists and libraries.  We need to retrieve that data and render it.  Before, I'd get the data directly from said lists using the List APIs, be it through server-side, REST, or JSOM.  Now, we can get it from the Search index.  This simple change has vast implications.

Flexibility

Since we're getting our data from the Search index, the location of the actual content no longer matters.  It could be on the same site, on a different site, spread across multiple sites, or even a different farm.  If it's being crawled and indexed, we can serve it up.

This gives us vastly more freedom in terms of site architecture.  For instance, a common design is to create a separate "authoring" private site for content authors.  Then, index its content and present it on a public publishing site.

Extensibility

Search driven design also offers the maximum separation between data and presentation.  SharePoint 2013 comes with a new Content Search Web Part (CSWP), which renders dynamic search results in a way that allows us to completely customize the presentation.  I have yet to write a custom webpart from scratch since I started using this.

The CSWP has two primary moving parts: a search query and a display template.  The search query filter drives the data--what's being retrieved from the search index.

The display template drives the presentation--how to render that data.  All presentation logic is contained in the display template, which is uploaded to the site.  No server side code involved!

So, we can move the source content around without breaking the presentation.  Or conversely, completely change how our content is rendered, simply by switching display templates.

Performance

Search returns data faster than the List APIs.  I'm not stating this as scientific fact.  I haven't done any measurements.  Obviously, it's possible to make the opposite true by putting your search index on a laptop.  But, this is my general observation, having worked with many portals in many different environments.  And it makes sense, as search is designed and optimized to serve up content as quickly as possible.

The Other Shoe

Having said all that, it's not all rainbows and unicorns.  There's a delay between when source content is updated, and when it gets indexed and reflected in the presentation.  Search would not be a good fit for time-critical scenarios.

Essentially, we're adding a layer between the data and presentation.  All sorts of things could go wrong in between.  Maybe a search Managed Property gets mis-configured.  Maybe someone fat-fingers the crawl schedules.  There's that teeeeny, tiny niggle of worry that maybe your users aren't seeing what you'd think.

Secondly, not everything in SharePoint can be indexed.  Specifically, webparts.  Search would be a bad idea if a lot of your source content lives in Content Editor or Summary Links webparts.  This should never be the case if you build a search driven solution to begin with.  Alas, users are capricious creatures.

All in all, I find the benefits far outweigh the risks in most scenarios.  Try it!


Call Server-side Code from Javascript

Sunday, February 08, 2015

3

Server-side code and postbacks already seem like remnants of a bygone era.  The next generation of SharePoint developers will likely know no more about them than punched tape computers.  Yet, there are still scenarios where you just can't avoid server-side operations.

I recently needed a way to asynchronously execute a block of server-side code from client-side Javascript.  It turns out there is an ICallbackEventHandler interface for precisely that purpose!

There's a detailed MSDN article on it, which I did not find very easy to digest.  In this post I'll try to boil it down to the essentials by going over a SharePoint example.

Background

I was building a visual webpart to let users update their Quick Links and other User Profile properties.  I stubbed out my methods, whipped up some JSOM to grab the properties, and threw in a snazzy jQuery drag-and-drop control.

Then, I tried to implement the Save button.  Disaster struck.  User profiles are read-only from all client-side APIs.

The Big Picture

This diagram illustrates the flow of execution from client to server and back.  I'll go over each piece in detail, but this is how they all fit together.



Client Side

We'll start from the Javascript side, as that is where the user action begins.  ICallbackEventHandler lets us asynchronously fire off a server side method, to which we can pass a single string parameter.

Let's say my Quick Links are stored in <div>s on the page:

<div class="link" data-title="Google" data-url="http://www.google.com" />
<div class="link" data-title="Bing" data-url="http://www.bing.com" />
<div class="link" data-title="MSDN" data-url="http://www.msdn.com" />

We can push this information into a JSON object, serialize it into a string, and pass it server-side to be saved.

Invoke server-side code


At this point, the ExecuteServerSide method below that will invoke our server-side code isn't defined yet. We will wire it up later from the code-behind.

function save() {
    var links = [];

    // Create JSON object with link data
    $('.link').each(function () {
        var title = $(this).data('title');
        var url = $(this).data('url');

        links.push({ 'Title': title, 'Url': url });
    });

    // Serialize the object to a string
    var strLinks = JSON.stringify(links);

    // Invoke server-side code.  Will wire up later.
    ExecuteServerSide(strLinks);
}

Completion Handler


Next, let's add the method that will be called when the server-side operation completes.  The server can return a single string back to the page.

function ServerSideDone(arg, context) {
    alert('Message from server: ' + arg);
}

Server Side

Begin by adding the ICallbackEventHandler interface to the webpart's code-behind.  This interface has two methods that need to be implemented: RaiseCallbackEvent and GetCallbackResult.

public partial class QuickLinksEditor : WebPart, ICallbackEventHandler

RaiseCallbackEvent


RaiseCallbackEvent( string eventArg ) is the entry point.  It's what's called by ExecuteServerSide in line #16 of the Javascript above.  eventArg is the string passed from the client side (e.g. the serialized link data)  In most scenarios, you would save this string in a class variable and use it later to perform whatever server-side operation.

We could parse out the links manually, but it's cleaner to make a simple data model class:

[DataContract]
public class QuickLink
{
    [DataMember]
    public string Title;

    [DataMember]
    public string Url;
}

Then we can take advantage of DataContractJsonSerializer to convert the JSON string to a List of QuickLink objects:

public void RaiseCallbackEvent(string eventArg)
{
    // Instantiate deserializer
    DataContractJsonSerializer serializer =
       new DataContractJsonSerializer(typeof(List<QuickLink>));

    // Deserialize
    MemoryStream stream =
       new MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(eventArg));

    // Save input data to class variable for later processing
    this.Links = (List<QuickLink>)serializer.ReadObject(stream);
}

GetCallbackResult


This method is where we execute the server-side operation.

It returns a string, which is how information gets passed back to the client-side.  That return value is the ServerSideDone method's arg parameter in line #18 in the Javascript code above.

public string GetCallbackResult()
{
    try
    {
        // Get User Profile Manager and update Quick Links
        // Full code at bottom of article
    } 
    catch(Exception ex)
    {
        return ex.Message;
    }

    return "Success!";
}

Wire Up

Finally, we wire up the two client-side functions, ExecuteServerSide and ServerSideDone.  That is done from Page_Load in the code-behind:

protected void Page_Load(object sender, EventArgs e)
{
    ClientScriptManager scriptMgr = Page.ClientScript;

    // Completion handler
    String callbackRef = scriptMgr.GetCallbackEventReference(this, 
        "arg", "ServerSideDone", "");

    // Invoke server-side call
    String callbackScript = 
        "function ExecuteServerSide(arg, context) {" + 
        callbackRef + 
        "; }";

    // Register callback
    scriptMgr.RegisterClientScriptBlock(this.GetType(), 
       "ExecuteServerSide", callbackScript, true);
}

Again, note that line #7 here defines the method signature in line #18 of the Javascript.  And line #11 here defines the method called in line #16 of the Javascript.

That's it!

Complete Code-behind


using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Collections.Generic;
using System.IO;
using System.Text;

using Microsoft.Office.Server.UserProfiles;
using Microsoft.Office.Server;

using Microsoft.SharePoint;

namespace SharePointificate.QuickLinksEditor
{
    [DataContract]
    public class QuickLink
    {
        [DataMember]
        public string Title;

        [DataMember]
        public string Url;
    }

    [ToolboxItemAttribute(false)]
    public partial class LegacyQuickLinks : WebPart
    {
        private List<QuickLink> Links;

        public LegacyQuickLinks()
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            ClientScriptManager scriptMgr = Page.ClientScript;

            String callbackRef = scriptMgr.GetCallbackEventReference(this, 
                "arg", "ServerSideDone", "");

            String callbackScript = 
                "function ExecuteServerSide(arg, context) {" + 
                callbackRef + 
                "; }";

            scriptMgr.RegisterClientScriptBlock(this.GetType(), 
                "ExecuteServerSide", callbackScript, true);
        }

        public string GetCallbackResult()
        {
            try
            {
                SPServiceContext serviceContext = 
                    SPServiceContext.GetContext(SPContext.Current.Site);

                UserProfileManager userProfileManager = 
                    new UserProfileManager(serviceContext);

                UserProfile currentUser = 
                    userProfileManager.GetUserProfile(true);

                QuickLinkManager quickLinkManager = currentUser.QuickLinks;

                quickLinkManager.DeleteAll();

                foreach (QuickLink link in this.Links)
                {
                    quickLinkManager.Create(link.Title, link.Url, 
                        QuickLinkGroupType.General, null, Privacy.Public);
                }
            } 
            catch(Exception ex)
            {
                return ex.Message;
            }

            return "Success!";
        }

        public void RaiseCallbackEvent(string eventArgument)
        {
            DataContractJsonSerializer serializer = 
                new DataContractJsonSerializer(typeof(List<QuickLink>));

            MemoryStream stream = 
                new MemoryStream(ASCIIEncoding.ASCII.GetBytes(eventArgument));

            this.Links = (List<QuickLink>)serializer.ReadObject(stream);
        }
    }
}


Bulk Load List Items with Powershell

Sunday, February 08, 2015

0

I constantly tear down and rebuild sites in my dev environment, and re-populating lists with test data is a royal pain.  One day, as CEO, I'll have "people" to take care of this sort of thing.  Alas, I have but my own wits to rely on for now.

The most concise and flexible solution I could find is using Powershell to load the data from a CSV file.  I usually create the list data in Excel, and Save As a .CSV file.  It's a huge time saver.  The first line of the file specifies the display names of the list fields to populate.

For example, this CSV data is for a Links list.  Note the URL field in all caps.  The display names are case sensitive.

Title,URL,Description
SharePointificate,http://sharepointificate.blogspot.com,A very cool blog
Funny Cat Videos,http://funnycatvideos.net,Just click it.
Angry Birds,https://www.angrybirds.com,U mad?

There's an auto-magic Import-CSV Powershell cmdlet that loads a CSV file into a list of objects.  The properties of each object correspond to the display names defined in the file's header line.

The links CSV above would produce a list of 3 objects.  Each object will have a .Title, .URL, and .Notes property.  The following code outputs "A very cool blog":

$data = Import-CSV links.csv
Write-Host $data[0].Notes

We can then iterate over each object's properties with .psobject.properties, and map their Name / Value to a new SPListItem.

The sample script below only handles field types where we can directly set the text value.  Some complex types like Taxonomy or Multichoice will require special logic.  To handle those cases, check $list.Fields[$csvField.Name].Type.  Refer to this Technet article for code samples for setting every type of SharePoint field.

param(
    [string]$WebUrl = $(Read-Host -prompt "Web Url"),
    [string]$ListName = $(Read-Host -prompt "List Name"),
    [string]$CsvPath = $(Read-Host -prompt "Path to CSV file")
)


# Load SP snapin if needed

$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
if ($snapin -eq $null) 
{
    Add-PSSnapin "Microsoft.SharePoint.Powershell"
}

$web = Get-SPWeb $WebUrl
$list = $web.Lists[$ListName]; 

# Load CSV into object

$csvItems = Import-Csv $CsvPath

# Iterate over items to import

foreach($csvItem in $csvItems)
{
    # Create new list item
    $newItem = $list.items.add();
 
    # Iterate over fields to import
    foreach($csvField in $csvItem.psobject.properties)
    {
        if(![string]::IsNullOrEmpty($csvField.Value))
        {
            # Set field value
            $newItem[$csvField.Name] = $csvField.Value;
        }
    }

    $newitem.update();
}


Bootstrap Responsive Framework with SP 2013

Saturday, February 07, 2015

7

Responsive UIs are all the rage these days, but SharePoint hasn't caught up just yet.  Maybe in SP15.  One can hope. In the meantime, Bootstrap UI is my go-to solution.  It's one of the most robust and mature (and free!!) responsive frameworks.  It has no external dependencies and integrating with SharePoint is as simple as including a single CSS and JS file in your masterpage.

However, Bootstrap's CSS overrides some important SharePoint classes.  This article covers CSS fixes to make the two play nicely together.  They are just some of the more common issues, and are by no means an exhaustive list.  If you discover others, please do share in the comments!

These changes were tested on a SP 2013 Publishing Site with Bootstrap v3.3.2.

Page Width

As we narrow the browser window on a responsive site, we expect to eventually hit the mobile breakpoint.  On a SP site, that may never happen.  At some point, horizontal scrollbars crop up and the actual content window stops getting smaller.

This is because one of the onion-like layers of container elements has a min-width restriction, which needs to be removed:

#contentBox {
    min-width: 0;
}

Text Styles

Bootstrap's line-heights and font sizing messes with SP menu and modal dialog text.

.ms-dlgContent {
    line-height: 1.1;
}

#s4-ribbonrow {
    line-height: 1.2;
}

.ms-core-menu-link:hover,
.ms-core-menu-link:focus,
.ms-core-menu-link:active,
#s4-ribbonrow a:hover,
#s4-ribbonrow a:focus,
#s4-ribbonrow a:active {
    text-decoration: none;
}

#pageStatusBar,
.ms-cui-tooltip {
    line-height: 1.1;
    font-size: 8pt;
}

.ms-cui-modalDiv-ie,
.ms-cui-glass-ie {
    background-color: transparent;
}

Box Sizing

Box-sizing is an innocent-looking CSS property that can sow utter chaos and ruin on a page.  It specifies whether an element's width and height include the padding and border (border-box), or just the inner content (content-box).

Content-boxing is the default, which is what SharePoint's CSS is built to work with.  Bootstrap, however, requires border-box and sets that pretty much globally.  This doesn't completely break SharePoint, but most controls will be mis-aligned or cut off.

We have to allow Bootstrap to use border-box, while reverting just the SharePoint elements to content-box.  I started with Olly Hodgson's fixes for SP2010, and added some classes for 2013:

#s4-ribbonrow *,
#s4-ribbonrow *:before,
#s4-ribbonrow *:after,
#ms-help *,
*[class*='ms-core-menu'],
*[class*='ms-dlg'],
*[class*='ms-dlg']:before,
*[class*='ms-dlg']:after,
.ms-dlgFrameContainer > div,
.ms-dlgFrameContainer > div:before,
.ms-dlgFrameContainer > div:after,
.ms-dlgFrameContainer > div > div,
.ms-dlgFrameContainer > div > div:before,
.ms-dlgFrameContainer > div > div:after,
.ms-MenuUIPopupBody,
.ms-MenuUIPopupBody:before,
.ms-MenuUIPopupBody:after,
.ms-MenuUIPopupBody *,
.ms-MenuUIPopupBody *:before,
.ms-MenuUIPopupBody *:after,
.ms-ToolPaneOuter,
.ms-ToolPaneOuter:before,
.ms-ToolPaneOuter:after,
.ms-ToolPaneOuter *,
.ms-ToolPaneOuter *:before,
.ms-ToolPaneOuter *:after,
*[class*='ms-cui'],
*[class*='ms-cui']:before,
*[class*='ms-cui']:after,
*[class*='ms-cui'] *,
*[class*='ms-cui'] *:before,
*[class*='ms-cui'] *:after,
*[class*='ms-dlg'] *,
*[class*='ms-dlg'] *:before,
*[class*='ms-dlg'] *:after,
*[class*='ms-webpart'] * {
    -webkit-box-sizing: content-box;
    -moz-box-sizing: content-box;
    box-sizing: content-box;
}