Tuesday, March 12, 2013

Arctic SharePoint Challenge 2013

This year was absolutely fantastic. I was a judge again this year and I can only say its has been a very pleasant experience. The ASPC have really grown since last year and the Hotel was just fabulous.

A lot of teams did some very cool stuff, many based on the App Model for SharePoint 2013. But it was not just all SharePoint, a lot other platforms was used, Azure, Mobils, Windows 8, Node.js and they integrated very nicely with SharePoint 2013.

Here is some pictures.

Tuesday, March 05, 2013

SharePoint 2013 Hosted App Validation Checklist

Before submitting an app to the Office SharePoint App Store, you must ensure you have done what you can do, to get it right the first time.

For starters use this check list “Validation checklist for apps for Office and SharePoint”

Then here are some from my own experience.

Validation checklist for submission to the Office Store

IC643463

The app title submitted via the Seller Dashboard, must be similar to that included in your app manifest.
Basically this means that you have to use the same title everywhere.

ManifestAppTitle SellerAppTitle

IC643463 Be sure to implement language. Replace the Language tag in AppManifest.xml.
Because of a bug in developer tool in Visual Studio, you are not allowed to use the correct Language tag, because it fails with a validation error when you build your app. Therefore you have to change the AppManifest.xml after you have package (Published) your app. Use a zip tool to to open up the app and change the AppManifest.xml directly.

Replace :

<SupportedLanguages>en-US</SupportedLanguages>

With:

<SupportedLocales>
  <
SupportedLocale CultureName="en-US" />
</
SupportedLocales>

Update! The SupportedLanguage problem has been fixed in the lastest version of Office Developer Tools for Visual Studio 2012 RTM
IC643463 Cleanup your JavaScript with JSHint or similar tools. Old browser can fail where new browsers don’t. The Internet Explorer 8 is especially bad here.
Optional tool: JSFiddle
IC643463 Your app must be fully functional with the supported operating systems and browsers for Office 2013 and SharePoint 2013.
This means that the app has to function in Internet Explorer 8,9,10.
The F12 Developers Tools, to change the Browser mode will only help see the difference in styling but not in JavaScript.
You need to run it in Internet Explorer 8. I would recommend downloading a VHD image  to test in “Real” environments. Internet Explorer Application Compatibility VPC Image
IC643463 Test the app Online. This is important because Microsoft is apprantly testing the app there themselves.
IC643463 Remember to check you app extensively. This involves the root domain, a sub web site and a new site collection under /sites/. Its important to check is you got all relative urls right.
IC643463 In a licensed app, implemented code for checking this. For SharePoint hosted apps see SharePoint 2013 hosted App License check on how to implement license check.
Also Licensing apps for Office and SharePoint

 

In my experience the approval process are mostly done within a week.

Saturday, March 02, 2013

SharePoint Manager 2013 Online on App Store

Finally I got the SharePoint Manager 2013 Online app on the SharePoint App Store after 5 very long weeks. The app was already ready back in January but due to an internal error in the approval process that resulted in a exceptional long approval time and then it was denied approval a couple of times because it did not meet all the requirements.

But now its out there go and try it out. SharePoint Manager 2013 Online. This version is build purely on JavaScript and only runs client side, but you are still able to view most of the data hidden behind the scenes.

Question: Will the Online version cost anything?

Answer: The Online version is a per person license, but it supports a 15 days trail.

Question: What are the status of the server side version found on Codplex?

Answer: The server side version of SharePoint Manager 2013 is still under GPL and free, there will be no changes here. The development of this version will continue when needed.

 

From the description of the Online version:
The highly recognize community tool SharePoint Manager, has now come in a version that works in Microsoft Online solutions. SharePoint Manager 2013 Online, provides the user a quick and easy overview of the structure and data hidden behind the scenes. Unlike the server version on codeplex, this app only uses html and javascript leveraging the SharePoint REST api. The well-known navigation tree on the left side and a property panel on the right side deliver an extremely user-friendly interface, which allows the user to burrow into SharePoint Online structure and hidden gems.

SM

Thursday, February 28, 2013

SharePoint 2013 hosted App License check

How do you manage/enforce licensing on a fully SharePoint hosted app with JavaScript only?

You need a couple of things:

1. JavaScript that checks for the license.

2. Landing pages, telling you that your license has expired or is invalid.

3. An app that helps you creating test keys for testing your App with licensing.


Is the License code valid for the App store?

Yes, the code is valid, because it is implemented in Site Tree View app that has been approved for the App Store.

1. JavaScript

After searching the web for a solution, I found this http://social.msdn.microsoft.com/Forums/en-US/appsforsharepoint/thread/f022893c-3927-4471-8ae8-7dbcd0b8e5fe

However I needed a more clean solution and rewrote the script. Please note that the code depends on jQuery.Cookie.js, JQuery.xml2json.js plugin and the SP client side libraries.

window.SF = window.SF || {};
window.SF.LicenseConstructor = function($) {

    var licenseCollection;

    var response;

    var licenseSettings;

 

    this.Check = function (s) {

        licenseSettings = s;

        var token = $.cookie(licenseSettings.productId);

        if (token) checkToken(token);

        else {

            licenseCollection = SP.Utilities.Utility.getAppLicenseInformation(licenseSettings.context, licenseSettings.productId);

            licenseSettings.context.executeQueryAsync(onRetrieveLicenseFromSPSuccess, onRetrieveLicenseFromSPFailure);

        }

    };

 

    function onRetrieveLicenseFromSPSuccess() {

        var topLicense;

        var encodedTopLicense;

 

        if (licenseCollection.get_count() > 0) {

            topLicense = licenseCollection.get_item(0).get_rawXMLLicenseToken();

            encodedTopLicense = encodeURIComponent(topLicense);

        } else {

            Redirect(licenseSettings.licenseUrl);

        }

 

        var request = new SP.WebRequestInfo();

        request.set_url("https://verificationservice.officeapps.live.com/ova/verificationagent.svc/rest/verify?token=" + encodedTopLicense);

        request.set_method("GET");

        response = SP.WebProxy.invoke(licenseSettings.context, request);

        licenseSettings.context.executeQueryAsync(onVerificationCallSuccess, onVerificationCallFailure);

    }

 

    function onRetrieveLicenseFromSPFailure(sender, args) {

        Redirect(licenseSettings.licenseUrl);

    }

 

    function onVerificationCallSuccess() {

        var xmltoken = response.get_body();

        $.cookie(licenseSettings.productId, xmltoken, {

            expires: 180

        });

        checkToken(xmltoken);

    }

 

    function checkToken(xmltoken) {

        var token = $.xml2json(xmltoken);

        switch (token.EntitlementType.toLowerCase()) {

            case "free":

                break;

            case "paid":

                break;

            case "trial":

                if (token.IsEntitlementExpired.toLowerCase() === "true") Redirect(licenseSettings.expiredUrl); // Trial app has expired!

                break;

        }

    }

 

    function onVerificationCallFailure() {

        Redirect(licenseSettings.licenseUrl);

    }

 

    function Redirect(url) {

        window.location.href = url;

    }

    return this;

};

 

window.SF.License = new SF.LicenseConstructor(jQuery);

2. Landing pages

Include the SF.License.Check(…) in be beginning of you document onload after the SP libraries has loaded.

var settings = {

    context: SP.ClientContext.get_current(),

    licenseUrl: "http://domain/nolicense.html",

    expiredUrl: "http://domain/trialexpired.html",

    productId: "{[App ID guid]}"

};

SF.License.Check(settings);

 

Finally you need to specify the verificationservice endpoint in the AppManifest.xml for your app.

https://verificationservice.officeapps.live.com/ova/verificationagent.svc

image

3. Test

Now you need to test you licensing code. Do this by installing a test License key, so you are able to test your app. Further reading on the subject can be found on Licensing your apps for SharePoint.

Direct link to the license manager app: 4760.LicenseSPAppSample.zip

Tuesday, February 19, 2013

SharePoint 2013 StandardTokens with IntelliSense

Update: 21 february 2013. Fixed a problem with the json not being valid in Internet Explorer 8. 

Problem:  The standard function getQueryStringParameter used to get the querystring parameters in SharePoint Apps is a bit poor. It does not offer any help on IntelliSense and will spilt the url every time you request a value, making it a bit slow function to use.

I love IntelliSense, especially when working with JavaScript. So I came up with this:

Solution:

window.SF = window.SF || {};

 

window.SF.CreateQueryString = function () {

    // Added defined properties for IntelliSense.

    var q = {

        SPAppWebUrl: '',

        SPClientTag: '',

        SPHostLogoUrl: '',

        SPHostTitle: '',

        SPHostUrl: '',

        SPItemId: '',

        SPItemUrl: '',

        SPLanguage: '',

        SPListId: '',

        SPProductNumber: '',

        SPRecurrenceId: '',

        SPRemoteAppUrl: '',

        SPSite: '',

        SPSiteCollection: '',

        SPSiteUrl: '',

        SPSource: ''

    };

    var 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('=');

        var val = decodeURIComponent(hash[1].replace(/\+/g, ' '));

        q[hash[0]] = val;

        q[hash[0].toLowerCase()] = val;

    }

 

    return q;

};

 

window.SF.Response = {

    QueryString: SF.CreateQueryString()

};

Usage:
Start with adding the javascript file with the code to the _references.js file, in order to get the IntelliSense going.

clip_image001

Now accessing the Standardtokens is like an easy game.

If you want to access other parameters in the url, there are sadly no IntelliSense, but the parameters are still available in a case mode and lower case mode. This is very handy when creating custom properties for an App Part and accessing those properties from the App Part iframed page.

<Content Type="html" Src="~appWebUrl/Pages/SiteTreeViewPage.aspx?{StandardTokens}&amp;showrootweb=_showrootweb_" />

Makes the “showrootweb” parameter available in the QueryString.

var showRootWeb = SF.Response.QueryString.showrootweb;

It does not matter what case the parameter have in the url, because it will always be available in lowercase and its normal form.

Monday, February 18, 2013

SharePoint 2013 App Part scrollbars

I had this problem the other day with no scrollbar showing up when the content of my App Part was larger than the iframe.

It turned out to be the /_layouts/15/1033/styles/themable/corev15.css that was the reason. This file is included on my App Part aspx page, in order to get the same style as the rest of the site.

The simple solution to the problem was just to add a style tag with overflow to the body tag on the App Part aspx page.

<body style="overflow:auto;">

Result:

image

If you wanner resize from your iframed page, its possible by use of the postMessage command.

Found on the forum posted by Yina Arenas

<script type="text/javascript">

    window.onload = function () {

        var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined);

        var regex = new RegExp(/[Ss]ender[Ii]d=([\daAbBcCdDeEfF]+)/);

        results = regex.exec(this.location.search);

        if (null != results && null != results[1]) {

            target.postMessage('<message senderId=' + results[1] + '>resize(500,200)</message>', '*');

        }

    }

</script>

Futher more Richard diZerega has written this very good blog post on Optimizing User Experience of Apps for SharePoint 2013