Dear Microsoft: In Office 365, Groups are Groups are Groups – Unless They Aren’t

One of the problems with using common English words for things like “groups” or “teams” is that we end up trying to figure out the difference between things with very common names. When Sue Hanley (@susanhanley) and I were building our session Lions and Tigers and Teams, Oh My! – Sorting through the options to connect and collaborate in Office 365 for SPTechCon Austin recently, I realized just how confusing this could be – yet again.

The Problem

On the SharePoint home UI, when I want to create a “group”, I am told I will “Get a team site connected to Office 365 Groups”. So based on that one sentence, I might quite reasonably assume I’m creating a “Team” and a “Group” – which it so happens I am – sort of.

In the Groups UI in the Admin Center, we see these options:

  • Office 365 Group
  • Distribution list
  • Mail-enabled security group
  • Security group

In the Exchange admin center when we start to create a “group”, we’re given these choices:

  • Office 365 Group
  • Distribution group
  • Security group
  • Dynamic distribution group

But don’t fret. When you go to create a “group” in the Azure AD UI, you can create a “group” with Membership type of:

  • Assigned
  • Dynamic Device
  • Dynamic user

We can also check a box to “

Selecting ‘Yes’ will turn on Office 365 features for this group

I can even try to add the “group” – it’s not clear which type of “group” I’ve actually created – to the membership of another “group”. “Group Type” seems to be one of the following:

  • Distribution
  • Security
  • Office
  • Mail enabled security

When I create a Team in Microsoft Teams, there’s not even a clue that I’m also creating an Office 365 Group behind the scenes. Maybe that doesn’t really matter to an end user, but the fact that a “Team” is also a “Group” is another terminology SNAFU.

So What?

Some people might say that as an “IT Pro”, one should always understand all of these terms intrinsically, but I doubt that it’s often the case that this is true. The terms cross different “workloads” in Office 365 and can vary in terminology from their on premises counterparts, especially across server versions. Add to that the fact that many “admins” are the lone SharePoint/Exchange/Office 365/Chief Cook/Bottle Washer in their organization, and there is little hope they will understand the nuances. Heck, I do this stuff for a living and I find it confusing. Sometimes things are hard for no good reason.

Let’s assume we have an organization which is totally sold on the value of Office 365 Groups (note the capitalization of “groups”). If they have been using SharePoint and Exchange either on premises or in Office 365, they probably already have a mix of all the different types of “groups” shown above. When someone in that organization tries to create an Office 365 Group for – say – the Executive Team, it’s highly likely they will get an error like:

Note that “group alias” is not one of the terms used above, nor does the error give even an inkling of how to solve the problem. Not even a worthless “Contact your administrator” as a fine how-do-you-do. So, what is the user likely to do?

Well, I’ll posit that they will create a Group named “Executive Group” or “Executives” or something else spelled a little differently. That Group won’t be linked to any of the previous artifacts and thus will begin the route to more chaos that we had before.

Of course, this is a real use case I’ve run into at an actual client, as is often the case with my beefs. As far as I can tell, there is no way to convert a “Mail-enabled security group” to an “Office 365 group” without some sort of Powershell tomfoolery. That’s just bad. I’ve reached out to my MVP network for help here and I’ll update this post if I’m wrong – and I’d love to be.

This is where the old Microsoft line would have been that it’s a great “partner opportunity” or that there are “$9 spent in consulting for every $1 spent on server products”. Those were always crappy answers, and they were often driven by confusing things just like this. The “avoid a mess” or “clean up the mess” roles fell to partners who were more than happy to lap up the dollars agonized organizations were willing to toss them to fix things.

The new Microsoft doesn’t want to work this way, but in many cases, their own large size and competing teams (small “t”, generic use) still lead us to these sorts of problems.

One of the things we as consultants spend a *lot* of time doing is working with organizations to build a common set of terminology. Sometimes you may hear this called taxonomy or ontology, but the bottom line point is to get everyone to agree to a set of words and phrases that they can use consistently to express the same thing. If I came out of one of those efforts with as many terms as I’ve listed above, I’d consider the effort a failure, especially if several terms meant essentially the same thing.

The Ask

So, Dear Microsoft, please tighten up the terminology as quickly as you can. Next we need to know how to convert each of the old, legacy types of “groups” to an Office 365 Group – without resorting to Powershell. Ideally, we should just be able to click a button on any of the old things to make it a new thing, which would take us through a process (if we need one) to get it done.

Even better, when I try to create that Executive Team Office 365 Group, walk me – as a user – through fixing the issue. In other words, help me solve the problem BEFORE I’ve created the mess, so I don’t need to call a consultant to fix things. (Yes, nose, face, etc., but I think most of us consultants would rather be building valuable functionality than cleaning up messes. If your consultants relish this type of work – beware.)

Make it easy for us to help ourselves. Cut down on those support calls. Stop making your customers hire expensive consultants to clean up messes. Make Office 365 the best it can be.


Post 999! Sadly, I may have just used up my allotment of quote characters for any future posts.

Advertisements

SPTechCon Austin 2015 Wrap-Up

A splendid time was had by all at the inaugural Austin, TX version of the venerable SharePoint Technology Conference, usually known as SPTechCon. There was a tangibly different energy in the air at this SPTechCon. I’m sure some of that had to do with the new venue, but there seemed to be a bit more to it than that.

My friends at BZ Media did a wonderful job – as always – with the conference. David Rubinstein, Stacy Burris, Katie Serignese (soon to be Katie Flash!), and the whole team really know what they are doing and it shows.

In addition to the two session I presented, I was honored to be a part of an “expert panel”, discussing ‘SharePoint at the Crossroads’. I think SharePoint always seems to be at some sort of crossroads, so there’s always a lot to talk about.

20150209_202805000_iOS

Photo courtesy Heather Newman (@heddanewman)

After hours, we had a meeting of the SharePoint beards and thought deep thoughts.20150210_223631000_iOS

Here are many of the speakers at the speaker party.

20150210_052836000_iOS

Photo courtesy Christina Wheeler (@cwheeler76)

And what would a trip to Austin be without a stop at Salt Lick? BTW, that dude sitting behind Joel is Chris Tomich. He was there all the way from Perth, Australia (via San Francisco, where he’s spending a few months). Chris is one of my SharePoint heroes (not to slight anyone else), and it was awesome to have the chance to spend a bunch of time with him in Austin.

20150211_140517000_iOS

Photo courtesy Joel Oleson (@joeloleson)

The ‘Q:

20150211_025706464_iOS

Oh, and I presented two sessions. Thanks to everyone who joined me for them. The slides are up on Slideshare if you’d like to take a look.

The session I did on Content Types was a new one for me, and I had an unbelievably large and engaged crowd. Clearly this is a topic area where there need to be more resources available.

SPTechCon SFO 2014 Wrap Up

SPTechConLogoAnother splendid time was had by all at the latest rendition of SPTechCon, this time in sunny (mostly) San Francisco at the Hilton Union Square. The BZMedia folks who put on SPTechCon are great people and both the San Francisco and Boston versions are on my list of favorite SharePoint events.

Announcing SPTechCon BostonIf you weren’t able to attend this one, you may still be interested in my slides. Face it, though: it’s much more fun to go to these events to watch me make a fool of myself in person. Consider, nay decide that you will be, attending the Boston SPTechCon coming up September 16-19, 2014.

I’ve posted them to SlideShare for your fun and enjoyment. Even though several of the sessions I did were repeats of previous performances, they continually evolve, so every deck always has some new goodies in it.

 

Create a Subsite in SharePoint 2013 Using REST Calls

Today I was working on what seemed like a simple little task. I wanted to create a subsite using a REST call in SharePoint 2013. I’m not working in an “app” per se – this is for SharePoint 2013 on premises, and I want to do the subsite creation directly from a call in a plain old aspx page.

Unfortunately, there isn’t a clear example out there, at least not that I could find. It seems as though everyone has simply copied the example from this page on MSDN and quotes it verbatim. (Look for the “Creating a site with REST” example.) Unfortunately, I couldn’t get that example to work for me. Most of the examples are also focused on creating subsites in a workflow rather than directly.

I knew I’d need to have a fresh copy of the Form Digest for the request to work. There’s a great article from Wictor Wilén (@wictor) called SharePoint 2013: How to refresh the Request Digest value in JavaScript that explains several different ways to get this token. You must have the token in order for SharePoint to be willing to accept your requests to do a write to anything; it’s a part of the security features. In the page where I am working, we’re building a full application on top of SharePoint, but without using the out of the box SharePoint UI. Because of this, things like the hidden variable which usually holds the Form Digest token aren’t available in our pages. By making a quick call to the ContextInfo REST API, I can grab the Form Digest token pretty painlessly using the second method from Wictor’s post.

The breakthrough came due to some great suggestions from Matt Gibson (@Gibz) and Rob Windsor (@robwindsor) on Twitter. (You’ve got to love the #SPHelp hashtag!) Both Matt and Rob were sure this should work and gave me tips. The winning idea came from Rob. There’s a comment at the bottom of the MSDN page referenced above that shows slightly different JSON data in the REST call payload, and that did it. Bottom line, the documentation doesn’t seem to be adequate or correct in this case. At the very least, I couldn’t find it if it’s out there.

Here’s the code I ended up with. I’ve built it as a jQuery function for reusability.

// Create a new subsite
$.fn.Site.create = function(options) {

  var opt = $.extend({}, {
    siteUrl: null,
    siteName: null,
    siteDescription: "",
    siteTemplate: "sts",
    uniquePermissions: false
  }, $.fn.ProjectName.defaults, options);

  // Because we don't have the hidden __REQUESTDIGEST variable, we need to ask the server for the FormDigestValue
  var __REQUESTDIGEST;
  var rootUrl = location.protocol + "//" + location.host;

  var contextInfoPromise = $.ajax({
    url: rootUrl + "/_api/contextinfo",
    method: "POST",
    headers: {
      "Accept": "application/json; odata=verbose"
    },
    success: function(data) {
      __REQUESTDIGEST = data.d.GetContextWebInformation.FormDigestValue;
    },
    error: function(data, errorCode, errorMessage) {
      alert(errorMessage);
    }
  });

  // Once we have the form digest value, we can create the subsite
  $.when(contextInfoPromise).done(function() {
    $.ajax({
      url: rootUrl + "/_api/web/webinfos/add",
      type: "POST",
      headers: {
        "accept": "application/json;odata=verbose",
        "content-type": "application/json;odata=verbose",
        "X-RequestDigest": __REQUESTDIGEST
      },
      data: JSON.stringify({
        'parameters': {
          '__metadata': {
            'type': 'SP.WebInfoCreationInformation'
          },
          'Url': opt.siteUrl,
          'Title': opt.siteName,
          'Description': opt.siteDescription,
          'Language': 1033,
          'WebTemplate': opt.siteTemplate,
          'UseUniquePermissions': opt.uniquePermissions
        }
      })
    });
  });

}; // End $.fn.Site.create

With the function set up this way, I can call it something like this:

$().ProjectName.Site.create({
  siteUrl: "mysite",
  siteName: "My Site's Name",
  siteDescription: "This is the description that explains how to use this site.",
  siteTemplate: "sts",
  uniquePermissions: false
});

I like to publish stuff like this when I figure it out. We’re all in this together and we shouldn’t need to figure things out more than once.

Thanks Matt and Rob!

InfoPath Forms with Master Page Applied or Script Enabled

InfoPath 2013Say what you will about InfoPath. It’s a dead technology, it’s not getting any Microsoft love, it’s only for the wealthy (Forms Services is only available only in Enterprise -level CALs of SharePoint), it has no future. I’ve heard all of these and more. That said, it’s a pretty solid technology and works. We can bemoan it’s future, but we can also use it today to Get Good Stuff Done.

All that said, InfoPath browser-based forms don’t do everything one might need. I’ve written in the past about the way my friend Marcel Meth (@marcelmeth) and I worked together to use jQueryUI’s autocomplete function to augment InfoPath’s functionality in my post Using SPServices with jQueryUI’s Autocomplete Function on InfoPath Forms in SharePoint.

I was trying to do this again in an Office365 tenant recently and ran into a roadblock. A similar method worked just fine when filling out a new form, but we also needed it to work with existing form data when we edited it.

By default, InfoPath browser-based forms open up in a “chrome-less” state. By that, I mean that they don’t use the master page so one doesn’t see any of the site branding or tools around the form on the screen. From a branding perspective, that can be annoying but tolerable. However, when one needs to add some additional script to the page, one needs that chrome. This should be something simple to implement, but lo, it is not.

I had an InfoPath form that worked great. One of the things I needed to work in it is a little bit of script which is similar to the one in my blog post above. In this case, the basic idea was to add some autocomplete functionality to a field on the form, choosing (in this case) from a list containing 19000 cities and towns. (Cities seems to be a pretty common use case; there are a lot of them and autocomplete works well.)

To implement the script in the form, I created a new aspx page and dropped an InfoPath Form Web Part on it to display the form. The InfoPath Form Web Part pointed to my InfoPath form and the appropriate Content Type and worked perfectly. I embedded a reference to the script I needed in a Content Editor Web Part (CEWP) and everything worked a charm. By adding a few well-placed links to that page on the site, it was easy to get there to create a new InfoPath item.

The problem came in when we wanted to edit an existing form. Because links to the form go to

, I couldn’t get my script to run in the page. As I mentioned, the FormServer.aspx page doesn’t pick up the chrome or anything else from the master page, which was where I wanted to put the script references.

I found a great suggestion on StackOverflow to simply run some script in the master page which changes the destination of links to the FormServer.aspx page. The posts didn’t give all of the details on how to do it, but I whipped something up pretty quickly.

//Rewrite Form Links to Open in Custom Page
$("a.ms-listlink[href*='/sites/Projects/Sales Orders']").each(function() {
  var formFileName = $(this).attr("href");
  var formServerUrl = "/sites/Projects/SitePages/Manage%20Sales%20Orders.aspx";
  $(this).bind("click", function(e) {
    e.preventDefault();
    var destination = formServerUrl + "?XmlLocation=" + formFileName + "&Source=" + location.href;
    location.href = destination;
  });
});

The script worked, in that it rewired all of the existing links to the FormsServer.aspx page correctly, sending me to a link that looked something like this, which was basically what I wanted:

Unfortunately, the InfoPath Web Part then showed the error below.

Error Loading InfoPath Form

Error
There has been an error while loading the form.
Click Try again to attempt to load the form again. If this error persists, contact the support team for the Web site.

Click Close to exit this message.
Hide error details
XmlLocation and XsnLocation have both been set to non-empty values. It is an error to set both to non-empty values. Set XsnLocation to open a new copy of the form template. Set XmlLocation to open the xml file corresponding to an existing InfoPath document.

There had to be some way to pass the XmlLocation to the InfoPath Web Part successfully.

The trick turned out to be pretty simple, but I don’t know if I ever would have gotten to it. Luckily I have friends in far away places. John Liu (@johnnliu) in Sydney, Australia saw my #SPHelp on Twitter which pointed to my query on the MSDN Forums. (John’s a newly awarded MVP, so congratulations to him! Well deserved.)

1-13-2014 10-39-33 AMJohn’s suggestion seemed cryptic at first. (Hey, he was limited to 140 characters on Twitter.) I was passing in the XmlLocation, so why wasn’t it working?

After some thought (OK, a lot of thought), I decided to try something which seemed crazy. I created a new page and dropped an InfoPath Form Web Part on it, but I didn’t configure it at all. It looked like this on the page:

Unconfigured InfoPath Form Web PartThis seemed to be asking for trouble, right? Without configuring the Web Part, how would it work?

But I tried my URL with this page, and voila, it worked!

The issue with the first page was that it was configured to point to the XSN file for the InfoPath form. By omitting that configuration, the InfoPath Form Web Part was open to discussion about what to load.

As far as I know, this isn’t documented *anywhere* on the InterWebz. At least I couldn’t find it with my Bingling skillz.

Now I have two pages: New Sales Order.aspx which has the InfoPath Form Web Part configured as usual, and Edit Sales Order.aspx with the InfoPath Form Web Part not configured. Going to New Sales Order.aspx with no XsnLocation or XmlLocation on the query string loads the form to create a new item. Going to Edit Sales Order.aspx with the XmlLocation on the Query string opens an existing form for editing.