Monday, 17 September 2012

SharePoint apps: working with the app web, and why you should

Disclaimer: Just because I’m writing about apps, doesn’t mean I think everything in SP2013 should be developed as an app! I’m focusing on apps simply because it’s a deep area with lots to learn.

  1. SharePoint 2013 apps – architecture, capability and UX considerations
  2. Getting started – creating lists, content types, fields etc. within a SharePoint app (provisioning)
  3. Working with data in the app web, and why you should [this article]
  4. Access end-user data (in the host web) from a SharePoint 2013 app
  5. Rolling out SharePoint 2013 apps to the enterprise - tenant scope and PowerShell installs
  6. Azure is the new SharePoint ‘_layouts’ directory
  7. “Host web apps” – provisioning files (e.g. master pages) to the host web
  8. ““Host web apps” – provisioning fields and content types
  9. Deploying SP2013 provider-hosted apps/Remote Event Receivers to Azure Websites (for Office 365 apps)
  10. Working with web parts within a SharePoint app

The app web is the default

You might already have come across the idea that SharePoint 2013 apps which host SharePoint components (e.g. lists, content types etc.) put these into an isolated area called the app web. If not, my previous article Creating lists, content types and fields within a SharePoint app (provisioning) provides some details, including some explanation around the diagram below, which summarises an app I’m building:

TimeTrackingApp

The main thing to consider about the app web is that it is the default – if you write some Client Object Model (CSOM) code (remember that SharePoint server-side code is forbidden within an app), perhaps to find a list and retrieve the items, in the standard app arrangement it is the app web you are working with. This is because, by default, you’ll be in a page within the app web – meaning your context is the app web, and that’s what is reflected in the ClientContext object you’ll be working with in your code. What happens is that when you go through an app’s “front door” in the Site Contents area, you are redirected to a page in the app web – and the ClientContext object in the CSOM is simply doing what it always does i.e. obtaining the current web as per the URL in the browser address bar.

Consequently, there is nothing special needed to ‘get a reference’ to the app web in your code. As a separate topic, the same cannot be said of the “host web” (the “real” SharePoint site where the app was installed) – in a best practice configuration which has the app domain on a completely separate URL domain to team sites/My Sites etc. (e.g. “http://myCorp-apps.com” vs. “http://myCorp.com” ), you’ll need to use the SP.RequestExecutor cross-domain library which Microsoft supply to access the host web. More on this in the "Working with data in the host web” article later in this series.

Getting the URLs for the app web and host web (within an app)

Within an app, the developer needs to know the URL for the app web and the host web where the app is installed.  The relevant URLs for the app web and the host web are passed as querystring parameters in the URL through to the first page load of your app, when the user arrives there from the app entry point in the Site Contents area. In fact, any time the user hits the home page of your app (e.g. by clicking the breadcrumb link within the app itself), you’ll see them in the browser address bar. So, it’s simply a matter of reading these URL  parameters – there are lots of ways to do this in JavaScript, I use the following jQuery plugin but anything similar will be fine; simply ensure it’s in a JavaScript file referenced by your app pages:

// jQuery plugin for fetching querystring parameters..
jQuery.extend({
    getUrlVars: function () {
        var vars = [], hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for (var i = 0; i < hashes.length; i++) {
            hash = hashes[i].split('=');
            vars.push(hash[0]);
            vars[hash[0]] = hash[1];
        }
        return vars;
    },
    getUrlVar: function (name) {
        return jQuery.getUrlVars()[name];
    }
});

Then to grab the app web/host web URLs, you would do:

// retrieve passed app web/host web URLs..
hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl"));
appweburl = decodeURIComponent($.getUrlVar("SPAppWebUrl"));

BEWARE: bug in SharePoint 2013 Preview around the host web URL

One thing I notice in the current build (Preview), is that the host web URL only comes through on the first-time page load (app entry) from the host web. When the user is navigating around any pages you create in the app web, if they click on the breadcrumb link to go to the app’s default page, the URL parameter is present but has an incorrect value. It now has the URL  of the app web, instead of the host web. Consequently if you’re trying to look up something in the host web, this code will fail at this point. I’m assuming this has to be a bug, and will file it on Connect very soon (it hadn’t already been logged when I checked).

As a temporary workaround, I’m persisting the value in a cookie on first-time entry, then fetching it from there as needed. This works fine, I’d be happy to share my code if useful to anyone (just leave me a comment below), but it’s not particularly complex and I’m sure you can imagine what it does.

Common tasks with the app web

So if it’s just standard Client Object Model code and nothing special is required to ‘get a reference’ to the app web, then standard JavaScript CSOM code samples are all you need to help you along. As such, a previous article of mine has some basic JavaScript CSOM examples that I often find myself referring to when I can’t remember syntax and whatnot:

Other good sources are:

Remember that the JavaScript version of the Client Object Model (which I’m discussing here) is what you’d use in a web page, but if you were in server-side code of some sort (Windows desktop app/Windows Phone app, WCF service, PowerShell script, console app etc.) then you could use the .NET Managed Client Object Model and code in C# for example.

Using the app web vs. the host web

So, working with the app web is pretty simple. In security terms, the app principal (the identity of the app – this is a fully-fledged security identity) has Full Control permission of the app web, so you can accomplish anything you need to, so long as it can be done with client-side code. However, as I pointed out last time, the app web is pretty sparse by default – there’s no real navigation, many of the SharePoint system pages cannot be used (e.g. the Site Contents page, /_layouts/viewlsts.aspx) and obviously there’s a separation between the app and the end-user’s collaboration stuff (lists and libraries) which is back in the host web. Obviously you could customise heavily and maybe even get to something like that feels like a team site, but it would be quite a lot of work – it’s not permitted to actually use a template such as a team site (STS#0) for an app web. Here’s a reminder of what that default user experience looks like:

AppDefaultPage 

If this isolation of the app doesn’t really suit the requirements, an alternative arrangement could be for the app to provision it’s lists/pages/etc. into the host web. To be clear, this is actually isn’t possible in many scenarios for two main reasons:

  • Security – the app cannot mess around with the host web; by default it has very limited read permissions only. This, of course, is the whole point of the isolation and a key benefit of apps for whoever has to plan, maintain and upgrade the SharePoint deployment
  • Provisioning model – if you add a Feature to your Visual Studio project to create, for example, a list – then this list gets created in the app web. It is simply not possible to use traditional provisioning approaches against the host web from within an app

However, there is a combination of circumstances where it is possible:

  • The app comes with a permission request for Full Control to the host web (which must be granted by the user installing the app for the app to install)
  • Provisioning is implemented via the Client Object Model (CSOM)

These constraints mean it might not always be possible to use this approach. For example, Microsoft is unlikely to ever approve an app which requires Full Control of the host web for use in Office 365/SharePoint Online. Similarly, SharePoint administrators deciding whether a publicly-available app is safe to be added their organisation’s App Catalog would most likely take the same view. BUT, it might make sense to fulfil a particular client requirement – if a client wants a certain set of functionality and wants it implemented as an app, then this might be the way.

Doug Ware, a fellow SharePoint MVP and true “developer’s developer”, is doing a lot of thought leadership in this area. The links at the bottom of this article are to his posts, which I think are required reading if you believe this approach is relevant to you. To build an app of this type, it quickly becomes obvious that a library of CSOM provisioning methods (e.g. createField(), createContentType(), createList(), addListItem(), addFile() and so on) is necessary, and Doug is starting a blog series on this topic.

However, I don’t believe this approach is a panacea for challenges around the app model. Here’s why…

…implementing an app in the host web might not be a good idea

So we’ve already covered the constraints which could mean it’s not possible in the first place (the need for Full Control permissions in the host web). I also think that:

  • The host web approach partly defeats the purpose of apps (a very specific piece of functionality, which runs isolated from the SharePoint environment so there’s no negative impact to platform stability, any future upgrades and user data) 
    • If the app is just for one client, I feel like the benefits of apps are eroded so much you may as well build a farm solution. It will be way quicker, no matter how good you are with JavaScript and the CSOM.
  • For many requirements, putting the artifacts in the app web should actually work just fine. If you need to store some list/library data and provide some pages for a front-end, then the fact that this is somewhat separate from the end user’s core data doesn’t really matter.
    • For example, in the somewhat contrived time-tracking app that I’m building, so long as the user interface makes it quick and easy to see how many hours the current user has logged this week, and what his/her target is - I don’t think he/she would care whether the data was coming from a list in the app or host web.
  • I’m convinced Microsoft don’t want you to build apps in the host web. I’m mainly basing this on the following observations:
    • The provisioning model is all around the app web
    • It’s not possible to set the app’s start page to be a page in the host web. Notably, all the URL tokens which can be used at the beginning of the start page URL are for the app web (or remote app web in the case of an externally-hosted app). See the MSDN page URL strings and tokens in apps for SharePoint and note the “This token cannot be used in the StartPage element of an app manifest…” statements next to each token which refers to a host web item
    • No SDK samples use this approach
    • No MSDN documentation discusses it (at the time of writing at least)
    • Many of their design goals of the app web would not be met (security, reduction in upgrade complexity for customized environments, etc.)

Effectively, I almost see the idea of an app requesting Full Control permission and then provisioning there as a LOOPHOLE.

Summary

So does that mean that we should never use this approach (and that Doug is wrong)? Do I think that we have to build solutions exactly as Microsoft envisage? Hell no! For starters, Microsoft say that every customization should be built as an app in SharePoint 2013, and as my disclaimer at the beginning of these articles says, I personally don’t agree with that. 

But I do think you should think long and hard before using the “exploitable loophole” of building in the host web. Too many devs fail to see the full Total Cost of Ownership of their solutions - around maintainability (and skills/experience/capability required), long-term stability, performance, re-work required during upgrade and so on - because they’ve long since moved onto the next project/client. I’ve seen extremely impressive solutions (created by very talented devs) slide from being absolutely loved to being a big organizational headache within months. Of course, the original dev wasn’t around to see this – if he was, he probably could have managed perceptions, minimized the problem and perhaps learnt a lesson - but he wasn’t. It’s hard to accept, but some of my solutions have probably gone in this direction too. Now I’m not suggesting Doug is guilty of this – I’m sure someone of that brainpower is able to look after his SharePoint clients’ best interests very well – but there is a  long-term impact around SharePoint dev work which implementers should consider. And I feel that if the app model is suitable in the first place, then implementing an app in the host web (rather than the app web) may be part of the problem, rather than part of the solution.

All this isn’t to say it never makes sense though – as I said earlier, some situations where the SharePoint moving pieces really have to sit next to the other lists/libraries/pages the end user has would be examples. I think Doug is actually saying as much when he talks about Building Traditional SharePoint Collaboration Solutions with the App Model – it’s just that I might be more optimistic that the isolated model works for more scenarios than he is. Time will tell.

Further reading: