Wednesday, June 8, 2016

SharePoint mobile app + SharePoint Framework support?

The latest development approach for SharePoint customizations is via the new SharePoint Framework. MVP Waldek Mastykarz has multiple informative posts on what it is and how to utilize. In Everything you need to know about the SharePoint Framework he makes a remarkable statement: "When building solutions on the SharePoint Framework, if you follow the guidelines provided by Microsoft, not only will your solution look great on mobile devices but it will also be rendered in the native SharePoint mobile app which will be released shortly".
Remarkable, as I'm confused by how a native App could be running clientscript. So I challenged Waldek and Bill Baer via twitter to expand on that:
Well, turns out the statement is for now a bit too early. Microsoft is working on bringing this capability in the new SharePoint mobile app, and Vesa Juvonen acknowledges that Microsoft should tackle my question in their message.
UPDATE (June 13) Jeff Teper, interviewed at SharePoint Saturday Paris, answered on my question, being "We host html in JavaScript" [within the native code of the mobile app]. Although this is merely the high-over answer, and I want to understand more of the lower level details; this is sufficient for now. Details will be shared later by Microsoft and informed MVPs.

Wednesday, June 1, 2016

Performance repercussions of the Add-In model

The Add-In model is great to deliver self-contained functionalities, isolated from (harming) the SharePoint farm health - runtime and wrt physical installation. But be aware that there is a price: performance. In general, the overall performance will be negatively impacted due the Add-In model. Let me clarify:
  1. Conceptual every Add-In is an own and independent (mini) webapplication. Requesting a SharePoint page that hosts <X> Add-Ins, effectively means that the browser is visiting 1+<X> webapplications to load and render the page: first the SharePoint hostweb for the SharePoint hostpage, and next the individual AppWebs for the contained Add-Ins (SharePoint-hosted or Provider-Hosted).
  2. Each Provider-Hosted Add-In (re)enters in an own Add-In launch cycle, starting with App-authentication via the _layouts/AppRedirect.aspx page.
    Our renewed SharePoint based intranet heavily applies the Add-In model, with personalized homepage containing between 2-10 Add-Ins (mixture of SharePoint-hosted and Provider-hosted). In preparation of the Go-Live date, I validated performance and scalability through Visual Studio loadtesting. Due the multiplication of Add-In instances on the homepage there are multiple AppRedirect.aspx calls per homepage visit. On increasing the load the SharePoint server execution of that request became the scalability bottleneck, resulting in CPU to reach 100%. Investigation by Microsoft Premier Support confirmed that the high number (higher as what Microsoft architects had foreseen) of AppRedirect.aspx requests caused the peak in CPU:
    • The high CPU is caused by triggering of Garbage Collector
    • Garbage Collector is triggered due large managed heap, with int64 array datastructures
    • The int64 array datastructures are allocated by App Authentication, to encrypt and decrypt SharePoint internal certificates

    Note: As this (CPU reaching 100%) was for load well beyond both the expected / typical as the peak load, it was not needed to qualify as a No-Go for our Go-Live.
  3. Each Add-In is included in the page-html via iframe element. Due iframe boundaries, there is no runtime sharing possible of client-side objects and resources: every iframe must load its own required objects in own runtime browser context (can be from browser cache for static resources, if that is enabled at client and server-side).
  4. Be careful with building (or buying) too much self-contained Add-Ins, that all store the resources used by the Add-In in own AppWeb. Typical examples is sp.js. If that is administrated per AppWeb, then this heavy library will be retrieved multiple times: typical from the hostweb for standard SharePoint handling, and next from AppWeb(s) due Add-In usage. Due different urls, the browser does not recognize them as same resource, that is already retrieved. On each 'first' visit scenario this will result that the browser will retrieve the same resource multiple times, despite that it is already retrieved before and cached in browser.
  5. Resources that are provisioned as Add-In rootcontent are not included in the blobcache. The effect of this is twosome: [1] SharePoint does not cache the content in blobcache, and on every request must get it again from SharePoint content database; and [2] retrieved resources do not include the ‘max-age’ cache-control header setting (see Max-age cache-control setting for SharePoint content), and therefore the browser must every time ask the webserver whether client-cached resource is still valid or changed on the webserver side. Which in case of static resources (e.g., jQuery library) typically will not have changed, always responding with HTTP 304, and therefore wasting requests and network bandwidth + time.
    Via Yammer Office 365 Network I’ve inquired how-to reduce the number of 304 responses for an Add-In, but apparent this answer is not known as no answer is given - also not by any of the connected Microsoft Office 365 architects and engineers.
  6. Due own appdomain's, no sharing of http-connections across hostweb and the Add-Ins, and as result also no sharing of http-authentication context (see SharePoint App-Model + NTLM results in more 401’s).
  7. Client-side altering of the page-DOM results in a reload of all Add-Ins. This is standard iFrame behavior, implemented as such in all current browsers (E.g. iFrame reloading when moving it in DOM).
All of the above enumerated performance-degradation aspects are inevitable when utilization of Add-In model. Our end-users on average are confronted with one or more 'Working on it...' spinners when they open up their homepage filled with Add-Ins. We managed to mitigate for a large part by combination of some asynchronous behaviour, lazy loading, and enforce the Add-Ins to retrieve resources from shared location (hostweb, but can also be a CDN - internal or SharePoint static).
Once we're enabled to utilize the new SharePoint Framework, we will evaluate which of the Add-Ins can be migrated to that new model and thus break out of the iFrame boundaries + constraints. At minimal the SharePoint-hosted Add-Ins are good candidates for migration to SharePoint Framework based solutions.

Sunday, May 8, 2016

Critical thoughts on [yet another] new SharePoint development approach

This Wednesday May 4th, Microsoft announced GA of SharePoint Server 2016. This event is accompanied by blogposts from multiple MVP's. Waldek Mastykarz posted an interesting one "When to use the new SharePoint Framework". However, this posts holds a rather distracting message for us: Solutions built using other development models introduced in the past, such as farm solutions or SharePoint add-ins, cannot be used on modern team sites. Rather then repeating my concerns and thoughts here, I refer to the post of Waldek for full context, and the comments-thread between Waldek and myself.

Tuesday, May 3, 2016

Tip: Steps to resolve from parallel safe-conflict on provisioning a PageLayout

We’ve constructed our SharePoint 2013 based intranet cloud-ready, utilizing the AddIn-model. This for runtime functionalities, examples are Company News, My Favorites, My Tasks, Yammer AddIn's. And conform the Office 365 best-practice also for provisioning of SharePoint branding entities: masterpage(s), PageLayouts, content types. Yesterday I needed to reactivate a (new) version of a provisioning AddIn to update a PageLayout. However, exactly the update of that PageLayout failed at the App activation, with the message: Save Conflict – Your changes conflict with those made concurrently by another user.
The origin of this can be diverse. In my case it was for sure not due an actual parallel safe conflict. I suspect that as side-effect of a previous action, the safe-administration in SharePoint is no longer in sync with the state as original provisioned by the AddIn. But as SharePoint content database is a black box, almost impossible to analyze what caused the safe conflict. However, that is even not that interesting here. What counts is how to resolve such that the updated version of the PageLayout is successful provisioned by the AddIn. Well, this comprises of multiple steps that I outline below.

Approach to resolve from the safe-conflict

In concept this is simple: avoid the occurrence of safe-conflict by removal of the existing PageLayout, and next the new version can be provisioned without errors. However, this is easier said then done as the PageLayout typically is already in-use. And SharePoint is aware of that, and blocks the deletion as long as in use. So a prerequisite is to also and first remove the ‘used-by’ occurrences.

Steps to resolve from the safe-conflicts

  1. Open ‘Content and Structure’ from ‘Site Settings’, and navigate to the ‘Master Page Gallery’
  2. Select the PageLayout that gives the SafeConflict, and click in the top menubar on ‘Show Related Resources’
  3. This will expose whether and by which publishing pages the PageLayout is ‘Used-By’
  4. For each of these pages, edit them and temporarily associate with another PageLayout
  5. After all relevant pages are deassociated from this PageLayout [you can verify this by refreshing ‘Show Related Resources’], you can delete the PageLayout from the masterpage catalog
  6. And now activation of the provisioning AddIn will no longer give a safe-conflict
  7. Afterwards, reassociate all the relevant pages with the (new version of the) PageLayout.

Thursday, April 21, 2016

Freeze pane on SharePoint list

In case of a larger SharePoint list, end-users may appreciate that the header row remains in the visible area while they scroll through the list. Out-of-the-box the SharePoint XsltListView webpart does not provide this functionality. However, it is possible to augment the standard behavior by modifying the rendered HTML through javascript. There are multiple examples published, but for some reason these did not immediate work for me. Did get me inspired though, and I coded a jQuery-snippet that when inserted in a page containing a XsltListView webpart delivers the 'freeze pane':
<script src="https://code.jquery.com/jquery-1.11.2.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { var $table = $(".ms-listviewtable").first().data("summary", "<listview name>"); // WRAP TABLE IN SCROLL PANE $("#s4-workspace").css( { overflow: 'hidden' } ); $table.wrap("<DIV class='FreezedLV' style='OVERFLOW: auto; HEIGHT: 550px;'></DIV>"); // FROZEN HEADER ROW $(".FreezedLV").wrap("<DIV class='FreezedLVContainer'></DIV>"); $("<table id='FreezedTR' class='ms-listviewtable' cellPadding='1' cellSpacing='0'></table>"). insertBefore(".FreezedLV"); $("#FreezedTR").width($table.width() + "px"); var $origHeader = $("TR.ms-viewheadertr:first", $table); var $firstRowTable = $("TR.ms-itmhover:first", $table); var $freezeHeader = $origHeader.clone(); // Propagate the computed width of columns of origheader to freezeheader $origHeader.children("th").each(function() { var width = $(this).width(); var ownerIndex = $(this).index(); $($freezeHeader.children("th")[ownerIndex]).width(width + "px"); }); // Propagate the implicit width of row columns as explicit widths in first row to // avoid layout-changes when hiding the original header $firstRowTable.children("td").each(function() { var width = $(this).width(); var ownerIndex = $(this).index(); $($firstRowTable.children("td")[ownerIndex]).width(width + "px"); }); $("#FreezedTR").append($freezeHeader); $origHeader.hide(); }); </script>

Tuesday, April 19, 2016

How-to have IE11 pick up local PAC file

In our company we utilize a cloud-hosted Proxy Automatic Configuration script (PAC file). On occasion it is convenient to switch to a local version, e.g. to investigate or troubleshoot. When we still were at IE9 this was easy, but since we’ve upgraded the company browser to IE11 it becomes more difficult. To promote interoperability across network stacks, Microsoft deprecated the option for a local PAC file. You can still configure it in Internet Settings, but IE 11 will simple ignore a local proxy auto-config file.
However, Microsoft also understands it’s backwards compatible responsibility. And therefore build in support to overrule the deprecation of local PAC file. The approach consists of 2 steps:
  • Step 1: [source: Understanding Web Proxy Configuration] Via registry value 'HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\EnableLegacyAutoProxyFeatures' specify that it is ok to utilize the legacy Internet Settings. Note that in our company the regular users are not enabled to set this, as they are not administrator on their workstation. But the occasional usage of local proxy file is then also not for these regular end-users.
  • Step 2: [source: local proxy which is set in the proxy auto config of IE11 is not processed correctly by IE11] Create the local PAC file as "c:/windows/system32/drivers/etc/proxy", and specify in IE as setting for automatic configuratio script = 'file://c:/windows/system32/drivers/etc/proxy'

Friday, April 1, 2016

Corrupted blobcache hindered responsive rendering

In the branding of our SharePoint 2013 based intranet we utilize Bootstrap for responsive design. However, we noticed that the rendering for mobile user agents was not correct, e.g. the company logo was displayed twice. Remarkable was that in our acceptance environment, it did render correct. On analysis I first discovered that the requests of some resource files (javascript, css) resulted in AccessDenied, but only for mobile UserAgent in the request header:
  • User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) ➜ Downloaded
  • User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 ➜ Not downloaded
Also I noticed that the response for IE as User Agent was successful serviced, but that 'max-age' setting was set to zero in the response (Cache-Control). As on SharePoint webapplication level the Cache-Control header setting is controlled by SharePoint blobcache, this put me in the direction of a blobcache corruptness issue in our production environment. To resolve from the corruptness, our SharePoint operations created a new blobcache for the intranet webapplication. And indeed this resolved both issues: max-age value is set with a positive value (so that lesser times the resource is requested by browser), and also for mobile UserAgents the resource request are now responded with the actual files.