Powershell: Issue with Connect-SPOService

Assuming, SharePoint Online Management Shell is installed..

First step to manage SharePoint Online tenant is connecting to the tenant. However, the following is a common error that occurs while connecting to tenant (SPO Connection) using “Connect-SPOService” Powershell command. I encountered this error when in the early days of the SharePoint online management shell. Surprisingly this still persists with the latest version. So wanted to summarize the findings.

Symptoms:

PS C:\WINDOWS\system32> Connect-SPOService -Url "https://organizationname-admin.sharepoint.com" -Credential $creds

Connect-SPOService : For security reasons DTD is prohibited in this XML document. To enable DTD processing set the

DtdProcessing property on XmlReaderSettings to Parse and pass the settings into XmlReader.Create method.

At line:1 char:1

+ Connect-SPOService -Url "https://organizationname-admin.sharepoint.com" -C ...

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo          : NotSpecified: (:) [Connect-SPOService], XmlException

+ FullyQualifiedErrorId : System.Xml.XmlException,Microsoft.Online.SharePoint.PowerShell.ConnectSPOService

Observations from Fiddler Trace:

  • A get request sent for https://msoid.organizationname.onmicrosoft.com/FPUrl.xml has been responded with content of type “text/html” instead of a valid “XML”. The role of “FPUrl.xml” is unknown.DTD Error_Fiddler
  • The returned html(in the above step) is from my ISP saying, the specified URL is not found.ISP-Response-MSOID
  • Got similar response when tried directly in the browser.
  • Another interesting thing here is – msoid, a CNAME record required at the External DNS. This is to direct the user to best server for auth-process to facilitate faster authentication response. This is not going to solve my issue, as I don’t have any custom domain. Moreover, MS’s documentation states this as requirement only for O365 operated by “21Vianet” – which, again, is not my case.(Click to See Adding DNS entries for GoDaddy)

Error Message:

  • DtdProcessing of XmlReaderSettings: Nothing to do with this setting as we do not explicitly handle the XML in our script.

Solution (1): A straight forward option to go with Web Login. This is said to be the only way when MFA is enabled, however, this comes to the rescue with this issue too.
Execute without passing the credentials..

PS C:\WINDOWS\system32> Connect-SPOService -Url "https://organizationname-admin.sharepoint.com"

This will pop a web form prompting for credentials. Once we login, it works without any issue.

Solution (2): Configure Google’s public DNS server for IPv4. This will resolve the issue. This solution is also suitable, in case the error source is a CSOM code.

DTD Error

Note: For IPv6, corresponding IPv6 value of Google’s DNS should work.

SharePoint O365!! Why I see all my site contents are updated at once a few hours ago?

Most of the sites, I’ve been working with, have not been updated in the recent past. However, one fine day I opened the site content and all the lists and libraries were showing the content was modified a few hours ago. It was not a big deal at that time to figure it out what was the reason behind those ‘all of a sudden & all at once‘ updates. Anyhow, it is time now to see what’s going on. Then I found a SPUser Voice shouting at Microsoft about the same issue( reflect the content changes not the system changes). This article is to present some findings about the ‘Unexpected Modified Dates’. And one of the factors being the REST Query. PFB, the screenshot..

   LastModified_Doclib

Recently when I retrieved a list item, I found three different date attributes came along as highlighted below.

<entry xml:base="https://organization.sharepoint.com/sites/somesite/_api/"...>
    <id>e11700c1-97d3-4451-8544-0bd6d1e4c995</id>
    <category term="SP.Data.TestFormListItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <link rel="edit" href="Web/Lists(guid'e743b81c-0245-4970-a87d-9692d42813cc')/Items(<ID>)" />
    <title />
    <updated>2016-07-20T19:58:50Z</updated>
    <author>
        <name />
    </author>
    <content type="application/xml">
        <m:properties>
            <d:Modified m:type="Edm.DateTime">2016-06-20T12:35:20Z</d:Modified>
            <d:Created m:type="Edm.DateTime">2016-05-31T11:33:56Z</d:Created>
        </m:properties>
    </content>
 </entry>
  • Created : Attribute of the content, tells when the item/document is created
  • Modified : Attribute of the content, tells when the item/document’s metadata is last modified
  • Updated : Attribute of the list item, tells when the list item is last updated.

Now, More Insight…

Before the REST Query

As we can see in the below image, the given lists were updated at the same time. BeforeRestQueried

Then I though of observing which REST query is triggering the mysterious update mechanism. Then this happened…

AfterAllListsQuery

And then this…

AfterQueryModifiedUpdated

At this point, after observing it for multiple times I came to an understanding that every first time the items are retrieved by using REST query, the date value is getting updated. Since all the above requests are get requests, I tried to access the same resources from the site contents. However, accessing so didn’t update the modified date.

I’m really ambiguous about making any further comments on this behaviour and Microsoft’s thinking about how this ‘Updated’ is more useful than ‘Modified’ time. However, whatever is the intention of Microsoft, presenting it as the last modified date to the user is so confusing/miss-leading. And it raises questions about the integrity of the product.

Interesting thing is this behaviour occurs only with REST queries. I tried the below things as well..

  • Click on link title and open an item
  • Managed client object model code equivalent to “/items”

However, the “updated” property of the list object didn’t get updated. Final words…

The modified date, for lists, we see in the site contents is getting updated when a user accesses the “items” using REST query (using either code or directly hitting the url in  browser’s address box) for the First Time – So is it for analysing the REST activities of unique uses on the list? Nay, doesn’t make sense.

However, it’s not just about the REST calls – It is one of the reasons. There must be multiple reasons causing this behaviour.

Riddles here :

  1. What is the use of the “Updated” property?
  2. Somehow, I figured out one way that is causing the “Modified” date change for lists. And the question is, what is causing the change in “Modified” dates of libraries & other objects?

Download a file from SharePoint using CSOM

Looks like a silly question. But it took me two hours to solve.
– I faced an issue while downloading a file… not from the browser, though.
My requirement was to read the data from an excel file that is stored in a document library. BTW, I was using OpenXML libraries to parse the excel data, which requires either the path of the file or the stream as a parameter. Since it is not possible to provide the path of a document that was stored in a library, I had to pass the stream as the argument.
Exactly here I encountered an issue when I tried to read the file as stream.
The name of the issue is… “NetworkStream“, one of the least discussed class.
A Brief History of “NetworkStream”:
It inherits from ‘System.IO.Stream’. And so the methods derived in it.
However, it denies any seek operation we ask for. In fact this is the first time
I found this class.

	
// The below statement returns the NetworkStream Object
// fileInfoObject is of type - Microsoft.SharePoint.Client.FileInformation
var stream = fileInforObject.Stream;

As per the documentation, it should return a standard stream. But it returns a stream object that doesn’t support any Seek operations(Length, Seek ..etc). Read here.. for the info.

In fact I tried couple of methods I found over the internet and found them of no use.
As a result of the experiments, I was left with a corrupted stream object saved to disk as a file that scolded me very badly, in machine language, when I tried to open it. Then, further search awarded me with a way to deal with the file – using the System.Net.WebClient class that provides methods for sending data to and, in my case, receiving data from a resource identified by a URI.

So, it is an easy operation as shown below when we want to get the file as stream from a site that readily authenticates us with the identity used to run the code.
Here, for authentication purpose, we can assign either NetWorkCredentials object or CredentialCache.DefaultCredentials to the WebClient.Credentials;

using (WebClient webClient = new System.Net.WebClient())
{
   // excelFileUrl - path/url of excel file that is uploaded to a document library
   
   Stream streamObject = webClient.DownloadData(excelFileUrl)
   // now I can do anything with this stream - such as...
   // 1. Saving to disk; 
   // 2. Passing as an argument to OpenXML library method to parse it.
}

It would have been end of this post if my struggle ended here. However, my actual requirement is to read the data from Office 365 SharePoint site. Here the problem is the ‘Authentication’. Since our request doesn’t carry any authentication tokens/cookies, the server rejects the request saying we don’t have access. Here I tried to use my brain but it hardly paid off. The method I tried..

 webClientObj.Credentials = sharePointOnlineCredentialsObject;

When I observed the request pattern using fiddler, I found that no FedAuth cookie is associated with the request. Then again google helped me. The solution is to make the WebClient class as Cookie aware. That is, to associate the WebClient with a cookie container that gets added the authentication cookies when we pass valid credentials.
The below method describes how to make WebClient cookie aware. We do a series of steps as..
1. Adding a cookie container to the child class – it helps storing the authentication cookies
2. Overriding the GetWebRequest method – it makes the web request associated with an authentication cookie as a header
3. while overriding, we add the UserAgent string to the web request –

In the absence of the UserAgent string, cookie will not get added to the container. By adding it, our web request mimics a request sent by a browser. This is any valid UserAgent string – need not be the one I’ve given below.

// Method [1]
// This class extends the capabilities of the WebClient class by adding 
// a CookieContainer to it.
class AuthenticatedWebClient : System.Net.WebClient
{
public System.Net.CookieContainer WebClientCookieContainer { get; private set; }
public AuthenticatedWebClient()
{
WebClientCookieContainer = new System.Net.CookieContainer();
}
protected override WebRequest GetWebRequest(Uri url)
{
var request = (HttpWebRequest)base.GetWebRequest(url);
//Adds the existing cookie container to the Request
request.CookieContainer = WebClientCookieContainer;
request.UserAgent = "Mozilla/5.0 (Windows NT 6.0; rv:12.0) Gecko/20100101 Firefox/12.0";
return request;
}
}

Now the downloading part – The below snippet tells how to pass credentials along with the Cookie aware WebClient class. We pass credentials, using the NaveValueCollection object, to the login url of the Office 365 site.

using (AuthenticatedWebClient authenticatedWebClient = new AuthenticatedWebClient())
{
System.IO.Stream filestream = null;
var values = new NameValueCollection{{ "username", "saratchandra@myorganization.com" },{ "password", "mypassword" }};
authenticatedWebClient.UploadValues("https://login.microsoftonline.com/login.srf", values);
string url = "https://myorganization.sharepoint.com/sites/development/Documents/TestRule.xlsx";
byte[] content = authenticatedWebClient.DownloadData(url);
filestream = new System.IO.MemoryStream(content);

// The below code is a custom helper method to parse the excel data
OpenXmlExcelHelper.GetExcelData(filestream);
}

With the method explained above, everything seemed to be under control. However, the result again was an incomplete chunk of byte stream – which again is not useful.

This time I really had to use my brains and…this time it worked.. The below is the code snippet for that. A simple way of making a WebClient cookie aware without struggling much.
Here, the `credentials` object is of type – SharePointOnlineCredentials

// Method [2]
using (WebClient webClient = new System.Net.WebClient())
{
webClient.Headers.Add("Cookie", credentials.GetAuthenticationCookie(new Uri("https://myorganization.sharepoint.com/sites/development")));
string url = "https://myorganization.sharepoint.com/sites/development/Documents/TestRule.xlsx";
byte[] content = webClient.DownloadData(url);
filestream = new System.IO.MemoryStream(content);

// custom method to parse excel data
OpenXmlExcelHelper.GetExcelData(filestream);
}

Demystified : Breaking the Role Inheritance of a SecurableObject with CSOM

Managing permissions is a very important aspect of SharePoint. As there are so many techniques to authenticate a user to a SharePoint application, the permissions part takes care of the authorization. There are so many articles that discuss the permissions levels, groups etc to a great extent.

In this post I’m going to discuss the following things.

  • Code snippet to break role inheritance of a Securable Object – In current example a list
  • Analyzing the code with User Interface
   
public static void AssignUniquePermissions(string ctxUrl,string listTitle)
{
	ClientContext ctx = new ClientContext(ctxUrl);
	List targetList = ctx.Web.Lists.GetByTitle("listTitle");
	ctx.Load(targetList);
	
	// the below line of code breaks the Permission Inheritane
	// The below piece of code is equivalent to...
	targetList.BreakRoleInheritance(true,false);

	/* PermissionKind => is an Enum type that represents 
        all the permissions available */
	/* BasePermissions => is a class to which we set the Selected 
        Permissions. Later we use 'BasePermissions' object in creating 
        the 'RoleDefinition' */
	BasePermissions permissions = new BasePermissions();
	permissions.Set(PermissionKind.ViewListItems);
	permissions.Set(PermissionKind.AddListItems);
	permissions.Set(PermissionKind.EditListItems);
	permissions.Set(PermissionKind.DeleteListItems);


	/* Create a new role definition => Creating a Permission Level 
        @ /_layouts/addrole.aspx of site collection. */
        //The below piece of code is equivalent to...
	RoleDefinitionCreationInformation roleDefInfo = new RoleDefinitionCreationInformation();
	// Permission Level Name
	roleDefInfo.Name = "ExampleEditListItems1";
	roleDefInfo.Description = "Allows a user to manage list items";
	roleDefInfo.BasePermissions = permissions;
	/* Adds RoleDefinition => Adds Permission Level 
        @ /_layouts/role.aspx */
	RoleDefinition roleDef = ctx.Site.RootWeb.RoleDefinitions.Add(roleDefInfo);

	// Creating another RoleDefinition
	BasePermissions permissions2 = new BasePermissions();
	permissions2.Set(PermissionKind.ApproveItems);
	RoleDefinitionCreationInformation roleDefInfo2 = new RoleDefinitionCreationInformation();
	// Permission Level Name
	roleDefInfo2.Name = "ExampleEditListItems2";
	roleDefInfo2.Description = "Allows a user to Approve Items";
	roleDefInfo2.BasePermissions = permissions2;
	RoleDefinition roleDef2 = ctx.Site.RootWeb.RoleDefinitions.Add(roleDefInfo2);

	/* The permissions we are assigning to the User/Group
	Equivalent to 'Grant User Permissions Directly
        (in this example) by selecting multiple permission 
        levels */
        // The below piece of code is equivalent To..
	RoleDefinitionBindingCollection permissionLevelsCollection = new RoleDefinitionBindingCollection(ctx);
	// Add the role to the collection.
	permissionLevelsCollection.Add(roleDef);
	permissionLevelsCollection.Add(roleDef2);

	// User user = ctx.Web.EnsureUser("Domain\\userId"); (or)
	Group group = ctx.Web.SiteGroups.GetById(12);

	/* The page that contains all the Permissions. => 
        @ _layouts/user.aspx of the corresponding securable object */
	RoleAssignmentCollection roleAssignmentCollection = targetList.RoleAssignments;
	RoleAssignment roleAssignment = roleAssignmentCollection.Add(group, permissionLevelsCollection);

	ctx.ExecuteQuery();
	Console.WriteLine("Done!!");
	Console.ReadKey();
}

targetList.BreakRoleInheritance(true,false);

This above line of code is equivalent to clicking on the ‘Stop Inheriting Permissions’ option in the ribbon

breakRoleInheritance1

What is the difference between BreakRoleInheritance(true,true/false)?
This function breaks the role inheritance of the SecurableObject on which we are calling the function in both cases.
The second parameter(true/false) acts on the Child objects of the SecurableObject on which we called the function.
true: It makes the children of the current SecurableObject inherit RoleAssignments from the current SecurableObject even though they have UniqueRoleAssignments on them.
false: It allows the children of the current SecurableObject have their own RoleAssignments(UniqueRoleAssignments) if they have any.
Back To Code

Creating a RoleDefinition:

Creating a RoleDefinition is equivalent to creating a new permission level from the SharePoint UI @ ‘addrole.aspx’ of the root web

// Adds the permission level to the collection of permission levels at the root web level
RoleDefinition roleDef = ctx.Site.RootWeb.RoleDefinitions.Add(roleDefInfo); 

Everything I mentioned in the below image happens only after executing the query.
You find this info when you click on the Permission Level created @
~siteCollectionUrl/_layouts/editrole.aspx?role=ExampleEditListItems1
createRoleDef
Back To The Code

Granting Permissions To a User Or Group:

Adding RoleDefinition Objs to RoleDefinitionBindingCollection obj is equivalent to
select the multiple permission checkboxes as shown in the below image.

	permissionLevelsCollection.Add(roleDef);
	permissionLevelsCollection.Add(roleDef2);

grantPermissions

	// if we want to grant permissions to a user
	User user = ctx.Web.EnsureUser("Domain\\userId");
	(or)
	// if we want to grant permissions to a Group
	// The group id '12' represents 'TestGroup1' as shown in the above image
	Group group = ctx.Web.SiteGroups.GetById(12);
	
	// This line of code grants permissions to the Group
	RoleAssignment roleAssignment = roleAssignmentCollection.Add(group, permissionLevelsCollection);
	// This line of code grants permissions to the User
	RoleAssignment roleAssignment = roleAssignmentCollection.Add(user, permissionLevelsCollection);

Get number of hits to a page – SharePoint(2013) Analytics

This post briefly explains how we can get the total number of hits to a page in SharePoint. In SharePoint 2013 Usage Analytics is integrated into Search Service Application (Pretty old news, a pre-historic event).
SharePoint analytics is useful in improving the Search experience by letting know the most popular items or most visited pages.
The code I’m going to explain will work fine. However, the below are the things to check before proceeding.

  1. Search service application must be attached to the web application(Central Admin -> Manage WebApplications -> Service Connections)
  2. Windows Timer service must be running

Know the role of Timer Service and SSA here

public static int GetNumberOfHitsPerPage(Guid siteGuid, SPListItem item)
{
   int totalHits = 0;
   
   // Total hits per page
   using (SPSite siteCollection = new SPSite(siteGuid))
   {
     using (SPWeb rootWeb = siteCollection.OpenWeb())
     {
        // Method 1:
        // How it works?
         SPServiceContext serviceContext = SPServiceContext.GetContext(siteCollection);
         ISearchServiceApplication searchServiceApplicationProxy = SearchServiceApplicationProxy.GetProxy(serviceContext);
         SPList pagesList = item.ParentList;
        // What is '1' stands for in the below line of Code?
         AnalyticsItemData analyticsItemData = searchServiceApplicationProxy.GetAnalyticsItemData(1, pagesList.ID, siteGuid, Convert.ToString(item.ID));
         totalHits = analyticsItemData.TotalHits;

        // Method 2:
        // How it works?
        // using UsageAnalytics Class
         UsageAnalytics analyticsData = new UsageAnalytics(siteCollection);
        // What is '1' stands for in the below line of Code?
         AnalyticsItemData analyticsItemData2 = analyticsData.GetAnalyticsItemData(1, item);
         totalHits = analyticsItemData2.TotalHits;
       }

       return totalHits;
    }
}

What is ‘1’ stands for in the below line of Code? Ans: EventTypeId

Here 1,2,3 and 4 are EventTypeIds. I believe there should have been a Enum type that represents these events instead of sending them as bare numbers.
1 – Views (number of times an item is viewed)
2 – Recommendation Displayed (number of times an item appeared in the recommendations – Popular Items/Recommended Items WebParts)
3 – Recommendation Clicked (number of times the link to an item was clicked when displayed in the recommendations)
4 – Search Queries (internal event for reporting purposes)

Method 1:

Important step in this method is getting the Service Context using SPServiceContext.GetContext(..):
We have two overloads of this method.

  1. GetContext(SPSite):
    Takes site collection object as a parameter. Then gets the Service Application ProxyGroup
    associated to the web application in which the site collection resides. Then creates the
    SPServiceContext object using the Service Application Proxy group object and id(of type GUID)
    of the Site Subscription object(spSiteObject.SiteSubscription)
    What is a Site Subscription?

  2. GetContext(HttpContext):
    Takes HttpContext object as parameter. Then gets the SPServiceContext value in one of the following ways and returns the object

    				// httpContext.Items is of IDictionary type
    				(SPServiceContext) httpContext.Items["Microsoft.SharePoint.SPServiceContext"]; // When HttpContext is not null
    				(or)
    				(SPServiceContext) SPThreadContext.Get("Microsoft.SharePoint.SPServiceContext"); // When HttpContext is null
    				

After getting the Service Context it’s pretty straight forward. Next we get the SSA proxy and from that analytics data.

Method 2:

In this method as a first step we create the object of ‘UsageAnalytics’ sealed class.
UsageAnalytics class has only one parameterized constructor that takes ‘SPSite’ object
as input parameter.
Then we get the ‘AnalyticsItemData’ object, contains the historical analytics data
for an item, from which we get the number of times the page was visited.