This is a nasty SharePoint bug which caused some trouble in one of the SharePoint Online environments I was working on.

I created a Site Template using the “Save site as template” feature of SharePoint. Now when I created a new site from the template it resulted in the web parts all mixed up in the Left web part zone. Remarkably it was only the Left zone, the others were keeping their correct web part sequence.

When I took a look in the Onet.xml of the Site Template the web parts were nicely divided into seperate AllUsersWebPart’s and having correct WebPartZoneId’s. And although the web parts were also mixed up in the XML, the WebPartOrder properties were set correctly, 1 for the first, 2 for the second and so on. I still tried to rearrange the XML so the web parts would also have the right sequence in the syntax, but with no success, the web parts were still mixed up.

The final thing to do was to create a Web Part Zone for each Web Part. I did this by opening the page in SharePoint Designer and edit in the Advanced Mode (button on the ribbon).

This way the order is hardcoded on the page. I admit, it’s not very neat, but it does the trick.

In an earlier post I’ve explained that it’s possible to use PowerShell for SharePoint Online. In this post I want to explain what the difficulties of SharePoint Online are and how we did manage to overcome that.

So, what’s the problem?

When you use the Client Object Model directly from PowerShell, it might seem to work, up to the point that you’re executing a query. Then you will receive a 403 – forbidden error.

Off course, when you don’t provide any credentials you will not get access. You could try to provide a username, password and domain programmatically, but you won’t find any way to authenticate. This is because the authentication method of SharePoint Online differs from the ones that Client Object Model supports.

What’s the solution?

The only thing we have to do is get an authenticated ClientContext and we’re ready to script. Fortunately, Microsoft has done all the hard work, because they have published a sample project for authentication with SharePoint Online.

We are going to pimp this project with our own code and make it possible to retrieve and use an authenticated ClientContext in PowerShell.

Go ahead, download sample project and open the ClaimsAuth solution file in Visual Studio. You will see that it also loads a project called Sp_Ctx; you can remove or just ignore it, we won’t use it.

Now, in the ClaimsAuth project add a new class called “SPOContext” and use the following code:

using System.Net;
using Microsoft.SharePoint.Client;
using MSDN.Samples.ClaimsAuth;

public class SPOContext : ClientContext
{

  public SPOContext(string siteUrl) : base(siteUrl)
  {
    // this method gives a popup asking for credentials
    CookieCollection cookies = ClaimClientContext.GetAuthenticatedCookies(siteUrl, 0, 0);

    this.ExecutingWebRequest += delegate(object sender, WebRequestEventArgs e)
    {
      e.WebRequestExecutor.WebRequest.CookieContainer = new CookieContainer();
      foreach (Cookie cookie in cookies)
      {
        e.WebRequestExecutor.WebRequest.CookieContainer.Add(cookie);
      }
    };
  }

  // need a plain Load method here, the base method is some
  // kind of dynamic method which isn't supported in PowerShell.
  public void Load(ClientObject objectToLoad)
  {
    base.Load(objectToLoad);
  }
}

As you can see, we made an object that inherits from the ClientContext. The magic happens in the constructor; a pop-up will be shown where the user can login with his credentials. It then receives a few cookies which are stored and sent with each request. The cookies are the fairy dust in all of this, the cookies make it possible to authenticate with each request.

You also see a Load method. Why do we need this? The base class already has a Load method! Yes, but that method is some kind of dynamic method which isn’t supported in PowerShell. This custom Load method now acts as a wrapper for the real Load method.

This is it! Build the project and test the ClaimsAuth.dll according to my previous post.

EDIT: THIS PROJECT IS CONTINUED ON CODEPLEX!

Due to the success of this post I continued this as a project on CodePlex: https://sharepointpowershell.codeplex.com

 

But if you still want to continue reading this post, go ahead.

How it works

Download this file and extract the DLL to an easy location to reach from PowerShell. Because this assembly uses the Client Object Model you need to install the COM Redistributable as well.

These are the PowerShell commands to run:

You need to run PowerShell in single-threaded mode (the authentication requires it).

powershell -STA

Import the DLL.

[Reflection.Assembly]::LoadFile("D:\ClaimsAuth.dll")

Instantiate a new SPOContext providing your SharePoint Online site URL. (Don’t forget the https)

$spoContext = new-object SPOContext("https://jeffreypaarhuis.sharepoint.com")

Now, let’s test it:

$spoContext = new-object SPOContext("https://jeffreypaarhuis.sharepoint.com")
$web = $spoContext.Web
$spoContext.Load($web)
$spoContext.ExecuteQuery()
Write-Host $web.Title

As you already might understand, this solution is based on the SharePoint Client Object Model. This means that everything that’s possible with the COM is possible to script. This post explains how to work with Sites, Permissions, Documents, etc. with the COM. It is written in C# but it’s fairly easy to translate to PowerShell.

Samples


-------------------
Init spoContext
-------------------

powershell -sta
[Reflection.Assembly]::LoadFile("D:\ClaimsAuth.dll")
$spoContext = New-Object SPOContext("https://mysharepointonline.sharepoint.com")

-------------------
Print sitename
-------------------

$web = $spoContext.Web
$spoContext.Load($web)
$spoContext.ExecuteQuery()
$web.Title

-------------------
Add property to bag
-------------------

$web.AllProperties.FieldValues.Add("propA","Property A")
$spoContext.ExecuteQuery()

$web.AllProperties.Item("propA")
-------------------
Show features
-------------------

$site = $spoContext.Site
$spoContext.Load($site)
$spoContext.ExecuteQuery()
$features = $site.Features
$spoContext.Load($features)
$spoContext.ExecuteQuery()

$features
-------------------
Permissions stuff
-------------------

Function GetWeb
{
 $ctx.ExecuteQuery()
 Return $ctx.Web
}

Function GetList ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $lists = $web.Lists
 $ctx.Load($lists)
 $ctx.ExecuteQuery()
 $list = $lists | where {$_.Title -eq $name}
 return $list
 }
 return $null
}

Function GetRole ($rType)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $roleDefs = $web.RoleDefinitions
 $ctx.Load($roleDefs)
 $ctx.ExecuteQuery()
 $roleDef = $roleDefs | where {$_.RoleTypeKind -eq $rType}
 return $roleDef
 }
 return $null
}

Function GetPrincipal ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $principal = $web.EnsureUser($name)
 $ctx.Load($principal)
 $ctx.ExecuteQuery()
 return $principal
 }
 return $null
}

Function GetGroup ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $groups = $web.SiteGroups
 $ctx.Load($groups)
 $ctx.ExecuteQuery()
 $group = $groups | where {$_.Title -eq $name}

 return $group
 }
 return $null
}

Function GetDocumentLibrary ($name)
{
 $web = GetWeb
 if ($web -ne $null)
 {
 $docLibs = $web.Lists
 $ctx.Load($docLibs)
 $ctx.ExecuteQuery()
 $docLib = $docLibs | where {$_.Title -eq $name}
 return $docLib
 }
 return $null
}

$web = GetWeb

$web.BreakRoleInheritance($true, $false);
$principal = GetGroup "MyGroup"
$roleType = [Microsoft.SharePoint.Client.RoleType]"Contributor"
$role = GetRole $roleType

$collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$collRdb.Add($role)
$collRoleAssign = $web.RoleAssignments
$collRoleAssign.Add($principal, $collRdb)

$ctx.ExecuteQuery()

$list = GetList "Shared Documents"

$list.BreakRoleInheritance($false, $false);
$roleType = [Microsoft.SharePoint.Client.RoleType]"Reader"
$role = GetRole $roleType

$collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$collRdb.Add($role)
$collRoleAssign = $list.RoleAssignments
$collRoleAssign.Add($principal, $collRdb)

$ctx.ExecuteQuery()

Errors?

You might see the following errors from time to time, which aren’t a big deal:

– “The requested site does not appear to have claims enabled or the Login Url has not been set.”.
Problem: This usually means that no session can be instantiated (SharePoint Online bug).
Solution: Navigate to your site in Internet Explorer, when your site doesn’t show, refresh it a few times until it shows, or go to https://portal.microsoftonline.com. When you get a login page, go back to your script.

– “The remote name could not be resolved: ‘mysp.sharepoint.com'”
Problem: During scripting, the context timed out.
Solution: Re-instantiate the $spoContext object. When running a script in one go, this error shouldn’t pop because the Context will not prematurely expire.

Under the hood

How does it work under the hood? What’s in the DLL? What makes SharePoint Online so difficult?

In line with this one I’ve written a post that explains what the problem with SharePoint Online is and how to build your own DLL. Read it here.

UPDATE (11-7-2012):

Forgot to mention you need to install the COM Redistributable.

UPDATE (3-9-2012):

Added samples.

I’m sorry

June 6th, 2012 | Posted by Jeffrey Paarhuis in General Talk - (2 Comments)

Erika Andersen has written an article about apologizing. Doing it the wrong and the right way.

Normally I don’t post these kinds of things, but because I agree so very much with this article I want to put it in the spotlights, right here.

People apologizing with “I’m sorry, but..” or “I apologize you didn’t understand…” can really really really annoy me. They apologize first, but then instantly switch to defense mode trying to talk their way out of it. I wonder if these people know how untrustworthy they sound.

The article writes about apologies of leaders and their respect, but I think the ‘apology primer’ applies to everyone.

Here is the article: http://www.forbes.com/sites/erikaandersen/2012/06/05/courageous-leaders-dont-make-excuses-they-apologize/