Archive for SharePoint Online

Elevating User permissions in SharePoint Hosted App Web

Lately we’ve done some SharePoint Hosted App development and came across a Permission Issue when it comes to the App Web. Also inspired by this thread on the MSDN forums: http://social.msdn.microsoft.com/Forums/sharepoint/en-US/a34b1a2d-40d4-46d8-838b-1ca90dc860a2/user-permissions-inside-a-sharepoint-hosted-app-web we decided to follow up on this challenge.

Host Web vs. App Web

There are lots of different opinions when it comes to the subject of Host Web vs. App Web. Many people believe that in order to be completely isolated from “Out of the Box” SharePoint, all App data must be stored and managed within the App Web. This eliminates the need of complex App Permissions on the Host Web and the risk of rejection by users that don’t want to install the App because of those. The drawback of this however in my experience is that by default an App Web fully inherits it’s permissions from the Host Web. Meaning users that have read permissions on the Host Web aren’t allowed to create, update or delete the data stored in Lists in the App Web. In some scenario’s you want even users that are just visitors on the Host Web, to work with your App and therefor be able to update data stored in the App Web. What to do?

Setting Permissions in the App Web

Fortunately for us, the App Web is very similar to just any SharePoint SPWeb object. It’s possible to manage the Permissions in the App Web the same way we do on the Host Web, by using the Applications Page “/_layouts/15/user.aspx”. Of course this page is only available to users with the right permissions on the Host Web (due to the inheritance). But if you navigate to this page from your App Web we can see what’s going on with the Permissions.

PermissionsAppWeb

If you have a List deployed in the App Web that holds App Data just add a QueryString to this page “?List=[ListId]” and you’ll be able to see the permissions of that specific List.

Break the chain

In our case we needed All Users to be contributors to this List but without touching the Host Web’s permissions. Because we had just one List deployed in our App we decided to Break the Inheritance on the App Web level and so the List inherits all Permissions from the App Web level. We needed a one time event by a user that is allowed to do this on the App Web in the first place. This is were we need App Permissions on the Host Web. The idea is to let an admin install the App. When asked for App Permissions, the admin grant the permissions thereby redirecting him to the App Web after installation and allow us to let him “Initialize” the App. That initialization step executes some JavaScript that breaks role inheritance and adding Contribute Permissions to the group Everyone on the App Web.

App Installation

When dealing with App Permissions obvisouly the “Least Priviliges” Approach is best practice. In this particular scenario it doesn’t really matter what App Permissions are granted in terms of abuse. Since the App doesn’t have any interaction with the Host Web we don’t load any libraries that let users make Cross Domain calls back to the Host Web. So even if the App Permissions are set to “Manage Web” users will not likely to be able to make CSOM calls back to the Host Web unless they are very skilled. In this example I’ll use the Manage Web Permissions for the App.

AppPermissions

Now when an Admin Trusts this App he will immediately be redirected to the Default Page of the App Web.

Redirect

Now we have the event set up to get some cool script going for changing the App Web’s permissions.

Break Role Inheritance

In order to break the Role Inheritance and add a new Role Assignment we used the example from Yuri Leontyev:http://spsite.pro/Blog/Post/3/SharePoint-2013-REST-API-%E2%80%93-How-to-set-Unique-Permissions-(Item-Level-Permissions)

My function to break role inheritance:

P42.UpdateAppPermissions = function ($) { 
 var execute = function (appWebUrl) {
  var deferred = $.Deferred();
  $.ajax({
    url: appWebUrl + "/_api/web/breakroleinheritance(copyRoleAssignments = true, clearSubscopes = true)",
    type: 'POST',
    headers:{ "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() },
    success: function () { deferred.resolve(true); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return { 
  execute: execute
 }
}(jQuery);

This will only break the role inheritance between the Host Web and the App Web, but also copies the default permissions.

UniquePermissions

Add Role Assignments

The second part is to get the Principal ID of the group “Everyone” and add a Role Assignment that says Everyone has Contribute permissions. We will need two functions for that.

P42.GetEveryone = function ($) {
 var execute = function (appWebUrl) {
  var url = "/_api/web/siteusers?$filter=Title eq 'Everyone'&$select=Id";
  var deferred = $.Deferred();
  $.ajax({ 
    url: appWebUrl + url,
    type: 'GET',
    headers: { "accept": "application/json;odata=verbose" },
    success: function (data) { deferred.resolve(data); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

P42.AddEveryone = function ($) {
 var execute = function (appWebUrl, principalId) {
  var deferred = $.Deferred();
  var ctx = SP.ClientContext.get_current();
  var web = ctx.get_web();
  var roleDefinition = web.get_roleDefinitions().getById(1073741827);
  // Create a new RoleDefinitionBindingCollection
  var newBindings = SP.RoleDefinitionBindingCollection.newObject(ctx);
  // Add the role to the collection
  newBindings.add(roleDefinition);
  // Get the RoleAssignmentCollection for the target list
  var assignments = web.get_roleAssignments();
  // Add the user to the target list and assign the use to the new RoleDefinitionBindingCollection
  var roleAssignment = assignments.add(web.getUserById(principalId), newBindings);
  ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

To obtain the ID of the default Contribute permission we can use a simple REST call on any SharePoint Web. This ID is fixed and always usable. From this overview we can also see other Permissions we could set if we like.

https://your_sp_site.sharepoint.com/_api/web/roledefinitions​ 

image

Run Once

Ok now the script part only has to run once. In order for that to happen there are lots of different options, but the one I always remembered from early SharePoint Development is setting a Property in the property bag that indicates the script has already been executed. I’ve created two functions, one to check if the Property has been set and one to actually set it.

P42.GetInitProp = function ($) {
 var execute = function () {
  var deferred = $.Deferred();
  var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/AllProperties/?$select=" + "_AppInit";
  $.ajax({
    url: url,
    type: 'GET',
    headers: { "accept": "application/json;odata=verbose" },
    success: function (data) { deferred.resolve(data); },
    error: function (sender) { deferred.reject(sender); }
  });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

 
P42.SetInitProp = function ($) {
 var execute = function (value) {
  var deferred = $.Deferred();
  var ctx = SP.ClientContext.get_current();
  var web = ctx.get_web();
  var properties = web.get_allProperties();
  properties.set_item("_AppInit", value);
  web.update();
  ctx.load(web);
  ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
  return deferred;
 };
 return {
  execute: execute
 }
}(jQuery);

On the default page we can use this piece to check the Property.

P42.GetInitProp.execute().promise().then(function (data) {
 if (data.d.OData__x005f_AppInit) {
  jQuery('#AfterInit').show();
 }else {
  jQuery('#PreInit').show();
 }
}, function (sender, args) { 
 //Error 
});

Where my property in this case is “_AppInit”.

Promises

Now the final piece of this puzzle is to make all these functions run in controlled flow. You may have noticed there is quite some dependency on one another hence the jQuery Deferred that is in all of these functions. The one function that makes all these calls happen is:

initApp = function (appWebUrl) {
 P42.UpdateAppPermissions.execute(appWebUrl).promise().then(function (success) {
  P42.GetEveryone.execute(appWebUrl).promise().then(function (data) {
   var principalId = data.d.results[0].Id;
   P42.AddEveryone.execute(appWebUrl, principalId).promise().then(function () {
    //Create Web Property
    P42.SetInitProp.execute(true).promise().then(function () { 
      //Success! Do other stuff 
     }, 
     function (sender, args) { 
      console.log(args.get_message() + '\n' + args.get_stackTrace());
    });
   }, function (sender, args) {
    console.log(args.get_message() + '\n' + args.get_stackTrace());
   });
  }, function (sender, args) {
   console.log(args.get_message() + '\n' + args.get_stackTrace());
  });
 }, function (sender, args) {
  console.log(args.get_message() + '\n' + args.get_stackTrace());
 });
};

Basically this is it. We do need to pass the AppWebUrl to this method. I think some of this code can actually be done nicer, but just for this example it should work.

image

Final Considerations

With these pieces of code it is easy to set the App Permissions the way you want to in one initial step however this workaround is not really ideal. Even though it’s hard and users have to be real code guru’s, it is possible to abuse the App Permissions we granted. Using GetScript and the Cross Domain Library it’s possible to hack your way into the Host Web,  but frankly this is always the case when using App Permissions.

Besides that using an Initial step that must be done by an Admin is also not ideal. Still with the given tools at the moment I think this is the best solution yet to take control of the permissions in the App Web. Hopefully this will improve in the future.

And finally another great explanation on why Breaking Role Inheritance is not that good of a practice. A very well explained 3 part Blog Post.

Breaking is not hard to do

Site Permissions Breaking Bad Episode 1

Site Permissions Breaking Bad Episode 2

But remember with SharePoint it always depends! </out>

MVC using Angular and Promises in SharePoint 2013

Lately I’ve been reading all kinds of cool articles about Angular JS. One of them written by Jeremy Thake: http://www.jeremythake.com/2013/10/sharepoint-hosted-app-with-angularjs-for-mvc-javascript/. This was an excellent starting point for me to see what Angular has to offer. I noticed two things:

  1. Scoping in Angular JS is pretty tricky. Jeremy’s example was written in the global namespace and I wanted to add some best practices.
  2. Combine the scoping difficulty with multiple Asynchronous calls from your JavaScript code and it’s easy to get lost. Promises to the Rescue!

Namespaces in JavaScript

A very common issue in coding JavaScript is conflicts between 2 variables with the same name in the global namespace. If you code in multiple JS files it’s possible that var i for instance is used more then once. This can easily be avoided by using Namespaces like this:

var AP = window.AP || {};

This statement loads AP from the global namespace if it already exists and otherwise creates a new namespace AP. The functions or vars created in that namespace will never conflict with same function- or varnames in a different namespace. #winning!

AP.WebPropQuery = function() { var i = 0; };
AP.ContactsQuery = function() { var i = 1; };

This makes the code maintainable and readable while the var i now is always the right value in the right context. If you want to know more this is an excellent read:

http://www.kenneth-truyers.net/2013/04/27/javascript-namespaces-and-modules/

Angular JS

My goal here was to use the namespacing practice with the Angular is written. Angular also has a way of scoping everything and I found challenging to combine it with this approach of Namespaces.

With a directive in the HTML code “ng-app” we specify the scope of this Angular App. Using this code I register it as a Module so I also can use a Service later on.

AP.Promise = angular.module('AP.Promise', []);

My goal in this example is to load a Property from the Web’s Propertybag. This property contains the name of a Contacts list. I want all the contacts to be rendered with Full Name and Email address.

Controllers

A controller in the MVC pattern handles a certain part of the View. In our case a list of Contacts. I used the directive ng-controller=”ContactController” and matching JS code

/*
* Angular Controller Registration
* Dependencies: $scope and the $ProfileService
*/
AP.Promise.controller('ContactController', ['$scope', '$ContactService', function ($scope, $ContactService) {
   $scope.contacts = [];
   $ContactService.getContacts($scope);
}]);

When adding this Controller to the Module, I pass along 2 dependencies. $scope (which is kind of mandatory otherwise the controller would be useless) and $ContactService. In this service the logic is stored for getting the data out of SharePoint. The contents of the service

/*
* Angular Service Registration to Support SharePoint interaction
*/
AP.Promise.service('$ContactService', function () {
//Service Method to Get the Contacts
  this.getContacts = function ($scope) {
     //Logic
  };
});

This is the base of our Angular code. Now we need some JS to get the data out of SharePoint. In this example I’m using the REST API.

REST calls and Promises

One library that makes our lives easier when using REST API is jQuery. You might have noticed that the $ char is very popular and is used by lots of JavaScript libraries. We have to be carefull not to call into the wrong library here.

At the start of this post I created two functions in the AP namespace which we are going to fill. One for the Call to get the Web Property and one for the Call to get the Contacts.

/*
* Query object to retrieve Property from Propertybag
* Return: Promise object that is filled with data after ASync call is returned
*/
AP.WebPropQuery = function ($) {
   var deferred = $.Deferred();
   var execute = function () {
      $.ajax({
         url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/AllProperties?$select=p42contacts",
         method: "GET",
         headers: { "accept": "application/json;odata=verbose" },
         success: function (data) {
            deferred.resolve(data);
         },
         error: function (err) {
            deferred.reject(err);
         }
      });
      return deferred;
   };
   return {
      execute: execute
   }
}(jQuery);

This is what the first function looks like. Now the important part is the Promise var deferred = $.Deferred(); This var is called a promise and is kind of like an object placeholder which you can work with untill the Async call replaces the object with the final result of the call. Using these promises you don’t have to nest the second call in the success function of the $.ajax call. This increase readability and maintainability of your code. Scott Hillier wrote a great piece on Promises and how to use them:
https://curah.microsoft.com/11711/using-promises-patterns-in-sharepoint-2013-apps-with-javascript-and-jquery

Making the function calls

The final missing piece in this is making the function calls from the logic in the Angular Service. Again the code of the service with the missing logic

/*
* Angular Service Registration to Support SharePoint interaction
*/
AP.Promise.service('$ContactService', function () {
   //Service Method to Get the Contacts
   this.getContacts = function ($scope) {
      //First Async Call
      AP.WebPropQuery.execute().promise().then(function (data) {
         //Second Async Call depending on data from 1st call
         AP.ContactsQuery.execute(data.d.p42contacts).promise().then(function (data) {
            //Data to work with
            var results = data.d.results;
            for (var i = 0; i < results.length; i++) {
               $scope.contacts.push({ fullname: results[i].FullName, email: results[i].Email });
            }
            //Apply scope to see results
            $scope.$apply();
         }, function (err) { });
      }, function (err) { });
   };
});

AP.WebPropQuery.execute() is the regular function call. With .promise().then() it now is possible to add a second function call based on the promise that is returned in the first. That second call also returns a promise and with that data we can populate our Model.

The View

The View to match all this JS code is a very plain and simpel piece of HTML.

<script type="text/javascript" src="/Style Library/Scripts/p42.contacts.js"></script>
<div ng-app="AP.Promise">
   <div ng-controller="ContactController">
      <ul>
         <li ng-repeat="contact in contacts">{{contact.fullname}} - {{contact.email}}</li>
      </ul>
   </div>
</div>

Results

The thing I really like about this combination of Promises and Angular is that your JavaScript has very clear build up. It’s very readable and maintainable and on top of that it’s very fast! This way we don’t have to wait on all the SharePoint JS files to be loaded as well since we use the REST API. Very powerfull stuff!

You can download a Visual Studio 2012 Solution here. This solution contains a Sandboxed Solution (still fine for provisioning stuff!) with a default Script Editor WebPart that holds the View HTML with the Angular directives.

Todo yourself:

– Create a Contact List and make sure the Full Name and Email are filled
– Create a Property using SP Designer called “p42contacts” in the rootweb and add the name of the Contact List as value.

Upgrading SharePoint 2010 JS

Since a lot of Customers are upgrading their Office 365 Tenant to SharePoint 2013, I noticed one specific change in how SharePoint loads it’s JavaScript that can break down some of your code. Fortunately it’s very easy to fix and the best part is that this solution was already available in SharePoint 2010. So upgrading your scripts is highly recommended even if you’re still running SharePoint 2010.

SP.js

In SharePoint 2010 the SP.SOD (Script On Demand) was introduced. This library came with a very handy function to Execute some of your custom script after a specific registered script was loaded by the SOD.

To make use of the JavaScript Object Model we have to make sure that SharePoint has loaded sp.js first. Therefor a common line of script is used by lots of developers:

SP.SOD.executeOrDelayUntilScriptLoaded(MyFunction, ‘sp.js’);

When sp.js is loaded, the SOD is notified and MyFunction is executed. In SharePoint 2010 this works in 99% of the times it is called because SharePoint depends on the sp.js file in most of its UI, so you assume the file is always loaded. I say most of the times because sometimes it can be a choice not to load it and just go with the basic JavaScript files (init.js).

SharePoint 2013

In SharePoint 2013 this has changed. Now every SharePoint JavaScript file is loaded only when it’s used, using the same SOD principle. Now most times sp.js is never loaded until it is called by SharePoint. Our call using ExecuteOrDelayUntilScriptLoaded isn’t working anymore since SOD doesn’t gets the notification that sp.js is loaded.

The solution

To overcome this issue there is a simple solution:

if(!SP.SOD.executeOrDelayUntilScriptLoaded(MyFunction, ‘sp.js’)){ 
  LoadSodByKey(‘sp.js’);
};

This will force the loading of sp.js still using the SOD principles. Also this check works in the SharePoint 2010 context, so you can upgrade your scripts now so they will continue to work when upgrading to SharePoint 2013.

Happy Scripting!

SharePoint 2013: Programmatically add Related Items

The Task Content Type in SharePoint 2013 has a cool field named Related Items. Although there is some information about how to add this Field to other custom lists, there is no information on how to populate this field other then using the UI.

http://sampathnarsingam.blogspot.nl/2013/02/sharepoint-2013-related-items-site.html

Lucky for us this is quite simple! I will be using the JavaScript Object Model for this, but I’m sure this will be similar for the other models.

Console App to the Rescue

Since the Field Type of this column got me nowhere I just wanted to know the plain value of a Task that already had Related Items. It appeared to be a string[] containing JSON objects of related items according to this format:

[{“ItemId”:id, “WebId”:”WebGUID”, “ListId”:”ListGUID”}]

RelatedItemsValue

To my surprise I was able to put this literal string into the field using JavaScript Object Model. Nice! But the key is to first Create the Task and then Update it with the Related Items.

JavaScript Object Model

To test this I created a new Task using the value I got back from my Console App. Here’s the code:

var ctx = SP.ClientContext.get_current();
var taskList = web.get_lists().getByTitle('Tasks');
var taskInfo = new SP.ListItemCreationInformation();
var task = taskList.addItem(taskInfo);
task.set_item('Title', 'Appicon.png');
task.set_item('Body', 'Do some work for this item');
task.update();

ctx.load(task);
ctx.executeQueryAsync(function(){
task.set_item('RelatedItems', '[{"ItemId":1, "WebId":"c88c2b90-c2b1-4de4-9a05-ee109412152d", "ListId":"7be848a2-c703-47ef-8403-7d8fbf9e951c"}]');
task.update();
ctx.load(task);
ctx.executeQueryAsync(function(){
//Success
}, function(sender, args){
//2nd Fail
});
}, function(sender, args){
//Fail
});

Result

Opening the Task I created we can see the Related Items pointing to AppIcon.png which is just a picture in a Picture Library. Pretty cool huh!

RelatedItemsResult

Custom Ribbon Action Icons in a SharePoint Hosted App

Today I learned something about Data URI’s and that there absolutely awesome! Especially for this particular practice. I was creating a Custom Action in my SharePoint Hosted app. I wanted to add a Button into the Ribbon of any Document Library that takes me to my App Web and do some work based on List Information. I started by adding a Custom Ribbon Action.

blog-ribbonaction2

blog-ribbonaction3

This is all pretty basic and out of the box stuff. The XML generated contains also the URL to my App Web using the token ~appWebUrl(~remoteAppUrl for auto-hosted / provider-hosted). Unfortunately this Token could not be used to link to an icon that should be displayed in the Ribbon with my Custom Action. Also by default the images are linked to the _LAYOUTS folder which we cannot write to from an App.

blog-ribbonaction41

Data URI to the Rescue

On the web I came across this post on Stack Overflow http://sharepoint.stackexchange.com/questions/57328/is-it-possible-to-package-deploy-and-use-my-own-image-for-a-ribbon-customactio which offers the perfect solution: Create a data URI for the icons. You can read about Data URI’s and even create your own out of an image right here: http://dataurl.net 

In my example I used the same image for 32 and 16 px.

blog-ribbonaction4

As we can see (this is just a snippet) the URL’s can become quite long, which is pretty ok for this particular use but can be a disadvantage if your using this everywhere in your website. There are some other drawbacks on this approach but still for this use I think this is very good and solid solution!

blog-ribbonaction5

Cheers!