Create a Business Solution, Step by Step, with No Managed Code at SPTechCon Boston 2013

SPTC_Boston2013_speakerbadge[important]If you’re coming to this post late, remember that you can still save $200 by using code ANDERSON, right up the conference![/important]

SPTechCon Boston 2013 is coming up fast. If you haven’t registered yet, do so by tomorrow (June 28) to get $400 off. If you use my code ANDERSON, you can get an additional $200 off. Act fast because there’s only a day and a half left to save that $600!

I’m doing two regular sessions this time around and one tutorial. Tutorials at SPTechCon used to be called workshops and I always wanted to try to do what I think of as a “real’ workshop: a session in which we work through some challenge as a group from start to finish. I managed to convince David Rubinstein (@drubinstein) at BZMedia, the SPTechCon head honcho, that this would be a good idea for me to try. We’ll see if I led him down a garden path or not, I suppose.

Here’s the description of the tutorial, taken directly from the listing on the SPTechCon site:

People often ask this instructor how he comes up with the somewhat unorthodox solutions he has built in SharePoint. In this tutorial, you will learn how you can devise powerful solutions from beginning to end without deploying any managed code. First, we’ll take a business problem submitted by one of you, go through a rapid design session, and figure out how we might build it. Next, you’ll be taken through the actual solution the instructor whacked together based on the requirements beforehand, and we’ll compare and contrast. Hopefully, we will hit on most of the same high points that we’ve designed into the solution together, but since we won’t know until we get there, all bets are off!
We’ll do the work in SharePoint 2010 so that the solution’s usefulness will be as wide as possible. However, we’ll dip into SharePoint 2013 as well to see how the solution might work there and discuss how we might approach things differently.
If you are interested in this tutorial and have a small solution you’d like to see the instructor run through, please send it to him via his blog (sympmarc.com) before July 15. He will post the details of the requirements you’ll use, and then get cracking on his solution. A few constraints just to keep things relatively straightforward:

  • Single-site collection
  • Departmental scale solution (we’re not going to rebuild My Sites)
  • Things that are off the table: User Profiles, External Content Types (BCS), complex workflows, etc.

In other words, the point is education, not complexity or a production-ready solution. Come prepared to roll up your sleeves and participate. We’ll be making it up as we go, and we’ll all learn together!

Level: Intermediate

Topic Area : Developer Essentials, Information Worker Essentials

This post is to open up the discussion about a good topic or solution for the tutorial. If you have something you think would have broad appeal, I encourage you to post it as a comment here. Katie Serignese at BZMedia is also going to help me get the word out, so if something comes to me some other way, I’ll post it as a comment here myself. Even if you can’t make it to SPTechCon for some reason (Really? It’s a great conference!), if you have an idea, please post it. If you’re not there, I’ll still make sure that you get the demos and slides

In this case, it’s not a democracy, as I get to decide on the final topic(s). However, I’m sure many of you will have great ideas. If nothing else, we’ll gather a list of interesting things that one ought to be able to accomplish in SharePoint.

Please leave your ideas by July 15 and I’ll post the “winner(s)” shortly thereafter. Then plan to attend my tutorial on Sunday, August 11 from 9:00am-12:15pm and see if I can pull it off or not. It might be like watching a bad trapeze artist working without a net or it might be a thing of beauty. If you miss it, you may never know!

SPTechCon Social Demo

[important]

Mark Miller (@EUSP) and I did a webinar on Friday, March 15, 2013 at  to show the demo again and take questions about how we built it. If you’d like to look at the demo yourself, check out the publicly available page on NothingButSharePoint.com. The video is embedded below.

[/important]

It’s not every day that I get to create something in SharePoint with a Magic button.

Magic Button

I few weeks ago, Mark Miller pinged me asking if I’d be interested in helping him with a demo for his keynote at SPTechCon. When Mark asks, I tend to ask “how high” simply because it’s usually something fun. Sometimes, it’s even useful.

The basic idea was to show a social dashboard in SharePoint without letting on that we were using SharePoint. As you might imagine, none of this went along as linearly as I’m going to describe it. We tried various different things, added and removed columns, and widgets, etc. What I’ll describe is the end result.

First, I knew I wanted to use jQuery, jQueryUI, and SPServices, so I took a copy of default.aspx in a basic Team Site and added the following references. (Yes, I prefer the old Web Part Page over the Wiki pages. It’s much easier to work with. The Wiki pages tend to vomit markup all over what I do.)

<link href="Scripts/social_home_page.css" rel="stylesheet" type="text/css" />
<!-- Reference the jQueryUI theme's stylesheet on the Google CDN. Here we're using the "Start" theme -->
<link href="/ajax/libs/jqueryui/1.10.0/themes/start/jquery-ui.css" rel="stylesheet" type="text/css" />
<!-- Reference jQuery on the Google CDN -->
<script type="text/javascript" src="/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<!-- Reference jQueryUI on the Google CDN -- >
<script type="text/javascript" src="/ajax/libs/jqueryui/1.10.0/jquery-ui.min.js"></script>
<!-- Reference SPServices on cdnjs (Cloudflare) -->
<script type="text/javascript" src="/ajax/libs/jquery.SPServices/0.7.2/jquery.SPServices-0.7.2.min.js"></script>
<!--<script type="text/javascript" src="http://cdn.dev.skype.com/uri/skype-uri.js"></script>-->
<script type="text/javascript" src="Scripts/social_home_page.js"></script>

Next, I altered the markup for the page in SharePoint Designer. This was a demo, so there was zero concern about repeatability, of course. It’s nice to have that sort of freedom once in a while. By default, the page has a table with two columns set to 70% (left) and 30% (right) width. I simply changed that markup to have four columns, each with 25% width. In each of the two new table columns, I added a Web Part Zone. I wanted to drop as much of the social stuff as I could into the page using Content Editor Web Parts so that I could explain it more easily, like in this blog post. In edit mode, the page looked something like this: 3-11-2013 09-52-09 Next I added some CSS to the social_home_page.css file:

#s4-ribbonrow {
  display:none;
}
.s4-titlerowhidetitle {
  display:none;
}
#s4-leftpanel {
  display:none;
}
#MSO_ContentTable {
  margin-left:0;
}
#MSO_ContentTable table td {
  padding:3px;
}

This simple CSS removed all of the chrome we normally see on the page. In other words, we’ve removed the SharePoint from SharePoint to a large degree. The ribbon, title area, and Quick Launch are all totally hidden. (No screen shot for this – you wouldn’t see anything!) At this point, I had a page that looked like a big white nothing in the browser. Time to add the fun stuff. In Twitter, you can create widgets that display tweets from Twitter much like you can create different streams in HootSuite or TweetDeck. You can find this capability in Settings: 3-11-2013 09-58-00 I created three different widgets:

  • search stream for “EUSP”
  • search stream for “SPTechCon”, and
  • search stream for “SharePoint”

There are a few settings which give you basic control over the look and feel. If we were implementing this “for real”, it would probably make sense to make calls against the Twitter API and get the raw data so that we would have full control over the markup and visuals.3-11-2013 10-05-29 When you create the widget, you get a little snippet of markup and script which you can embed in a Web page. Those snippets are very simple. Here’s what the snippet for EUSP looks like:

<a class="twitter-timeline" href="https://twitter.com/search?q=EUSP" data-widget-id="306449761781817344"></a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

I put each snippet into a TXT file so that I could reference them in CEWPs. For the EUSP stream, I have a TXT file called twitter_search_eusp.txt in a Document library, and I just point to that file in the CEWP’s Content Link. This gets the three Twitter streams into the page:

3-11-2013 10-25-53

We also wanted to show something from Yammer, since it plays so importantly into Microsoft’s social story. While there are Yammer Web Parts available, I decided to go the widget route here as well. If you look under Apps in the Yammer Web interface, you’ll find Yammer Embed, which eventually takes you to the Yammer Embed page. There, you’ll find a PDF document that explains how to embed a Yammer feed. (Note to the Yammer folks: This isn’t a very clear process. Opportunity for improvement.)

Here’s what I ended up with for the SPYam feed. Note that I created my own id for the container so that it would fit into my page logic easily.

<script data-app-id="hyB2pTvrL36Y50py8EWj6A" src="https://assets.yammer.com/platform/yam.js"></script>
<script>
yam.connect.embedFeed({
  container: '#yammer-spyam-embedded-feed',
  network: 'spyam' // update with your own
});
</script>
<div id="yammer-spyam-embedded-feed"></div>

For some reason, the Yammer authentication mechanism simply won’t work in my page in Internet Explorer 9, so I switched to Firefox at this point, where I had no problems. (Another place for improvement, Yammer folks. However, it may well be that my IE9 is just sick.)

3-11-2013 10-43-14

Now we have all four streams we want in the page, at least in Firefox, for me.

I had seen some Twitter traffic about Dan McPherson’s lastest entries in the SharePoint Games under the Leap moniker. There are two offerings right now: LeapBackup and LeapMessages. Mark and I thought it would be cool if we could use LeapMessages to send a text message with the contents of a Tweet. I can imagine several use cases for this. Perhaps you work at Burger King, your Twitter account is hacked, and you need to notify the execs…

LeapMessages is really simple to implement. You just sign up on the Leap site, give the site URL and your Office365 credentials (it only works on Office365 right now), and Leap creates a list in your site called LeapMessages. If you add an item to that list, the LeapMessages engine grabs it (it polls every 30 seconds) and sends it out as a text from their gateway. Easy-peasy.

To implement this, I pulled a demo cheat. Since it takes a little time to load up the three Twitter streams and the Yammer stream, I set it up so that when I hovered over the Magic button (see below for more info on the button), this script would run, adding an SMS link at the bottom of each tweet in the three streams. I just had to wait until the three Twitter streams were loaded before I did the hover trick.

$("div.showSharePoint").hover(function () {

  if (!loadedSMS) {

    loadedSMS = true;

    $("iframe[id^='twitter-widget-']").contents().find("ul.tweet-actions").prepend("<ul><li><a class="SMS-send">SMS</a></li></ul>");
    $("iframe[id^='twitter-widget-']").contents().find(".SMS-send").click(function (e) {

      e.preventDefault();
      var thisTweet = $(this).closest("div.footer").closest("li");
      var thisTweetText = thisTweet.find("div.e-entry-content").text().trim();
      var thisTweetFullName = thisTweet.find("div.header span.full-name").text().trim();
      var thisTweetNickName = thisTweet.find("div.header span.p-nickname").text().trim();
      messageText.val("Interesting tweet from " + thisTweetFullName + " (" + thisTweetNickName + ") " + thisTweetText);
      countChars();

      $("#dialog").dialog("open");

    });
  }

}, function () {
  // Nothing to do
});

I added this markup at the bottom of the page for the dialog that would pop up when I clicked the SMS links.

<div id="dialog" title="Send SMS Message">
  <p class="validateTips">All fields are required.</p>
  <head>
    <meta name="WebPartPageExpansion" content="full" />
  </head>
  <form>
    <fieldset>
    <label for="phoneNumber">Phone Number</label>+1
    <input type="text" name="phoneNumber" id="phoneNumber"
    class="text ui-widget-content ui-corner-all" />
    <br />
    <label for="messageText">Message</label>
    <textarea rows="5" cols="40" name="messageText"
    id="messageText" value=""
    class="text ui-widget-content ui-corner-all"></textarea>
    <div id="charCount"></div></fieldset>
  </form>
</div>

When I clicked the SMS link, this script drove the dialog and wrote the entered data into the LeapMessages list using SPServices.

dialogDiv.dialog({
  autoOpen : false,
  height : 300,
  width : 500,
  modal : true,
  buttons : {
    "Send Message" : function () {
      var bValid = true;
      allFields.removeClass("ui-state-error");
      bValid = bValid && checkLength(phoneNumber, "Phone number", 10);
      bValid = bValid && checkRegexp(phoneNumber, /^([0-9])+$/i, "Phone number can only contain digits.");
      // From jquery.validate.js (by joern), contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
      if (bValid) {
        // Send the SMS message
        $().SPServices({
          operation : "UpdateListItems",
          async : false,
          batchCmd : "New",
          listName : "LeapMessages",
          valuepairs : [
            ["ReceiverMobileNumber", "+1" + phoneNumber.val()],
            ["Message", encodeXml(messageText.val())]
          ],
          completefunc : function (xData, Status) {}
        });
        $(this).dialog("close");
      }
    },
    Cancel : function () {
      $(this).dialog("close");
    }
  },
  close : function () {
    allFields.val("").removeClass("ui-state-error");
  }
});

There are a few helper functions referenced above, and here they are:

var loadedSMS = false;

var dialogDiv = $("#dialog");
var phoneNumber = $("#phoneNumber"),
messageText = $("#messageText"),
allFields = $([]).add(phoneNumber).add(messageText),
tips = $(".validateTips");

function checkLength(o, n, num) {
  if (o.val().length > num || o.val().length < num) {
    o.addClass("ui-state-error");
    updateTips(n + " must be  " + num + " digits.");
    return false;
  } else {
    return true;
  }
}

function checkRegexp(o, regexp, n) {
  if (!(regexp.test(o.val()))) {
    o.addClass("ui-state-error");
    updateTips(n);
    return false;
  } else {
    return true;
  }
}

function updateTips(t) {
  tips.text(t).addClass("ui-state-highlight");
  setTimeout(function () {
    tips.removeClass("ui-state-highlight", 1500);
  }, 500);
}

function countChars() {
  var messageLen = messageText.val().length;
  var errorClass = messageLen > 160 ? "class='ui-state-error'" : "";
  $("#charCount").html("You have used <span " + errorClass + " >" + messageLen + "</span> of 160 characters");
}
messageText.keyup(function () {
  countChars();
});

I decided to add one last piece of functionality. I couldn’t figure out how frequent the Twitter polling was, but when there was a new tweet in any any of the streams, I wanted to highlight it. To do this, I added this bit of script. It ran every three seconds to see if anything new had shown up, and if it had, it highlighted the counter at the top of each Twitter stream.

// Show tweet counters
setInterval("countTweets()", 3000);

function countTweets() {
  $("iframe[id^='twitter-widget-']").each(function () {
    var tweetCount = $(this).contents().find("li.tweet").length;
    var thisCounter = $(this).prev(".tweet-counter");
    if (thisCounter.length === 1) {
      var currCount = thisCounter.html();
      if (tweetCount != currCount) {
        thisCounter.addClass("ui-state-highlight");
        setTimeout(function () {
          thisCounter.removeClass("ui-state-highlight", 1500);
        }, 10000);
      }

      thisCounter.html(tweetCount);
    } else {
      $(this).before("<div class="tweet-counter">" + tweetCount + "</div>");
    }
  });
}

The Magic button is just a div. Yes, I violated best practices by making it a div instead of a real button. Don’t try this at home.

<div class="showSharePoint">Magic</div>

The Magic button contains the reveal. When I clicked on it, this script ran, unveiling the fact that the page was, in fact, a SharePoint page. There were very few oohs or aaahs that I could hear, which was a little disappointing, but hey, maybe I pushed it too fast or something.

// Set up the Magic button
$("div.showSharePoint").click(function () {

  var thisValue = $(this).text();

  if (thisValue == "Magic") {
    $("#RibbonContainer").hide();
    $("#s4-ribbonrow").slideDown(5000);
    $("#RibbonContainer").slideDown(5000, function () {
      $(this).fadeIn(1000)
    });
    $(".s4-titlerowhidetitle").fadeIn(5000);
    $("#s4-leftpanel").fadeIn(5000);
    $("#MSO_ContentTable").animate({
      "margin-left" : "150px"
    }, 5000, function () {
      // animation complete
    });
    $(this).text("No Magic");
  } else {
    $("#RibbonContainer").slideUp(5000, function () {
      $(this).fadeOut(1000)
    });
    $("#s4-ribbonrow").slideUp(5000);
    $(".s4-titlerowhidetitle").slideUp(5000);
    $("#s4-leftpanel").hide();
    $("#MSO_ContentTable").animate({
      "margin-left" : "0px"
    }, 5000, function () {
      // animation complete
    });
    $(this).text("Magic");

  }

});

So there you have it. I’m sure I’ve left some bits and bobs out of this in trying to make it consumable here, but I think I’ve gotten most of the important parts. All in good fun, and hopefully it got the message across that SharePoint doesn’t have to look like SharePoint and that we can do useful social stuff in SharePoint in many cool ways.

I’ve thought of a few neat things we could do with this, like storing the snippets in a SharePoint list and letting the user select which ones they want to see in their version of the page, dragging and dropping the streams into the order they like, and saving their settings for when they return. The possibilities with this stuff are really endless, depending on what you want to accomplish.

Have fun!

3-11-2013 11-47-29

SPTechCon San Francisco 2013 Wrap Up

Well, I’m back in the snowy Northeast after a great four days at SPTechCon San Francisco 2013. As always, the BZ Media folks put on a great show. SPTechCon is consistently well-run and packed with excellent content, vendors, and attendees.

I wanted to post links to the slide decks from my three sessions. They are available on SlideShare.

If you weren’t at SPTechCon or you were there and missed this session, I’ll be doing it again for the MetaVis SharePoint MVP Webinars Series on Wednesday, March 13, 2013 from 2:00 PM – 3:00 PM EDT. You can register here. The webinar will also be recorded for later viewing.

In addition, as I promised in my third session, here’s a link to a WSP which contains the demos I showed. The WSP is simply the Office365 site saved as a template. In the past, some people have had difficulty instantiating my demo templates in their environments due to activated features not matching. (My rants on the lack of  true portability of SharePoint-based content will probably continue, like Trials and Tribulations: Migrating My Demos Site to Office365, Office365 SharePoint Online Portability Issues Strike Again, and Moving Lists from Hosted WSS 3.0 to Office365 – The ShareGate Way.) If you run into this sort of issue, let me know in the comments and we’ll see what we can work out.

SPTechCon Boston 2012 Follow Up

SPTechCon Boston 2012The SharePoint Technology Conference (SPTechCon) has always been one of my favorite SharePoint conferences. It was the first big conference I ever spoke at, and the folks at BZMedia(where’s Katie’s photo???) who put it on are all aces, every single one of them.

I only did one session at the latest iteration in Boston because I totally forgot to submit anything before the deadline. On the day of the deadline, I begged a little bit and David Rubinstein relented and slid me in. Since it was almost literally in my backyard, I didn’t want to miss out! Next time in San Francisco, I hope to do a workshop or two as well as some “regular sessions”.

My session in Boston was one that I’ve been doing different versions of over the last year or so called Flying in the Cloud: New Ways to Develop for SharePoint. It’s different every time because I am always adding new examples based on the client work I’ve been doing.

In the session, I talked about some of the ways I’ve been building things in SharePoint way back to the early SharePoint 2007 days, when I worked for what I call Jornata I (ask Scott Jamison or Mauro Cardarelli about those heady days). To me it’s not a new way of working, but with SharePoint 2013 coming along with its new app model, it’s becoming almost fashionable to use things like jQuery, Web Services, DVWPs, XSL, and CSS. You know, that “no code” stuff.

If you’re interested in the demos I showed, I’ve packaged them into a couple of WSPs which you can download and instantiate in your own environment if you’d like. Additionally, for each of the examples, I’ve done other blog posts which describe what I did and how they work, along with the code. If you can’t find the posts, feel free to ping me via the contact for or on Twitter (@sympmarc) and I can shoot you a link for what you’re looking for.

SPTechCon Boston 2011 Wrap Up

SPTechCon was my first SharePoint conference back when I attended the San Francisco 2010 edition. That doesn’t seem so long ago, yet it really is in SharePoint time. (They say you’ll never forget your first girl, but in this case it’s my first conference, so… Yeah, if I have to explain it, it’s a stretch.)

SPTechCon was where I first really got how powerful and useful the SharePoint community is. It was when I realized that it would be cool to find a place for myself within it.

I’ve read a couple of write ups about the conference which I liked, so I figured I’d give pointers to them here. Ruven Gotz’s (@ruveng) post SPTechCon: Sing(apore) for your supper and Josh McCarty’s (@josh_a_mccarty) post Thoughts on SPTechCon Boston 2011 captured the conference well (and Josh’s not just because he talked about me!), plus a bit of what they enjoyed while they were here in Boston. If you’re interested in other slants on the conference and extracurricular activities, give them a read. 

I was once again honored to be able to speak at this edition of SPTechCon, right here in my home town of Boston. Not only was I able to do a technical session, but David Rubinstein (@drubinstein), tsar of the conference, invited me to do a Lightning Talk on Wednesday night. You get 4 minutes for those darn things, so I decided to simply do a “promo” for my session on Friday.

Thursday night, I also had the honor of being on the BASPUG “Ask the Experts” panel, sitting next to none other than Christian “Flying Monkey” Finn. Also on the panel were Michael Noel, Laura Rogers, Todd Klindt, Randy Drisgill, and Jeremy Thake. (I’m forgetting someone…)

My session was entitled “Developing in SharePoint’s Middle Tier”  (it’s session 607) and I demoed some generalized solutions from past project work. Since I had the slides from the Lightning Talks, I figured I’d clean them up a little and use them as an intro for the session. I’ve posted solution files (WSPs) for SharePoint 2010 to my Sympraxis Consulting Demos site (upper right of the page) which contain the two main demos I showed. There’s also a SharePoint 2007 version (MiddleTier2007) of the custom navigation demo, if that’s more useful to you. (It’s an earlier version, but not too far off from what I showed.)

“Customized Navigation”

image6

“Budget”

image7

 

USPJAlogo

Finally, and not to be too mercantile about it, if you are interested in learning more about these development techniques, consider taking my classes at the USPJ Academy. I have three courses which cover SharePoint’s Middle Tier:

  • Data View Web Part Basics
  • Enhancing the User Experience with jQuery
  • Introduction to the SharePoint Web Services

These three courses cover much of what I showed in the demos and more. You can see more details on the courses on the Academy Course Listing page. Come join us!