Friday, December 29, 2017

How-To: enable Azure Active Directory for Powershell on Windows 7 system

The AzureAD PowerShell module contains multiple convenient cmdlet's to manage an Azure AD tenant, a.o. to invite external accounts (e.g.: Automatically Provision Azure AD B2B Guest Accounts). When you try to use the AzureAD PowerShell module on a Windows 7 system, you may run into issue: The specified module 'AzureAD' was not loaded because no valid module file was found in any module directory
If so, the solution is import the module file via it's exacte filepath (inspiration for this: Azure RM PowerShell Module / Other Installation methods)
And the AzureAD cmdlets can be succesful utilized:

SharePoint Online External Sharing: blacklisting overrules whitelisting

Via the configuration capabilities of Office 365 SharePoint Online External Sharing, the SharePoint administrator has full control to both allow and disallow specific organizations by domain. Be aware that the 'disallow' configuration has higher prevalence then the 'allow': if you run into a situation that you cannot invite a guest account of whitelisted domain, then check your blacklisting (on tenant plus site collection level) whether the organization domain is also listed there.
Result: blacklisting overrules the whitelisting allowance

Sunday, December 17, 2017

Beware Column Formatting with JSON requires the New Experience

Column Formatting with JSON is a nice approach, with relative low knowledge hurdle, to customize the rendering result of a column. However, be aware that it only works in the New / Modern Experience; for Lists that are in the Classic Experience the Column Formatting is ignored / not applied.

Thursday, December 14, 2017

Automatic Azure AD B2B redemption is not feasible

Azure AD Business-2-Business is the enterprise-ready and secure new approach to enable SharePoint Online external sharing. In this approach, externals are added as guest users in the Azure AD of the inviting company. The guest account in the host Azure AD functions as placeholder to the actual account of the invited guest in external identity administration. Azure AD B2B is an implementation of federated Identity Management.
The process to authorize an external person for access in shared SharePoint Online site consists of 2 steps:
  1. First, Azure AD admin (or anyone who has the “Guest Inviter” role) has to add a guest account to the host Azure AD
  2. Next, site owner can invite the guest account to the external shared site
However, it turns out that there is some usage unclarity and sequence dependency in this process:
  • Usage unclarity: For the first step, the guest receives invite in mailbox to accept / redempt the invite. But next the guest user is redirected to empty Apps page in the tenant of inviting organization ==> no authorizations are granted yet;
  • Sequence dependency: The site owner cannot execute the 2nd step until the Azure AD invite is redempted ==> 'Sharing failed: Sharing to external users is not supported' (which is a misleading / incorrect error message; sharing is supported, yet not to the particular guest account as long (s)he has not redempt the invitation to the hosting Azure AD)
To address the first drawback, which may result in negative first impression with the invited guest ("I only see an empty Apps page, cannot do anything"), you can utilize the PowerShell Azure AD cmdlet 'New-AzureADMSInvitation' with '-SendInvitationMessage' parameter set to false. The result is that the invited guest is not informed yet of the invite to Azure AD, that from the perspective of the guest user is useless anyhow: one can only do something after authorized to a SharePoint site. But the indirect result is that the Azure AD invite is not redeemed - the guest is not made aware nor asked to perform the redemption -, and this results that the site owner cannot add the guest user. A catch22 situation.
A potential way out would be to automatic redempt the Azure AD invitation on behalf of the invited guest. However, this is only possible in case the organization of invited guest itself has an Azure AD tenant. Although the usage of Azure is growing in the market, there is and will remain a large set of organizations that have not themselves an Azure subscription + tenant. And then the automatic redemption on behalf of guest users is not possible.

Thursday, December 7, 2017

How-to identify faulty webpart on SharePoint (Online) page

Today business called in distress: they made a change in the start page of an important business site (solution) in SharePoint Online; and next on opening the page in browser it remained loading / spinning.
To support, I was temporary granted contributor authorization, and then applied the following steps to analyze + identify:
  • Step 1: very important, determine whether you can reproduce yourself;
  • Step 2: (answer was yes) Open the page in webpart maintenance mode, by adding "?contents=1" to query string;
  • Identify in the set of webparts on page which one could potential cause a problem; e.g. ImageViewer webpart is very unlikely; but anything that adds content can be;
  • Step 4: Close first of the potential suspect webparts;
  • Step 5: Reopen the page in normal mode, and verify whether it now can load;
  • Step 6: Continue with 4 and 5, until finally the page loads successful again;
  • Step 7: Once the faulty webpart identified, re-add the other ones: open page in edit mode, insert webpart, and select the closed webparts from webpart category 'Closed Parts'
  • Fix the faulty webpart, or delete it from the page.
In this particular situation, a Content Editor WebPart was added to the page that contained script with self-reference to reload the page; resulting in endless loop. This cannot be fixed, best option is to delete from page, and re-code the client script (but make sure to avoid the self-reloading...)

Friday, December 1, 2017

2 approaches to enable MFA for Azure AD B2B guest accounts

Element of Azure AD B2B SharePoint External Sharing, is to enforce multi-factor authentication for the external guest accounts. Documentation how-to enable is a bit difficult to find. Also in my search I encountered that there are actually 2 approaches to enable MFA condition for guest accounts.

Option 1: Direct enable on Azure AD user level

This is the option I first trembled into, due (my) inability to find proper documentation how to enforce the MFA rule. Approach here is to open Azure AD Admin, open 'All Users', click 'multi-factor authentication' in the top bar, and select the (guest) user accounts for which to enable MFA. Frankly I have the suspicion that this approach is there by accident. Motivation for that thought is that I only could enable MFA for guest users via a trick: selecting only guest users does not offer the enable/disable MFA option. However if you also select a regular account, the menu option becomes visible and the execution is applied to all selected accounts, including the guests. Not direct logical, may very well be a functional bug.



Option 2: Indirect enable via Azure AD Conditional Access

This approach appears to be the more structural, with management on higher level as individual guest users. Approach consists of following steps:
  • Create in Azure AD 'groups and users' a new group with dynamic membership, and rule equal to "userType Equals Guest"
  • Create in Azure AD Conditional Access a new policy, as membership include the just created group (of external accounts), as App select Office 365 SharePoint Online, and as Control select 'Grant Access under condition of Conditional Access.
These 2 configuration steps ensure that MFA is immediate applied for all guest accounts, without need to maintain this on the individual user account.



Friday, November 17, 2017

Convenient delete a single document from large list

SharePoint Lists and Libraries can store large amounts of (document) items. This does complicate the management of the stored items: before you can edit or delete the item, you need to find it in the large list storage. As result of the listview threshold max 5000 items are displayed, and you will therefore likely spend a lot of time navigating page-wise through the list/library before you eventually find the item you want to manage. When it concerns a regular list, you can automate this item-search via "search in 'this list'" (<site-url>/_layouts/OSSSearchResults.aspx?cs=This%20List&u=<list-url>); however for managing document items this does not help: the searchresult does not display the document library ribbon, and thus you cannot activate for instance the 'Delete Document' action. But the SharePoint toolbox has more to offer: setup an interactive listview search experience by combining it with a connected TextFilter webpart. For the how-to I refer to Connect a Filter Web Part to a List View Web Part on a classic page.
Screen impression of the convenience result:

Tuesday, November 7, 2017

Retrieve data from a large list via REST

The infamous ´5000´ listview threshold, we all have encountered that limit at least once in utilizing SharePoint as data backend. This time I was consulted by a business user that utilized SharePoint´s data management capabilities for storage of above 135,000 listitems. Wrt storage this amount is not an issue, but for retrieving it can be due the listview threshold. The advised approach to deal with that is via indexed columns, and tabbed/indexed views. That is for retrieving + viewing the big amount of listitems in the standard SharePoint UI. But what about requesting the data via SharePoint REST service? The REST protocol promises to support a similar navigation/tabbed experience via $top and $skip parameters. However, here SharePoint (2010) demonstrates to be not a fully compliant REST citizen. The $top parameters works fine on indexed large list, but usage of $skip results in an HTTP 500; and in ULS the error "Throttled:Big list slow query. List item query elapsed time: 0 milliseconds" is logged.
Also here it turns out that the '5000 threshold' is such a common encountered SharePoint issue. Internet search within a few hits leads to the helpful Stackoverflow resource: SharePoint 2010 REST top, skip fails on large list:
$skip query option is not supported, but $top is. Workaround, use a mix of $orderby on ID, $filter on ID and $top x items, and loop
Pseudo-code to loop through the entire big SharePoint List:
var nextId = 0;
WHILE TRUE DO
    var getData = $.getJson(“<site-collection url>/_vti_bin/listdata.svc/LargeList"
          + "?$select=Id,Name&$top=1000&&orderby=id&$filter=Id gt " + nextId);
    if (getData is not empty) {
        nextId = getData(last)[id];
    } else {
        break;
    }
END DO

Tuesday, October 31, 2017

Microsoft security (tools) landscape

Microsoft as platform vendor takes information security and protection very serious. It delivers a diverse set of tools and services to help enterprises protect the valuable resources. The protection differentiates on 4 layers:
  1. Devices
  2. Apps
  3. Files
  4. Identity
In below diagram, the palette of tools are displayed in their mutual relationships in the full Microsoft security landscape (with thanks to Peter van Leeuwen / Microsoft):
Legenda:

AbbreviationExplanation
AADAzure Active Directory
ADFSActive Directory Federation Services
MAMMobile Application Management
MDMMobile Device Management
MFAMulti-Factor Authentication
MIMMicrosoft Identity Manager
NDESNetwork Device Enrollment Service
PIMPrivileged Identity Management
RMSRights Management Services
SCCMSystem Center Configuration Manager
SSOSingle Sign-On
WIPWindows Information Protection

Thursday, October 19, 2017

Tip: code-snippet to enlarge image in context of SharePoint page

Requested Web Content Management (WCM) capability: include smaller / shrinked version of an image in the body of a page, and enlarge it on user click while remaining within the context of the page.

The rich SharePoint platform enables this with a small code snippet, via re-use of the standard SP.UI.ModalDialog.showModalDialog function. Reusable code-snippet:

<div>
   <script type="text/javascript">  
       function EnlargeImageInDialog(elem) {
           var imgUrl = $(elem).find("img").attr("src");
           imgUrl = imgUrl.replace("/_w/", "/").replace("_png.jpg", ".png”);
           var popupImg= 
                 "<div id='enlargeImg'><img src='" + 
                 imgUrl + 
                 "' style='margin: 5px; width:1200px;'/></div>";
           $(elem).append(popupImg);     
 
           SP.UI.ModalDialog.showModalDialog({
              html: document.getElementById('enlargeImg'),
              title: "...",
              allowMaximize: false,
              showClose: true,
              autoSize: true
           });
       }
    </script>   
    <a onclick="EnlargeImageInDialog(this); return false;" href="">
        <img src="/.../PublishingImages/_w/..._png.jpg" alt="" style="margin: 5px;"/>
    </a>
</div>
Example of the effect:
Smaller image (reference) in the web content page
Enlarged image displayed within context of the webpage via a SharePoint UI modal dialog popup

Sunday, August 27, 2017

Community answerring on typical SPFx usage scenarios

Vesa Juvonen asked the community to give some typical examples of business 'applications' that are build as client-side applications, before typical by utilizing ContentEditor or ScriptEditor. And now likely candidates for SharePoint Framework (SPFx) utilization:
Naturally I'm a good community participant / citizen, and answerred with some example scenarios I build myself of behalf of internal business stakeholders...

Friday, July 21, 2017

Enable CORS not possible on SharePoint authenticated application without HttpModule

On 1st research, enabling CORS on SharePoint (on-premisse) web applications seems to be easy: just configure it in the web.config.
Source: enable Cross-Origin Resource Sharing - CORS on IIS7:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration>
(also given as answer on stackoverflow: Enable CORS in SharePoint 2013)
However, on validating the effect of the config change, it turns out that the web.config based approach is insufficient in case of authenticated SharePoint application. You encounter 2 issues with only the configuration based approach:
  1. In the web.config you can only specify either the '*' or explicit client domain for the Access-Control-Allow-Origin header. To interoperate with an authenticated application, you need to pass authorization headers and set Access-Control-Allow-Credentials to true. The issue is that according to the CORS specification (see also explanation on MDN, HTTP Access Control), if Access-Control-Allow-Credentials is set to true, Access-Control-Allow-Origin cannot contain *, to disallow just any client making requests with credentials attached. Also on CORS specification level, it is documented that in case of specific domain list, it actually means 1 single specific domain (https://www.w3.org/TR/cors/#access-control-allow-origin-response-header). This limits enable CORS to a single client application.
  2. First call in the CORS protocol, is HTTP OPTIONS to establish whether CORS is allowed between client host domain and server host domain. A web.config enabled SharePoint application responds successful on the OPTIONS call with CORS-allow headers. However, SharePoint / IIS returns for authenticated webapplication the OPTIONS response with status code 401; and as result the preflight handling is stopped in browsers that respect the CORS specification.
Conclusion:
Enable CORS for a SharePoint / IIS authenticated webapplication, cannot be done via configuration only. Resolving both issues strict on SharePoint level is possible, but requires a custom HttpModule. If such server-side deployment is not allowed, another option is to resolve it on infra level, via a Reverse Proxy setup.

Saturday, July 15, 2017

Approach to performant display from a SPList with multiple Lookups + Person fields

Issue: business users complain that the page loading of a SharePoint listview takes a long time, up to a minute. During page loading + rendering, the browser is totally unresponsive (Chrome even pops up a dialog about page unresponsive).
In the analysis for the root cause of this structural slow performance I observe that the list contains multiple Lookup fields to other lists in the site, and also a 'Person or Group' field - which is a specific type of Lookup, to the hidden UserInformationList.
This multitude of Lookups in the list is the most significant cause for the slow performance. However, also on user experience / functional level, it is wrong designed: the usage of the page is to first load + display all the items from the list, and next the end-user must filter to select the relevant items.
I therefore first aligned with business owner on another functional approach: let the end-user start with specifying the relevant filter(s) [can be a combination of filters for multiple columns], and then select + retrieve only the items from the list that satisfy the filter-conditions. For the technical design, the Search tool must be setup future-proof, aka cloud ready. Thus interoperating with SharePoint either via CSOM or via REST services. I decided to utilize REST, so that data is returned in JSON data-format, and can be directly data-binded in client-side UI (I used Knockout.js; but same holds for other clientside UI frameworks as Angular, Ember).
In SharePoint REST one uses the $expand parameter to include referred lookup values in the result set. But similar as for XsltListView, this quickly destroys any performance:
So I needed a way to avoid the Lookup-expands, while still being able to filter on and select the values of the Lookup fields. Totally getting rid of the Lookup columns is not an option: for consistent data-management it is a Must-Have that one can select only from the values maintained in the Lookup list. And it holds even more for the Person field: selecting via the PeoplePicker ensures a user-friendly selection + validation.
The approach I decided to is to 'flatten' the lookup values in additional columns. Functional management can still manage the data-items using the lookup functionality, and on data-selection + retrieval I avoid the need to $expand.
The elements of the approach
  1. Per lookup value that is needed in the selection and/or display, add a single-line of text column to the list. Set them to hidden in the Item content type, so that their existense is invisible for functional management in the New/Edit/View list-forms;
      For the 'Person or Group' column, include 4 new fields, for
    1. Person Name,
    2. Photo,
    3. Department,
    4. and the UserId in the UserInformationList
  2. Create a SharePoint Designer Workflow on the list that activates on ItemCreated + ItemModified events. Design the workflow to propagate ('flatten') the lookup values to their respective flattened counter-field;
  3. Realize the bulk flattening of the existing list items through the same workflow, via javascript start the workflow on every item.
Impression of the solution setup + result

Wednesday, June 21, 2017

Visualization of analysis process from business demand to requirements

An IT project typically starts with a business demand for a new or modified functional capability. IT answers on the demand by selecting a most fit solution addressing the core of the business demand. Central element is to identify the requirements - functional + non-functional. In below figures, visualization of the requirements analysis process I apply:

Sunday, June 4, 2017

So much to share about...

Since ages, missed publishing a monthly blog (this one, and also on Thoughts on SAP Gateway Development). Not due lack of content, on contrary, but due lack of time. Topics ao our experiences with Office 365 implementation, SharePoint Online performance validation, upcoming go-live of our renewed supplier + customer portal in SAP Fiori Launchpad, architectural investigation into topic of Enterprise Video Platform, my thoughts on attending Azure Red Shirt Development Tour (watching full day of The Scott Guthrie on stage), how-to help business improve a business scenario involving a multi-lookup column, our experiences on custom build mobile App that will consume a.o. from on-prem People Search and also from Office Graph, conditional access, Intune Authentication Scenarios, Customer Portal / webshop in SAP Hybris. Just to name a few...Will share on some of the above in near time.

Saturday, April 22, 2017

Adding tenant-url to trusted sites improves performance + user experience

We employ Application Performance Management to structural monitor the performance of company applications, including SharePoint Online. Via APM we observed a severe effect of initial login to Office 365: it structural adds up to 8 seconds to the actual SPO activity, for all of our worldwide locations:
And via the same structural APM measuring this week we noticed that the effect of initial login was largely improved, to a mere 2 seconds. We analyzed what changes were done on our side that caused this performance improvement. Turns out that a general policy update was applied in which multiple Office 365 URLs, including that of our SharePoint tenant, were added to IE trusted sites. This workplace change has a significant positive effect on the Office 365 network access times, and it also results in situation with federated login that IE automatically logs the user in.

Friday, April 7, 2017

System architecture of Skype presence in SharePoint

Business end-users really appreciate the SharePoint capability that displays presence information of your colleagues, e.g. in a people results overview. They take this capability for granted, and request the same in other (web)applications, and even on other devices - mobile in particular. However, that is not that trivial to accomplish. The SharePoint presence capability current really shines in the combinations of Microsoft specific clients - IE browser, Outlook mailclient -, and the Skype client process on the local device. That local Skype client process functions actually as interface gateway to access Skype functionalities: retrieve presence information, start chatting.
Windows OS
Presence indication in SharePoint pages works via Microsoft proprietary combination: IE plug-in, that communicates via a proprietary protocol on Windows OS level against the local Skype client (process). The local Skype client on its turn connects to the central Lync / Skype server.
With this IE plug-in enabled, and local Skype client active + logged in; the presence information of colleagues can be retrieved and visualized in IE
Mobile OS
On Mobile OS, the system architecture is in essence the same: the local Skype Client operates as ‘gateway’/interface to the central Skype server. Element of the local Skype client deployment is an API via which custom applications can invoke Skype (client) functionaliteit: Skype for Business App SDK. Current this API includes capabilities to start a chat, start a video playing. There is yet no endpoint in the Skype client API to retrieve presence information.

Sunday, March 26, 2017

SharePoint integration endpoints for a SAPUI5-based PeopleFinder

SAPUI5 is a suitable framework to build responsive-design UIs that renders to the available screen estate of diverse device types: smartphone, tablet, and desktop. It is therefore a fit to deliver an alternative UI to SharePoint's PeopleFinder functionality, in case the out-of-the-box SharePoint UI (that is, with potentially the rendering still made company specific via customized Search Display Templates) for whatever reason does not qualify as sufficient fit. The basic requirement of a SAPUI5 mobile application is that it can consume data and functionality via OData REST services. The SharePoint platform supports such an architecture via the standard SharePoint REST services:
The integration surface for a peoplefinder functionality comprises 3 main elements:
  1. To interactive present people suggestions to user while typing in characters of the people name ==> Get names list of matched users on search input
  2. Get detailed list of matched users on search input
  3. Get details for selected user
1. Get names list of matched users on search input

Best:
SharePoint end-point 
https://<SharePoint root-url>/_api/search/query?querytext='preferredname:Jones*'''&selectproperties='PreferredName'&sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&rowlimit=10

Alternative is to search in User Information List:
SharePoint end-point 
https://<SharePoint root-url>/_vti_bin/listdata.svc/UserInformationList?$filter=((ContentType eq 'Person') and (substringof('Jones',LastName)))&$orderby=Name

but this has some disadvantages:
  • Incomplete qua users: user is only added to UserInformationList on first visit to SharePoint site; users not visited yet, are not included
  • Overcomplete: UserInformationList is never cleaned up, former colleagues remain in the list
  • Incomplete qua search: UserInformation does not contain a full name field
2. Get detailed list of matched users on search input

Search in full content / all crawled people data fields:
SharePoint end-point https://
<SharePoint root-url>/_api/search/query?querytext='Mobile*'&selectproperties='FirstName,LastName'&sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&rowlimit=10

Search in identified property-field(s) only:
SharePoint end-point https://
<SharePoint root-url>/_api/search/query?querytext='department:Mobile*'&selectproperties='FirstName,LastName'&sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&rowlimit=10
3. Get details for selected user

Get all public properties:
SharePoint end-point https://
<SharePoint root-url>/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='<uname of user>'

Get one single identified user profile property only:
SharePoint end-point 
https://<SharePoint root-url>/_api/SP.UserProfiles.PeopleManager/GetUserProfilePropertyFor(accountName=@v,propertyName='AboutMe')?@v='<uname of user>'

Wednesday, February 22, 2017

Beware: SharePoint App usage depends on OneDrive

I recent played around with the SharePoint App on multiple mobile platforms: iOS, Windows Phone 10 (yes, I still have that...). I connected to our SharePoint Online environment (the App also supports to connect to SharePoint 2016 on premises). Although the (navigation in the) UI needs some time to get acquainted, the SharePoint App definite has potential to become a low-barrier anytime/anyplace entrance into SharePoint. Access through the hamburger-menu the content of the SharePoint site collection, pages, list items. And document libraries? Well, yes and no: trying to navigate and open any document library from the menu resulted in an error message about 'missing SharePoint license'; which I'm pretty sure that I possess... However, navigating via 'Site Contents' to the document libraries does work, presents the library view, and allows to open the administrated documents in/via the App.
From user experience perspective this is undesired and also inconsistent behavior. On the one hand, trying to open it from the end-user logical approach via the menu results in an error that you cannot relate; and on the other hand when you navigate via alternative path in the App the document library contents can be accessed.
The question is then foremost why it does not work via the hamburger menu? I derived the answer to this when I tested the SharePoint App on an iPad, as then in the screen I observed a message about trying to start OneDrive to operate the document library from within the App. And current we have not yet enabled OneDrive in our Office 365 tenant. So it not that I do not have a SharePoint (online) license in our tenant, but the OneDrive license.
Bottom-line: productive and user-friendly usage of the new SharePoint App, requires that OneDrive is enabled for you as Office 365 end-user.

Saturday, January 28, 2017

Pragmatic approach to push update of Add-In when via SharePoint UI fails

In previous blogpost, I recalled on a malforming COTS Add-In. Once the supplier delivered a fix, our SharePoint IT Operations team executed a change to update the Add-In version in the SharePoint environment. First upload the new version to the SharePoint AppCatalog, and next update the installed Add-In in the using SharePoint site(s). Both steps can be done via the SharePoint GUI, the second via the 'Get It' capability. The operations teams verified the deployment approach first in the acceptance environment, and validated that the problem with Add-In was resolved. All well. However, during the change execution in the productive environment, a problem was raised on the 'Get It' step to update the Add-In installation in the using site: Sorry apps are turned off. if you know who runs the server tell them to enable apps. I first told them to verify that both App Management Service and Subscription Settings Services were (still) enabled on the SharePoint webapplication. On the positive answer, I then concluded the problem to be likely as a glitch in the SharePoint site. The Add-In itself namely was unlikely to have a deployment problem, as we earlier successful updated the deployed installation in the acceptance farm. Thinking out-of-the-box, I next advised the IT Operations team to retry the Add-In update through PowerShell iso via the SharePoint GUI. Either it would work, or otherwise a bigger chance of getting meaningful error information. In our case, the former was true: via PowerShell the Add-In update was successful pushed in the using SharePoint site.
PowerShell snippet:
$web = Get-SPWeb ‘https://<site-url>’ $appPackage = Import-SPAppPackage -Path ‘<Add-In deployment package>.app’ -Site $web.Site -Source ‘CorporateCatalog’ -Confirm:$false $curAppInstall = Get-SPAppInstance -Web $web | where Title -eq ‘<Add-In title>’ Update-SPAppInstance -Identity $curAppInstall -App $appPackage -ErrorAction SilentlyContinue -ErrorVariable err if($err) { $err[0].Exception }

Friday, January 27, 2017

Pragmatic approach to display maintenance message on malfunctioning Add-In

One of the promises of the SharePoint Add-In model is that one can purchase packaged / of-the-shelf Add-Ins to install and use in your SharePoint sites. This promise is picked up by the SharePoint community - although in lesser amount as what Microsoft was expecting. One of the selling charms of using pre-packaged software Add-Ins is that your organization is not self responsible for maintenance, and this is delegated to the supplier. However, this loss of technical ownership also implies that you are dependent on the external supplier to fix their Add-In in case of issue, you cannot call in your own troops to resolve the problem. When such a faulty Add-In is prominent present in your SharePoint site, this is rather confronting: the end-users are visually notified and remembered of the malfunctioning behavior. In case the Add-In is added as a shared AppPart, you can mitigate this effect by temporary hide the AppPart from the page. But in case the Add-In is potentially added by individual users to a personalizable page, this is undoable: you don't want to traverse all the personalized instances of the page, check whether the AppPart is present in that instance, and if so hide or delete the AppPart and save + checkin the modified page instance. And then later when a fix of the Add-In is delivered + tested, redo the same to make it visible again.
So what to do then, to avoid the embarishing user experience with the faulty Add-In? As often in these 'modern Apps' days, it is javascript to the rescue. Ultimately on browser level, every Add-In is included in it's own isolated iframe on the containing SharePoint page. Through javascript the iframe containing the faulty Add-In can runtime be located in the DOM, and if found on-the-fly have it's src modified to refer to a maintenance page instead. The javascript snippet can be inserted via a ScriptEditor added in the shared part of the SharePoint page, and easily removed again once the Add-In behavior is recovered.
Code snippet:
function displayMaintenanceMessage() { var addInIframe = document.querySelector('iframe[src*="<identifying part in Add-In start url>"]'); if (addInIframe != null) { addInIframe.src = '/StyleLibrary/snippets/MaintenanceMode.aspx'; } else { setTimeout(displayMaintenanceMessage, 1000); } } displayMaintenanceMessage();