Friday, May 22, 2015

Get Sharepoint current datetime in javascript for Sharepoint Online

First of all let’s clarify what I mean by Sharepoint current datetime in the title. This is the datetime which you can see in Created column when create new item in the list or upload new document to the doclib. As you probably know internally Sharepoint stores all datetimes in content database in UTC format. In the Site settings > Regional settings it is possible to specify timezone for the current site. Depending on selected timezone Sharepoint will show datetimes to end users (if you will change timezone and update list view, values in Created and Modified fields will be changed). I.e. Sharepoint current datetime is not always the same as server date time which is set in OS.

On practice we often need to get Sharepoint current datetime and server datetime, e.g. when want to display news which are not older than N days. In order to do that we will get server current datetime and compare it with Created/Modified fields of the news. The question is how to get this datetime in the javascript? We are talking about javascript because this is the basic way currently to create custom components for Sharepoint Online.

One of the ways is to use _spPageContextInfo.clientServerTimeDelta variable which is defined like that:

   1: clientServerTimeDelta: new Date("2015-05-21T16:54:00.0000000Z") - new Date()

If you will check my other article How to get URL of current site collection and other server side properties on client site in Sharepoint, you will find that date defined in string in example above is current UTC date time:

   1: sb.Append("\", clientServerTimeDelta: new Date(\"");
   2: sb.Append(DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture));
   3: sb.Append("\") - new Date()");

In javascript “new Date()” will create datetime object created for the local client’s timezone. I.e. _spPageContextInfo.clientServerTimeDelta contains bias between client’s current datetime and server’s UTC current datetime. When we need server’s current UTC datetime for comparing it with Created/Modified date, we may do it like this:

   1: var d = new Date();
   2: var serverDateTimeNow = _spPageContextInfo.clientServerTimeDelta + d;

There will be difference with actual server time depending on how late above code will be executed, but not crucial. The problem is that _spPageContextInfo.clientServerTimeDelta may not be always available on your page, so let’s consider other approach as well.

Interesting solution was posted by colleague Vadim Gremyachev here: Get current server datetime through javascript REST. However you may face with cross domain request problem when will try to use this approach in Sharepoint app (apps have own sub domains). Also when offset is calcuated:

   1: var offset = data.d.Information.Bias / 60.0;

it doesn’t takes into consideration daylight bias. I.e. more correct approach is the following:

   1: var offset = (data.d.Information.Bias + data.d.Information.DaylightBias) / 60.0;

(there is also StandardBias property but it is set to 0 in examples I saw. For safety you may add it as well). In order to be able to use it in Sharepoint app javascript object model should be used:

   1: var context = new SP.ClientContext(appWebUrl);
   2: var factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl);
   3: context.set_webRequestExecutorFactory(factory);
   5: var hostContext = new SP.AppContextSite(context, postsArchiveURL);
   6: var web = hostContext.get_web();
   7: context.load(web);
   9: context.executeQueryAsync(function () {
  10: create new document or item)
  11:     var regionalSettings = web.get_regionalSettings();
  12:     context.load(regionalSettings);
  13:     context.executeQueryAsync(
  14:         function () {
  15:             var timeZone = regionalSettings.get_timeZone();
  16:             context.load(timeZone);
  17:             context.executeQueryAsync(
  18:                 function () {
  19:                     var info = timeZone.get_information();
  20:                     var offset = (info.get_bias() + info.get_daylightBias()) / 60.0;
  21:                     var serverDateTimeNow =
  22: new Date(new Date().getTime() - offset * 3600 * 1000).toISOString();
  23:                     console.log("serverDateTimeNow: " + serverDateTimeNow);
  24:                 },
  25:                 function (sender, args) {
  26:                     console.log(args.get_message());
  27:                 }
  28:             );
  29:         },
  30:         function (sender, args) {
  31:             console.log(args.get_message());
  32:         }
  33:     );
  34: }, function(sender, err) {
  35:     console.log(err.get_message());
  36: });

I intentially converted serverDateTime variable to string using toISOString() function, because in this format it may be added to the CAML query for comparing with Created or Modified date.

In order to get Sharepoint current datetime we need to combine 2 approaches: to the server’s current UTC datetime

   1: var serverDateTimeNow = _spPageContextInfo.clientServerTimeDelta + d;

add offset like shown in example above. As result we will get time which is almost the same that is shown in Created/Modified fields. Hope that this information will help you in your work.

Update 2016-12-23. There is an issue with the shown approach of calculating serverDateTimeNow. See another post Get current server date time via javascript object model in Sharepoint which shows more correct way to calculate:

   1: var currentServerDateTime = new Date(new Date().getTime() + _spPageContextInfo.clientServerTimeDelta);

with last approach you will get correct datetime object in serverDateTimeNow variable.

Thursday, May 21, 2015

Enumerate all tenant’s site collections in Sharepoint Online via PowerShell

It is quite easy to enumerate all site collections via PowerShell for on-premise Sharepoint (see e.g. Set search settings in all site collections of Sharepoint web application via PowerShell), but for Sharepoint Online it is more tricky. C# solution which uses client object model was posted in the following article: Get list of site collections using CSOM in Office365. Let’s try to do the same in PowerShell. We will need Microsoft.Online.SharePoint.Client.Tenant.dll library which can be obtained from nuget.

Here is the script:

   1: param(
   2:     [string]$adminWebAppUrl,
   3:     [string]$login,
   4:     [string]$password
   5: )
   7: $currentDir = Convert-Path(Get-Location)
   8: $dllsDir = resolve-path($currentDir + "\dlls")
  10: [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($dllsDir,
  11: "Microsoft.SharePoint.Client.dll"))
  12: [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($dllsDir,
  13: "Microsoft.SharePoint.Client.Runtime.dll"))
  14: [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($dllsDir,
  15: "Microsoft.SharePoint.Client.Taxonomy.dll"))
  16: [System.Reflection.Assembly]::LoadFile([System.IO.Path]::Combine($dllsDir,
  17: "Microsoft.Online.SharePoint.Client.Tenant.dll"))
  19: if (-not $adminWebAppUrl)
  20: {
  21:     Write-Host "Specify admin web app url in adminWebAppUrl parameter"
  22: -foregroundcolor red
  23:     return
  24: }
  26: if (-not $login)
  27: {
  28:     Write-Host "Specify user name in login parameter" -foregroundcolor red
  29:     return
  30: }
  32: if (-not $password)
  33: {
  34:     Write-Host "Specify user password in password parameter" -foregroundcolor red
  35:     return
  36: }
  38: function Do-Something($url)
  39: {
  40:     Write-Host "Working with $url" -foregroundColor green
  41:     # ... add your logic here
  42: }
  44: # initialize client context
  45: $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($siteURL)    
  46: $clientContext.RequestTimeOut = 1000 * 60 * 10;
  47: $clientContext.AuthenticationMode =
  48: [Microsoft.SharePoint.Client.ClientAuthenticationMode]::Default
  49: $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
  50: $credentials =
  51: New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username,
  52: $securePassword)
  53: $clientContext.Credentials = $credentials
  54: $web = $clientContext.Web
  55: $site = $clientContext.Site
  56: $clientContext.Load($web)
  57: $clientContext.Load($site)
  58: $clientContext.ExecuteQuery()
  60: # enumerate all site collections
  61: $web = $clientContext.Web
  62: $tenant = New-Object "Microsoft.Online.SharePoint.TenantAdministration.Tenant"
  63: -ArgumentList $clientContext
  64: $props = $tenant.GetSiteProperties(0, $true)
  65: $clientContext.Load($props)
  66: $clientContext.ExecuteQuery()
  68: foreach($sp in $props)
  69: {
  70:     Do-Something $sp.Url
  71: }

The important moment is that we need to provide Sharepoint admin center URL as parameter for this script, not URL of any real site collection. In script we initialize client context (lines 45-58), enumerate site collections using Tenant class from Microsoft.Online.SharePoint.Client.Tenant.dll (lines 61-71) and for each site collection call our custom function. Hope that it will be helpful.