Friday, May 20, 2016

One reason for The request uses too many resources error when create sub site in Sharepoint Online

Recently we faced with the following issue: when users with standard Manage hierarchy permission level (according to description of this permission level, user with these permissions “can create sites and edit pages, list items, and documents”) tried to create sub site in Sharepoint Online the following exception was thrown:

ServerException: Provisioning did not succeed. Details: Failed to initialize some site properties for Web at Url: 'http://example.com' OriginalException: The request uses too many resources.

StackTrace: at Microsoft.SharePoint.Client.ClientRequest.ProcessResponseStream(Stream responseStream) at Microsoft.SharePoint.Client.ClientRequest.ProcessResponse() at Microsoft.SharePoint.Client.ClientRequest.ExecuteQueryToServer(ChunkStringBuilder sb) at Microsoft.SharePoint.Client.ClientRequest.ExecuteQuery() at Microsoft.SharePoint.Client.ClientRuntimeContext.ExecuteQuery() at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery() at Microsoft.SharePoint.Client.ClientContextExtensions.ExecuteQueryImplementation(ClientRuntimeContext clientContext, Int32 retryCount, Int32 delay)

We tried to increase server resource quota, but it didn’t help. The problem existed both for sites created from custom SP app and for standard site templates.

In order to avoid this error the following steps can be done: go to Site settings > Site permissions > Permission levels > Manage hierarchy and check the following permissions (by default they are unchecked for Manage hierarchy permission level):

  • Apply Themes and Borders – Apply a theme or borders to the entire Web site
  • Apply Style Sheets – Apply a style sheet (.CSS file) to the Web site

So they will look like this:

After that hierarchy managers should be able to create sub sites.

Tuesday, May 10, 2016

How to set author displayed in Mercurial commits on Windows

Note for readers: in this article I will show how to configure author name on Windows PC for future Mercurial commits. It won’t contain information how to change author for commits which were already done.

If you work with different repositories sometime it may be necessary to change Author field displayed in commits history. On Windows PCs in order to do that go to C:\Users\{username} and edit mercurial.ini file:

[ui]
username = developer

In this example “developer” is the user name which will be displayed in Author field. If you will now open TortoiseHG Workbench tool new user name should be displayed in Author column.

Wednesday, May 4, 2016

How to get user’s login name in Sharepoint Online via javascript object model

In this post I will show how to achieve simple task: get login name of the user via javascript object model. The main misleading thing is that in current version of MSDN documentation SP.User object contains only the following properties:

- email
- groups
- isSiteAdmin
- userId

You might think that email property can be used as login name (because in Sharepoint Online users are authenticated using their email – organizational or external connected to MS live id). However some time ago the following note was added to this property in MSDN:

This property is not available in SharePoint Online.

So how to get login name then? The answer is to use not-documented loginName property which will return you login name of the user in claims format. E.g. the following example shows how to get login name of the current user via javascript object model in Sharepoint Online:

   1: var ctx = SP.ClientContext.get_current();
   2: var currentUser = ctx.get_web().get_currentUser();
   3: ctx.load(currentUser);
   4:  
   5: ctx.executeQueryAsync(
   6:     Function.createDelegate(this, function (sender, args) {
   7:         console.log("Login name: " + currentUser.get_loginName());
   8:     }),
   9:     Function.createDelegate(this, function (sender, args) {
  10:         console.log("Error: " + args.get_message()
  11:     }));

Hope that this information will help someone.

Problem with creating cross-site collection SP.ClientContext and metadata filters in Sharepoint document libraries

Recently we faced with interesting problem in Sharepoint Online site: in one of document libraries we had custom ribbon button. Javascript handler of this button created SP.ClientContext object for different site collection and retrieved some data from this site collection:

   1: var otherSiteCtx = new SP.ClientContext("http://example.com/sites/foo");
   2: var otherSiteWeb = otherSiteCtx.get_web();
   3: otherSiteCtx.load(otherSiteWeb);
   4: otherSiteCtx.executeQueryAsync(
   5:     Function.createDelegate(this, function (sender, args) { ... }),
   6:     Function.createDelegate(this, function (sender, args) {
   7:         console.log("Get site failed: " + args.get_message());
   8:     }));

It worked until we activated Metadata navigation and filtering feature and added Metadata filter to this document library. After that code above started to throw exception:

Unexpected response from server. The status code of response is '403'. The status text of response is 'FORBIDDEN'

In order to avoid the problem we had to disable metadata filter for this document library, i.e. in doclib settings > Metadata navigation settings remove all fields from filter list:

After that javascript handler started to work again. It looks like issue in Sharepoint internal javascript implementation. If you know better solution, please share it in comments.

Friday, April 22, 2016

Get current Sharepoint list or document library via javascript object model

In server object model in Sharepoint there is simple way to get current list (i.e. list which is currently opened in browser window):

   1: var list = SPContext.Current.List;

In javascript object model it is not so obvious, but also possible. In order to get current list you have to use not very well known function GetCurrentCtx() defined in core.js. With this function we can get current list like this:

   1: var ctx = SP.ClientContext.get_current();
   2:  
   3: var currentCtx = GetCurrentCtx();
   4: var list = ctx.get_web().get_lists().getByTitle(currentCtx.ListTitle);
   5: ctx.load(list);
   6:  
   7: ctx.executeQueryAsync(
   8:     Function.createDelegate(this, function (sender, args) {
   9:         // use list here
  10:     }),
  11:     Function.createDelegate(this, function (sender, args) {
  12:         console.log("Error occured: " + args.get_message() }));

In this example the main difference between context created with SP.ClientContext.get_current() and context returned from GetCurrentCtx() is that last one has ListTitle property set to title of the current list or document library. Having it we can easily get list object from the web. Also this context contains other useful properties like listBaseType, listName, listTemplate and listUrlDir.

Tuesday, April 19, 2016

Avoid “Value does not fall within the expected range” error when read list item with taxonomy fields via javascript in Sharepoint Online

When list contain many taxonomy fields and you try to read list item with all these fields using CAML query in javascript:

   1: var ctx = SP.ClientContext.get_current();
   2: var list = ctx.get_web().get_lists().getByTitle("MyList");
   3: var query = new SP.CamlQuery();
   4: query.set_viewXml('<View>' +
   5:                       '<Query>' +
   6:                           '<Where>' +
   7:                           '</Where>' +
   8:                       '</Query>' +
   9:                       '<RowLimit>1</RowLimit>' +
  10:                   '</View>');
  11: var items = list.getItems(query);
  12: ctx.load(items, 'Include(Field1,Field2,Field3,...)');
  13: ctx.executeQueryAsync(Function.createDelegate(this, function (sender, args) { /* */ }),
  14:     Function.createDelegate(this, function (sender, args) { console.log("Error: " +
  15:         args.get_message() + "\n" + args.get_stackTrace()); }));

you may get the following error:

Value does not fall within the expected range error

It happens because of List Lookup Threshold Limit which is set to 12 in Sharepoint Online (see Increased site collection and list lookup limits). In order to avoid this error use the following workaround: at first read necessary item to get it’s id. Then load this item using List.getItemById method which will return list item containing all fields without need to explicitly enumerate them in “Include(…)” statement:

   1: var ctx = SP.ClientContext.get_current();
   2: var list = ctx.get_web().get_lists().getByTitle("MyList");
   3: var query = new SP.CamlQuery();
   4: query.set_viewXml('<View>' +
   5:                       '<Query>' +
   6:                           '<Where>' +
   7:                           '</Where>' +
   8:                       '</Query>' +
   9:                       '<RowLimit>1</RowLimit>' +
  10:                   '</View>');
  11: var items = list.getItems(query);
  12: ctx.load(items);
  13: ctx.executeQueryAsync(
  14:     Function.createDelegate(this, function (sender, args) {
  15:         getPropsSuccess(ctx, list, items);
  16:     }),
  17:     Function.createDelegate(this, function (sender, args) {
  18:         console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
  19:     })
  20: );
  21:  
  22: function getPropsSuccess(ctx, list, items) {
  23:     var itemId = null;
  24:     var enumerator = items.getEnumerator();
  25:     while (enumerator.moveNext()) {
  26:         var item = enumerator.get_current();
  27:         itemId = item.get_id();
  28:         break;
  29:     }
  30:  
  31:     if (itemId == null) {
  32:         return;
  33:     }
  34:  
  35:     var item = list.getItemById(itemId);
  36:     ctx.load(item);
  37:     ctx.executeQueryAsync(
  38:         Function.createDelegate(this, function (sender, args) {
  39:             // here you may get values of all fields in the list
  40:             // item.get_item("Field1"), item.get_item("Field2"), item.get_item("Field3"), ...
  41:         }),
  42:         Function.createDelegate(this, function (sender, args) { console.log("Error: " +
  43:             args.get_message() + "\n" + args.get_stackTrace()); }));
  44: },

In this example we read all items from the list first and get id of the 1st item only, but you may retrieve item using your own criteria of course by specifying appropriate CAML query. With this approach it is possible to get values of all fields from the list in list item.

Thursday, April 14, 2016

Issue with JQuery ajax request which uses CORS when JSONP is explicitly specified in request options

Sometime ago we faced with interesting issue: we use JSONP request via jQuery.ajax() to send data to another domain:

   1: jQuery.ajax({
   2:     url: url,
   3:     data: { ... },
   4:     dataType: "jsonp",
   5:     success: function(msg) {
   6:         ...
   7:     }
   8: });

On most sites it works as expected, i.e. it sends HTTP GET request and passes JSONP callback function name in query string parameter. But on one site because of some reason it sends HTTP OPTION verb to the same url, which as you probably know, happens when using CORS. Since our endpoint didn’t support CORS we got errors:

Chrome:

XMLHttpRequest cannot load http://example1.com. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin http://example2.com is therefore not allowed access. The response had HTTP status code 404.

FF:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://example1.com. (Reason: CORS header 'Access-Control-Allow-Origin' missing).

IE:

XMLHttpRequest: Network Error 0x80070005, Access is denied.

In order to fix the issue, i.e. in order to force jQuery to use JSONP instead of CORS additional parameter “crossDomain: true” should be added to request options:

   1: jQuery.ajax({
   2:     url: url,
   3:     data: { ... },
   4:     dataType: "jsonp",
   5:     crossDomain: true,
   6:     success: function(msg) {
   7:         ...
   8:     }
   9: });

After that jQuery will start to use JSONP. However the question why it implicitly uses CORS on some sites even if we pass dataType: “jsonp” in request options is still open. If you will find the reason please share it in comments.