Archive for Apps

Custom MasterPages and SharePoint Hosted Apps

We all know that Microsoft is moving away from the concept of Custom Masterpages (at least on Office 365). Chris O’Brien explains some considerations in this great blogpost http://www.sharepointnutsandbolts.com/2015/01/custom-master-pages-and-web-templates-in-office-365–thoughts.html. But say you already have this On Premises SharePoint 2013 Intranet with a nice custom MasterPage in place on a Publishing Site. When you have subsites and especially Team Sites, there is this scenario, when setting the MasterPage, you want to force the MasterPage on all subsites.

Inheritance of the MasterPage

When you stick to Publishing Sites, a new subsite will always inherit the MasterPage from its RootWeb by default. However when mixing publishing sites with a Team Site for instance, things get tricky. Because the publishing Web Feature is not activated on a Team Site, no MasterPage is inherited.

image

image

I also deployed a SharePoint Hosted App to the Root Web. Just an Hello World App to illustrate this issue.

image

If you activate the Publishing Web feature on the Team Site, inheritance is immediately restored. Unfortunately there is another, more harmful, way to force the MasterPage. If you go to Site-settings > MasterPage on the Root Web you can check the box “Reset all subsites to inherit this site master page setting”.

image

By doing this you can force the MasterPage upon all the subsites in this sitecollection. Because our subsite is a Team Site we need to set the System MasterPage as well. When I select my custom MasterPage at first sight nothing happens and everything is OK. (I only changed the SharePoint brand in the top left Suitebar into a custom text).

image

Oops! My Apps broke down

After I reset the MasterPage to all subsites when I open up my SharePoint Hosted App, suddenly this error appears (CustomErrors are off).

image

This error instantly tells us what’s wrong. By resetting the MasterPage to ALL subsites, the App Webs are updated as well, therefore introducing a reference issue because the MasterPage is now linked to the Host Web’s MasterPage instead of app.master on it’s own App Web.

Powershell to the rescue

The easiest solution is to remove the App and add it again to the site but in most cases this don’t want to do this. Especially when there is data in the App Web. Using powershell to repair the MasterUrl is the obvious solution here. The code I provide here is based on an On Premises environment. When using Office 365 you will have to write some additional code to make the connection.

Famous last words

This error occurs only in this very specific scenario and if the reset is applied for the System Master Page. When you reset all sites to use the Custom Master Page all apps will keep working. In my experience this specific scenario occurs when mixing up Publishing Sites with Team Sites. Because Team Sites don’t inherit and you can’t set a Team Sites MasterPage from the Site Settings.

4 Steps to smarter App Part resizing

Wow it feels like forever since I wrote something and finally I got some good new content. I started to research a little bit more on the resizing of App Parts in SharePoint Hosted Apps and came up with some really small changes in basic CSS and JS that make App Part resizing a lot more stable and X-browser compatible. #winning

 

1. No horizontal scrolling

In this example I’ll use an App that Lists Apps on the Host Web. This particular App displays a list of Title just below one another. Nothing fancy. However when we want to display the page in an App Part, this happens.

Capture1

Visually it seems that we have more then enough space between the Titles and the end of the IFrame (300px width by default). Still we see a nasty scrollbar! What happened? Inspecting some elements up in the DOM explains a lot. Very funny SharePoint.

Capture1-2

The defaultcss.ashx causes trouble! This control is loaded on the page by default in an ASPX page like Default.aspx or most likely by a dev in a plain HTML page to keep default SharePoint branding alive in the App. That is a good principle, but this width style is not really helping us out.

Full Width Power

In order to get rid of the scrollbars in this scenario there’s a simple CSS fix we can apply in our App.css.

#contentBox { min-width:100%; margin:0; }
#contentRow { padding:0; }

This way we make sure no weird margins or paddings will cause our App Part more pain and it’s also going to help us when resizing the WebParts height later on.

Capture2

Better! Now the content, that we know for sure should fit, actually fits. Width wise. Still the height of the App Part is not right.

 

2. Resize your App Part

Resizing App Parts using the postMessage technique is nothing new to most people. We need a function in our App that sends a message to the parent of the IFrame. SharePoint can pickup this message and then resizes the IFrame for you using a given width and height. I used the following code

resizeIFrame = function (height, width) {
 if (window.parent == null) return;
 var senderId = '';
 var params = document.URL.split("?")[1].split("&");
 for (var i = 0; i < params.length; i = i + 1) {
  var param = params[i].split("=");
  if (param[0].toLowerCase() == "senderid")
   senderId = decodeURIComponent(param[1]);
 }

 //Bug for self reference in href='#'.
 senderId = senderId.replace('#', '');
 var message = "<Message senderId=" + senderId + ">"
 + "resize(" + width + "," + height + ")</Message>";
 window.parent.postMessage(message, "*");
}

In this common piece of code I added a check for “#”. When this is used in the App (I came cross it once), the QueryString is extended with this “#”. This causes the SenderId, which is always last in the QueryString, to be compromised and the Resize function from SharePoint to fail!

Function call

To call this function we first determine the height of the DOM element that holds all the content. The width shall be “100%” to have an App Part that will always fill the entire space.

var height = jQuery('#OverView').outerHeight(true);
var width = "100%";
resizeIFrame(height, width);

This function resizes the entire iframe to the dimensions given.

Capture3

But wait! there is still a vertical scrollbar! Something must be wrong. In the Elements File of my ClientWebPart I removed the Height and Width properties of the App Part.

Capture3-1

Here is where a very weird phenomenon shows itself. Since we don’t have any requirements set regarding width or height, SharePoint sets a default width and height to the AppPart which is the same as the ClientWebPart xml states when it is just created.

Capture3-2

Here our default width is 300px. The funny bit is that because we use the ResizeIFrame method, the width is only set to 100% after the Resizing is done. To make it even more complex, the Height is meisuring the content that now is rendered within 300px width. Therefor the height also cannot be trusted. This makes the resizing of the App very buggy. Now we see it still as 300px width. Refreshing a couple of times shows 100% width but still with a vertical scrollbar and height that is just a couple of px off. Some DOM element must be holding back some padding info.

 

3. Body Container

Again the main obstacle here is some styling that comes from defaultcss.ashx. By default the CSS sets a padding-bottom:35px; on #s4-bodyContainer. This is why we add to our App.css this piece as well.

#s4-bodyContainer { padding:0; }

Capture4

Instantly this is starting to look good! The App Part now scales over the entire width of the Page using a correct height and most important: NO scrollbars! Since I placed my App Part in the text part of this SitePage it is displayed in full width. When I use a Publishing page with multicolon WebPart Zones, it will also adept to the width of the colons. This comes in very handy for instance with use of Bootstrap.

 

4. One last thing

One last nag, which is actually really tough. Thereby I don’t mean the fix, that ones easy. I’m talking about consideration on how to deploy the fix. The thing is, as I just explained, the height is calculated at the wrong time in rendering. In order to get the height right, the IFrame must have it’s width set to 100% first. We’ve seen SharePoint set it to a default value of 300px. When a dynamic list of <span>’s wrap over 3 lines for instance, the height at a 100% width is very different from the height at 300px width.

A fix here can be set all IFrames to a default width of 100%. I don’t see why you shouldn’t do that in the first place. But still it depends right.

Custom CSS

The best option in my opinion is to use the capability to upload and add your custom CSS when using Publishing Sites. You can do this in the MasterPage section of the Site Settings. Just create a blank CSS file and add

iframe{ width:100%; }

When you aren’t using publishing sites, there is another possibility.

UserCustomAction

You can add some code to your App to deploy a UserCustomAction to the HostWeb. In the UCA you can add a ScriptBlock that writes this CSS style to every page. This is a dangerous option when not done right. Waldek wrote about these things and explained why it can break your site. The following code will do so, but keep in mind that everytime this executes another UCA is created. You still have to create a “Run Once” type of mechanism.

P42.ScalingAppPart.setFrameWidth = function ($) {
 var execute = function () {
  var deferred = $.Deferred();
  var hostUrl = decodeURIComponent(P42.Utils.getQueryStringProperty("SPHostUrl"));
  var appWebUrl = decodeURIComponent(P42.Utils.getQueryStringProperty("SPAppWebUrl"))

  var context = new SP.ClientContext(appWebUrl);
  var factory = new SP.ProxyWebRequestExecutorFactory(appWebUrl);
  context.set_webRequestExecutorFactory(factory);
  var appContextSite = new SP.AppContextSite(context, hostUrl);
  var site = appContextSite.get_site().get_rootWeb();
  var userCA = site.get_userCustomActions();

  var customAction = userCA.add();
  customAction.set_location("ScriptLink");
  customAction.set_scriptBlock("document.write('<style>iframe{width:100%}</style>');");
  customAction.set_sequence(1000);
  customAction.update();

  context.executeQueryAsync(function () {
   deferred.resolve();
  }, function (sender, args) {
   deferred.reject(sender, args);
  })

  return deferred.promise();
 };

 return {
  execute: execute
 }
}(jQuery);

Famous Last Words

These steps will get you some smoother App Part resizing experience that will work cross-browser. By removing all the extra margins and paddings you are now in control over the exact width and height of your App Part. I never got it right untill I put some work in it, hopefully this post will help you too getting it right.

– Cas

SharePoint Hosted Apps: Targets and Debugging

UPDATE: After I got a nice email from the Office Tools Dev Team I installed Update 2 for Visual Studio 2013. This Update provides a choice of Deployment Target in the Project Properties window (not the pane). On the SharePoint Tab you’re now able to switch between SharePoint 2013 and SharePoint Online. This changes the Target Version and solves the error.

Capture4

Original Post

Today I came across a strange issue when deploying and testing SharePoint Hosted Apps from Visual Studio. When you create a SharePoint Hosted App in VS, you need to specify a debug target for the App in this dialog:

screen1

In this Example I choose to deploy it to my Office 365 Developer site. Deploying and testing this App on my Developer site works perfectly but things go sideways when I change the Deployment Target to my On-Prem server. When I hit Deploy I instantly get the error:

Unknown SharePoint version: 16.0, Parameter name: version

Weird!

Deployment Targets and versioning

The cause of this nasty error is the dialog above where we specify our debug site. For some reason Visual Studio thinks we are never gonna change it after this point. Therefore the TargetOfficeVersion property in the project file is set to the first target of choice. In my case this was my Office 365 Developer Site which is already running on version 16.0. I can easily check this by opening my .csproj file.

<TargetOfficeVersion>16.0</TargetOfficeVersion>

When I create a new App and set my debug site to my On-Prem environment the number in the TargetOfficeVersion property is 15.0. Unfortunately this is something I wasn’t able to find through the Visual Studio UI in project settings or properties.

Conclusion

If you start developing with a Cloud First approach (which I think is the way to go) you might get into trouble testing On-Prem depending on the versioning and updates on Office365. Therefore keep in mind the rapid update cycle on Office 365 and the difference with your On-Prem environment.

My Key Takeaways from #SPC14

WOW is the first word that comes to mind when I’m thinking of the week I have had in Las Vegas during the SharePoint Conference 2014. IT. WAS. AWESOME. It’s also cool to see lots people felt the same way. The hashtag #spc14 was on fire! But still for the people that couldn’t go, I want to point out some new Key Concepts that are going to rock our Dev World!

Office Graph

Something we could have been expecting since MS bought Yammer is a Graph protocol that works with SharePoint, Office and Yammer objects. Yammer was already based on Open Graph (like Facebook is) and therefor it’s no surprise Microsoft has been working on this to cover the entire Office space. In the Keynote the Office Graph was anounced with a demo on Project Oslo. A new, personalized and highly interactive App based on this Office Graph principle. It really looks awesome and the Graph will also be part of a new API in Office! Learn more about this right here: http://blogs.office.com/2014/03/03/work-like-a-network-enterprise-social-and-the-future-of-work/

Unified API’s

With the addition of Office Graph and lots of other new Features, the API’s for Office (I’m including SharePoint as a part of Office) are starting to change. A thin red line that was to discover in the Development tracks this SPC is that Microsoft is unifying the API’s that Office 365 (including SharePoint) have to offer. By that I mean that Microsoft is clearly pushing 1 technique and that is by using REST and OData. Microsoft is heading for a model where every API is a RESTful service, wheter it is an Office API or a SharePoint API. Now this is a big deal! The vision here is that those API’s will be Entity Driven. When asking the API for files they will most likely come from SharePoint or OneDrive. When asking for contacts, they will come from Office. The Office / SharePoint team is now even talking to the Dynamics CRM guys and the goal is to achieve the same for Dynamics CRM Online. That would be sweet!

Examples of the new Office API’s and a list of new Features can be found here: http://officeams.codeplex.com/

The App Model

It may be clear to most of you that considering the API “Unification”, the App Model is still the way to go and is heading for adulthood! Good to hear the Product Team is hoping to get AutoHosted Apps out of Preview by the end of this year. This will be a nice addition to the App Store across the Globe. There already is a roadmap for the localization of the store for the next wave which will be somewhere Q2 of this year. The Netherlands is one of those first countries the App store will become available to.

In combination with the new Office API’s it is way easier to build Office Apps now. Microsoft rebranded these to “Contextual Apps” by the way. On top of this all, Microsoft has released an Open Source Android SDK to build Apps for Android devices. Didn’t see that one coming! You can get it here from GitHub: https://github.com/OfficeDev/Office-365-SDK-for-Android

Another thing that really surprised me was the presence of Google in the App Development tracks. One of my favorite frameworks Angular JS was used quite a lot in App Demo’s. Microsoft is not afraid anymore of Frameworks not build by Microsoft and the MVP’s are really promoting these new JavaScript Frameworks (Angular, KnockOut, Breeze).

Microsoft is opening up

Finally something that I think is the most important thing during these conferences. You can get a good idea of where Microsoft as a company is heading. My overal feeling with Microsoft this conference was that Microsoft is really opening up to it’s partners. Never was I so close to the people that actually built SharePoint and Office 365. As one of the Nordic Partners we had the opportunity to sit down with the productteam and ask questions about the present and future of SharePoint and Office 365.

Besides organizing these kinds of roundtables, it was clear that Microsoft is really happy with the Feedback they receive from everyone in the SharePoint community. There is a User Voice site to submit feedback from a developers perspective. And also Stack Exchange is an official channel used by Microsoft.

http://officespdev.uservoice.com/
http://sharepoint.stackexchange.com/

It was also good to hear that Microsoft is working on plans to re-introduce the Technology Adoption Program (TAP) for Cloud models. This would be a very welcome feature so Partners and Developers can test the new API’s, Features etc that will be released in Office 365 later on. Unfortunately Microsoft is still working on this idea and so my guess is that we will have to wait a little longer before this will come around. Lot’s of uncertainties here, but the important point here is that Microsoft is sharing more and more to it’s partners and developers. Go Microsoft!

Famous last words

When I compare this conference to the one in 2012 I can clearly see Microsoft has started listening to our feedback and they’ve done a great job on this conference. The sessions and speakers where top notch in my experience and everything was very well organized. Obviously I just covered a tiny part of all the stuff that was talked about in the breakout sessions. It’s not clear when all the content will be released, but for now there are lots of Post Conference Blogs you can read to get the information you need.

See you next time at SharePoint Conference!

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'&amp;$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>