How I built our first commercial SharePoint hosted app Lessons learnt from building the KWizCom Cascading LookApp
Jan 15, 2015
How I built our first commercial SharePoint hosted app
Lessons learnt from building the KWizCom Cascading LookApp
KWizCom Corporation
• Founded in 2005
• Headquartered in Toronto
• Global vendor of SharePoint Add-Ons
• 5000+ customers worldwide
Shai Petel
Director of Research and Development at KWizCom
MCPD, MVP: SharePoint
[email protected] | @shaibs | http://kwizcom.blogspot.com
Overview
OVERVIEWSharePoint apps, your options
SharePoint app store
Provided Hosted?
SharePoint Hosted?
Use JSLink to customize columns and list views
Build app parts to display information whoever you wish, from any data source you want.
OVERVIEWApp licensing
Use Microsoft Store licensing
Per seat, unlimited threshold
Subscription or Purchase*
Auto generate trial liense
Or, build your own licensing service.
OVERVIEWPublish a new version
OVERVIEWUpdates, upgrades
Go to site contents
Updates will show up here, next to the app. Click the link
to update your app.
App web vs. Host web
APP WEB VS. HOST WEBApp login problem, single sign on
App login problem, single sign on
App web on different url, requires separate login
Single sign on in place for most cases
Single sign on will not work for resource files (css, js, images, etc…) but only in iframes
APP WEB VS. HOST WEBIE 8,9 known issue with trusted sites
IE 8,9 known issue with trusted sites
Known issue on IE 8, 9
Requires adding both domains to trusted sites
Read more: KWizCom KB http://support.kwizcom.com/KnowledgebaseArticle50208.aspx
APP WEB VS. HOST WEBProvisioning files to host web
Provisioning files to host web
Find site assets library, create a sub folder for your app and upload the files there
Cannot read files, only upload
Minimize your foot print as files are not cleaned up on uninstall
APP WEB VS. HOST WEBLoading additional resources from app web
Loading additional resources from app web
After single sign on issue resolved
In effort to minimize footprint, only provision the file that will handle the single sign on issue
Can use host web URL, will redirect to app web URL automatically
APP WEB VS. HOST WEBCheck for app license from host web
Check for app license from host web
Only for SharePoint hosted apps
Only within the app site itself
From host web, must use an iframe (licensechecker.aspx)
Using JSLink
USING JSLINKPower of client side rendering
Customize the way your columns and views look
Add special logic in editing your lists
USING JSLINKHow to register for specific field, not for type
How to register for specific field, not for type(function () { var ageFiledContext = {}; ageFiledContext.Templates = {}; ageFiledContext.Templates.Fields = { "Age": { "NewForm": ageFiledTemplate, "EditForm": ageFiledTemplate } }; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(ageFiledContext);})();function ageFiledTemplate(ctx) { var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx); formCtx.registerGetValueCallback(formCtx.fieldName, function () { return document.getElementById('inpAge').value; }); return "<input type='number' id='inpAge' min='18' max='110' value='" + formCtx.fieldValue + "'/>"; }
Register to field name “Age”, or field
type “choice”
How to register for specific field, not for type Pass field name in query stringfield.set_jsLink("mycsr.js?FieldName=Status");field.update();
Catch it in the JS file, use it to register your templatevar scripts = document.getElementsByTagName('script');for (var i = 0; i < scripts.length; i++) { if (scripts[i].src.toLowerCase().indexOf("mycsr.js") >= 0) { var currentField = KWizComUtilities.GetQueryStringParameterByName("FieldName", scripts[i].src);… currentFieldRendering.Templates = {}; currentFieldRendering.Templates.Fields = {}; currentFieldRendering.Templates.Fields[currentField] = {}; currentFieldRendering.Templates.Fields[currentField].EditForm = myTemplate; currentFieldRendering.Templates.Fields[currentField].NewForm = myTemplate; SPClientTemplates.TemplateManager. RegisterTemplateOverrides(currentFieldRendering);}
How to register for specific field, not for type
TIP: Query strings are very important. You need SharePoint to tell your external website important information about where in SharePoint it is coming from.
USING JSLINKSupport MDS
Notice it always stay on the same page
Support MDS
On MDS enabled sites, most online samples will not work after few page navigations
When you revisit a page, document.ready event does not fire
Instead, you must register your init code here:RegisterModuleInit(jsfileName, initHandler);
USING JSLINKStore additional settings
Store additional settings Additional column settings can be stored in field schema or query string In schema, under<Customization> <ArrayOfProperty> <Property> <Name>x</Name> <Value>Y</Value> </Property> </ArrayOfProperty></Customization> Schema changes might break OneDrive support in document library For simple settings, you can use JSLink query string
App License
APP LICENSECheck for license using script
Check for license using script
Check for license using script Requires adding remote endpoint to
https://verificationservice.officeapps.live.com Request must come from the app web Get a collection of licenses, sometimes expired
licenses as well
licenseCollection = SP.Utilities.Utility .getAppLicenseInformation(context, '{cd8d046c-91d4-401f-825d-494e6c2776c5}'); context.executeQueryAsync(onRetSuccess, onRetFail);
On Success:if (licenseCollection.get_count() > 0) { license = licenseCollection.get_item(0) .get_rawXMLLicenseToken(); processRawXmlLicense(license);}
Process license:
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(context, request);
context.executeQueryAsync(onVerifySuccess, onVerifyFail);
On Success:
var verificationResponse = response.get_body();var xmlDoc = $.parseXML(verificationResponse);var $xml = $(xmlDoc);var licenseType = $xml.find("EntitlementType").text();var licenseIsValid = $xml.find("IsValid").text().toLowerCase() == "true";var licenseIsTest = $xml.find("IsTest").text().toLowerCase() == "true";var isExpired = $xml.find("IsEntitlementExpired") .text().toLowerCase() == "true";
Prompt user on license issues, once is enough
APP LICENSETest licenses
Test licenses
Create / Manage using code or tool: http://code.msdn.microsoft.com/office/SharePoint-2013-Import-f5f680a6/view/SourceCode#content
Don’t forget to check if a license is a test license
Block test licenses on production build, or show a message
SharePoint 2013 Import, validate, and manage app licenses.zip
Adding a ribbon button
ADDING A RIBBON BUTTONCheck for license using script
Adding a ribbon button Limited use of CustomAction element allows to add ribbon buttons
CommandAction can only open a page or a popup<CommandUIHandler Command="cmd1"CommandAction="~appWebUrl/Pages/ListDialog.aspx?{StandardTokens}&SPListItemId={ItemId}&SPListId={ListId}"/>
Open as popup option<CustomAction … HostWebDialog="TRUE"HostWebDialogHeight="600" HostWebDialogWidth="1050"
Scripts are blocked
Single sign on issue cause image to be missing…
Will start working once the user had the iframe log him
into the app web.
Protecting your code
PROTECTING YOUR CODEProtect JS code
Protect JS code
Have app.debug.js with your source
Upon publish, obfuscate it and save in app.js
Consider not publishing debug.js with your app to protect your full source
Protect JS code
http://javascriptobfuscator.com/default.aspx
PROTECTING YOUR CODEDebug
Debug client code, or on your server. Nothing to debug on SharePoint since your app does not run on SharePoint.
Debug
Use fiddler, IE developer tools
On your customer’s site you will still get the obfuscated script.– For files in the app site, live with it!– For provisioned files, upload the source file for easier
debug
On prem vs. o365
ON PREM VS. O365API is different
API is different
Client side API is different and more hardened usually in O365
Basic changes in functionality, some APIs are blocked while other behave different
Example: field.update will cancel field.set_schemaxml on o365 only
ON PREM VS. O365Updates
Updates
On O365, updates are regularly pushed and installed throughout the farm.
Not all tenants are upgraded at the same time! Your dev/test might be older or newer than some tenants.
ON PREM VS. O365Maintenance
Maintenance
App break. You need to keep an eye out.
Updates sometime will break your app. (App / user throttling)
Support and publishing updates is key to success.
QUESTION?
Shai Petel
Director of Research and Development at KWizCom
MCPD, MVP: SharePoint
[email protected] | @shaibs | http://kwizcom.blogspot.com