blog.scoreman.net

Create Site Collections in SharePoint Online using CSOM

February 26th, 2013

This week I’m working with a customer that wants their users to be able to create site collections in SharePoint Online. Below is a step-by-step guide how this can be done using the Client Side Object Model in SharePoint 2013.

UPDATE 2014-01-15: Registering a

Prerequisites

The example application is created on my Windows 8 desktop computer without SharePoint 2013 installed. The following applications are required:

  • Visual Studio 2012
  • Microsoft Office Developer Tools for Visual Studio 2012
  • SharePoint Online Management Shell

Create a Client Application

In this example I’ve created a simple console application but it might as well be a service running in Azure or any other .NET application. Start by creating a Windows console application and add assembly references to:

  • Microsoft.SharePoint.Client.dll
  • Microsoft.SharePoint.Client.Runtime.dll
  • Microsoft.Online.SharePoint.Client.Tenant.dll

The assemblies can be found here: C:Program FilesSharePoint Online Management ShellMicrosoft.Online.SharePoint.PowerShell.

Add TokenHelper Class

To make it easier for developers Microsoft has created a helper class used for OAuth communication with SharePoint. This example aslo uses this class and it can be included by creating a new App for SharePoint (provider or autohosted) in Visual Studio project and then copying the TokenHelper.cs file into the console application. Change the namespace in the class to match the console applications namespace. The class requires references to the following assemblies:

  • System.Web.dll
  • System.ServiceModel.dll
  • System.IdentityModel.dll
  • System.Web.Extensions.dll
  • Microsoft.IdentityModel.dll
  • Microsoft.IdentityModel.Extensions.dll

Add the Code

Start by adding these two using statements to Program.cs:

using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.SharePoint.Client;

Then simply add the following code inside the Main-method:


            var SHAREPOINT_PID = "00000003-0000-0ff1-ce00-000000000000"; // SharePoint constant
            var tenantAdminUri = new Uri("https://<TENANT>-admin.sharepoint.com");
            var token = TokenHelper.GetAppOnlyAccessToken(SHAREPOINT_PID, tenantAdminUri.Authority, null).AccessToken;

            using (var context = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), token))
            {
                var tenant = new Tenant(context);
                var properties = new SiteCreationProperties()
                {
                    Url = "https://<TENANT>.sharepoint.com/sites/site1",
                    Owner = "<USER>@<TENANT>.onmicrosoft.com",
                    Template = "STS#0",
                    StorageMaximumLevel = 1000,
                    UserCodeMaximumLevel = 300
                };
                tenant.CreateSite(properties);

                context.Load(tenant);
                context.ExecuteQuery();
            }

All occurences of <TENANT> should be replaced with your real tenant name and also <USER> should be replaced with the username of whoever is going to be the owner of the site collection.

Register the Application in SharePoint Online

For this to work the application must be registered in your Office 365 tenant and also given tenant permissions. To do this first navigate to https://<TENANT>.sharepoint.com/_layouts/appregnew.aspx. Click on the buttons to generate an App Id and an App Secret. Fill in a Title, App Domain and Redirect URI and click on the Create button. You will be taken to a summery page. Copy the App Id and App Secret values because you will be needing them later.

Next you need to give the application tenancy permission. This can be done by going to https://<TENTANT>.sharepoint.com/_layouts/appinv.aspx. Fill in your App Id and hit Lookup. Now paste the following XML in Permission Request XML and click Create:

<AppPermissionRequests AllowAppOnlyPolicy="true">
    <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
</AppPermissionRequests>

You will be taken to a summary page saying that this application requires full control of all site collections, access basic user information and share its permission with other users. Click Trust it.

Finally you need to find out the app realm. Navigate to https://<TENANT>.sharepoint.com/_layouts/appprincipals.aspx. Identify the row with your registered application and look at the App Identifier column. The unique identifier after the @-sign in the App Identifier is the app realm. Copy the realm (not the whole App Identifier just the last part after the @-sign).

Finish the Client Application

The App Id, App Secret and App Realm now needs to be added to the client application. If your console application does not have an app.config add one and then add these appSettings inside the configuration element:

  <appSettings>
    <add key="ClientId" value="<APP ID>"/>
    <add key="ClientSecret" value="<APP SECRET>"/>
    <add key="Realm" value="<APP REALM>"/>
  </appSettings>

Replace , <APP ID>, <APP SECRET> and < APP REALM> with the values you got from SharePoint Online. The application should now be ready for testing. Not many lines of code but some wiring up to make it work with OAuth and SharePoint Online.

SharePoint 2013 – 401 UNAUTHORIZED

November 7th, 2012

Just started using my new SharePoint 2013 server (hosted in Azure) I noticed that I was getting 401 UNAUTHORIZED when I accessed the site using http://{COMPUTER NAME} but it was working when I accessed it through http://localhost. Turns out this was caused by alternate access mappings not being configured for {COMPUTER_NAME} and because of this the new OAuth authorization was not working properly. You can read more about OAuth here : http://msdn.microsoft.com/en-us/library/fp179932(v=office.15).aspx

Also if you are browsing from the server make sure you configure loopback check exceptions: http://support.microsoft.com/kb/926642/en-us

Office 2013 has reached RTM

October 12th, 2012

Today (October 11, PDT) the Office team reached a major milestone going RTM with the entire Office suite (both client, server and cloud services). This means that the bits for SharePoint 2013 are final and ready to ship! You can read more about it here:

http://blogs.office.com/b/office-news/archive/2012/10/11/office-reaches-rtm.aspx
http://sharepoint.microsoft.com/blog/Pages/BlogPost.aspx?pID=1035

Error when saving reusable workflow as template

October 11th, 2012

If you get an error message saying “The server was unable to create your template. Please notify your server administrator” when you try to save a reusable workflow as template, the reason could be that the name is too long.

SharePoint Designer Error

When I ran into this I looked in the ULS log and found this: 

 SPSolutionExporter: System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

After renaming the workflow to a shorter name it worked (in my case only 10 characters worked). Looking at the ULS some more I found out that the original name I chose was still used in combination with the new name. So if you still want that long name for your workflow you could first create the workflow with a short name, publish it and then rename it to the long name. Now you can hopefully save it as a template.

Don’t use Linq on collections in DocumentSetTemplate

October 11th, 2012

If you ever find yourself working with document sets through the SharePoint API, don’t use Linq on the Collections in DocumentSetTemplate. Even though they implement IEnumerable<T> they have not wired everything up internally so you will get NullReferenceException (Object reference not set to an instance of an object) when using any Linq statement on them. This applies to AllowedContentTypes, DefaultDocuments, SharedFields and WelcomePageFields.

You can easily reproduce this by running this code:

var ct = web.ContentTypes["Document Set"];
var template = DocumentSetTemplate.GetDocumentSetTemplate(ct);
template.AllowedContentTypes.Any();

Hopefully this will be fixed some day in the API

Debugging ItemAdded when InfoPath has codebehind in sandbox

October 3rd, 2012

 I was working with a customer that had an InfoPath form with code behind running as a sandboxed solution. The forms library also had an event receiver with both ItemAdded and ItemUpdated. We could debug ItemUpdated but not ItemAdded. I finally found that if the form has code behind running as a sandboxed solution you have to attach to the SPUCWorkerProcessProxy.exe process to debug ItemAdded. Even using Attach to All SharePoint Processes in CKSDev won’t do it since the SPUCWorkerProcessProxy.exe process is not attached. ItemUpdated will run in the normal SharePoint process and not in SPUCWorkerProcessProxy.exe.

jQuery Site Assets

February 3rd, 2012

There is a conflict in the SharePoint API and jQuery that causes the JavaScripts in Site Assets libraries to stop functioning. This is because the APIs both use $. To avoid this conflict you can override the $-function and instead use the jQuery-function.

More information on how you override can be found here: http://docs.jquery.com/Using_jQuery_with_Other_Libraries

Week numbers in calendar view – revisited for SharePoint 2010

April 9th, 2011

A while ago I wrote a post on how to add week numbers to the monthly calendar view. The customer I built this for is now upgrading to SharePoint 2010 and since the calendar is now completely rebuild with ajax functionality I had to revisit and rewrite this code. This time I’m doing it with jQuery instead which in my opinion leads to a much cleaner solution. Thanks to Mike Smith’s post for showing how to intercept the asynchronous ajax events  of the calendar. The neet thing about this is that even if asynchronous events happen like if you page to a new month, the calendar still gets updated with new week numbers. For calculating week number I’m using the same javascript as in my post on how to add week numbers to the gantt view.

Week Numbers in Calendar

This is the code you need to put in a content editor web part on the same page as the calendar is displayed.

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.2.min.js" type="text/javascript"></script>
<script type="text/javascript">

Date.prototype.getWeek = function () {
   //lets calc weeknumber the cruel and hard way 😀
   //Find JulianDay
   var year = this.getFullYear();
   var month = this.getMonth();
   var day = this.getDate();

   month += 1; //use 1-12
   var a = Math.floor((14 - (month)) / 12);
   var y = year + 4800 - a;
   var m = (month) + (12 * a) - 3;
   var jd = day + Math.floor(((153 * m) + 2) / 5) +
                (365 * y) + Math.floor(y / 4) - Math.floor(y / 100) +
                Math.floor(y / 400) - 32045;      // (gregorian calendar)

   //now calc weeknumber according to JD
   var d4 = (jd + 31741 - (jd % 7)) % 146097 % 36524 % 1461;
   var L = Math.floor(d4 / 1460);
   var d1 = ((d4 - L) % 365) + L;
   NumberOfWeek = Math.floor(d1 / 7) + 1;
   return NumberOfWeek;
};

function updateCampaignCalendar() {

   // calendar rows
   jQuery('TH[evtid="week"]').each(function () {

      // add week numbers
      var firstDay = new Date(jQuery(this).attr('date'));
      if (firstDay.toString() != "NaN" && firstDay.toString() != "Invalid Date") {
         var week = firstDay.getWeek(firstDay.getDay());
         jQuery(this).children("DIV").attr("innerHTML", week);
      }
   });
}

_spBodyOnLoadFunctionNames.push('calendarEventLinkIntercept');

// hook into the existing SharePoint calendar load function
function calendarEventLinkIntercept() {
   var OldCalendarNotify4a = SP.UI.ApplicationPages.CalendarNotify.$4a;
   SP.UI.ApplicationPages.CalendarNotify.$4a = function () {
      OldCalendarNotify4a();
      updateCampaignCalendar();
   }
}
</script>

Solution to: Missing server side dependencies 8d6034c4-a416-e535-281a-6b714894e1aa

November 1st, 2010
The new Health Analyzer in SharePoint 2010 is great and you should always try to clean up all errors in this list. One error I had after installing the RTM version of SharePoint Server was:
 
[MissingWebPart] WebPart class [8d6034c4-a416-e535-281a-6b714894e1aa] is referenced [X] times in the database […AdminContent…].
 
There are many suggested solutions to this problem on the internet (Accessing search administration, Querying the databasesetup SharePoint Foundation Help Search and crawl help files)
 
What worked for me was accessing these search pages in the help-site-collection in Central Administration. (just ignore the error about missing list).
http://{centraladmin}/searchfarmdashboard.aspx
http://{centraladmin}/sites/Help/_catalogs/masterpage/SearchResults.aspx
http://{centraladmin}/sites/Help/_catalogs/masterpage/PeopleSearchResults.aspx
http://{centraladmin}/sites/Help/_catalogs/masterpage/TabViewPageLayout.aspx
http://{centraladmin}/sites/Help/_catalogs/masterpage/AdvancedSearchLayout.aspx
http://{centraladmin}/sites/Help/_catalogs/masterpage/SearchMain.aspx
 
After that just reanalyze the issue in the Health Analyzer and hopefully it will dissapear.

Open SharePoint links in new Window using JQuery

October 15th, 2010

Today a customer wanted to open some of their links in a SharePoint Links List in a new window. After some googling I found this post. It solved the problem by adding a named achor to the url and then manipulating the all hyperlinks with that anchor using JavaScript. I decieded to do this using JQuery instread.

Below is the code I wrote. Put the script in a content editor web part above the web part showing the links or incorporate it into your master page. Please change the protocol for the jquery script link depending on if you run http or https.

<script src="https://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
  $("a[href$='#openinnewwindow']").each(function() {
    $(this).attr("target", "_blank");
    $(this).attr("href", $(this).attr("href").replace(/#openinnewwindow/, ''));
  });
});
</script>