Tuesday, May 19, 2015

Beware: BLOB cache may miss modifications via SPD

There are 2 alternative approaches to manually (not via a SharePoint solution) update content resources (.css, .js, page layouts, masterpage) that are provisioned in a SharePoint library. Via the browser: you can download the content resource/file, make the changes in the downloaded file, and upload, checkin and publish the modified file into the SharePoint library. Alternative approach is to open the SharePoint site in SharePoint Designer, open + edit the content resource direct from within SPD, and afterwards checkin + publish the modified file from SPD.
Although both approaches work to modify the content file administrated in a SharePoint library, the SPD alternative has a caveat. This week we noticed that the SharePoint BLOB cache may miss the modification trigger when the modification is done through SPD. One of our developers changed a CSS file via SPD, but when browsing the site did not see the effect of his modifications. I immediately had my suspicion towards the BLOB cache. To verify, I inspected the BLOB cache folder on the WFEs, and noticed that on all 3 of the WFEs the date of the file in cache was earlier as the published date of the file with modifications. All other files in the cache appeared up-to-date, so it was not a situation of complete BLOB cache corruption. Merely the file changed via SPD was outdated in the cache. Pragmatic resolution here is to delete the outdated file from the BLOB cache folder on all WFEs.

Wednesday, May 13, 2015

Takeaways from SharePoint YamJam

The SharePoint productteam augmented with some MVPs, hosted a SharePoint YamJam tonight. A.o. Bill Baer and Benjamin Niaulin were present to answer questions and share additional insights on what has been presented previous week at MS Ignite. Below I've summarized my main takeaways from the interesting YamJam.
[Bill Baer]
For NextGen Portal experiences such as Delve, Video, etc. we're investing in bringing them to our on-premises customers via hybrid scenarios as many take dependencies on technologies we cannot package on a DVD, I.e. WAMS, Office Graph, etc.
[Benjamin Niaulin]
You'll want to start using Groups for Office 365 as much as possible for Team Collaboration as a lot of things will tie in to it, including the new portal. Get familiar with Delve right away.
[Bill Baer]
SharePoint Designer will not be shipped with SharePoint 2016; however, SharePoint Designer 2013 can be used with SharePoint 2016.
[Bill Baer]
SharePoint social capabilities as designed and delivered in SharePoint 2013 will be carried forward into SharePoint 2016 in addition to new integrated Yammer experiences thru hybrid to include Post to Yammer, etc. from SharePoint Document Libraries.
[Bill Baer]
In SharePoint Server 2016 updates are orchestrated differently mitigating the offline upgraders as experienced in earlier versions of SharePoint, we're moving to a B2B online upgrade model that alleviates the need for post-patch experiences.
We'll share more details soon, at a high level patching remains thru MSPs (w/ significant reduction), but now with 0 downtime, removing dependencies between FE and BE components and now upgraders are all done online.
[Benjamin Niaulin]
Important: InfoPath Forms Services will be in SP2016 but not InfoPath client.
My experience, though it may change: When I installed Office 2016 it removed my InfoPath 2013 on my computer. May have been a bug or something still being worked on.
. . . That’s not a bug. It’s a feature (or rather a “known issue”). Microsoft explains it at bullet No. 4 here: Known issues for Office 2016 Preview
[Bill Baer]
We'll continue to invest in broader availability of endpoints to support search scenarios, particularly connectors, and deliver on the connectors we already ship. In addition we're shipping some new APIs to support surfacing external sources in Delve (as demonstrated at \\build) and have a number of partners in our search TAP program that are actively building connectors using some of our new search experiences and endpoints.
[Mike Holste]
Regarding question: how to decide when to use Yammer and SharePoint and Office 365 Groups; Check out this Channel9 recording of Ignite session for additional guidance on what to use and when: How to Decide When to Use SharePoint and Yammer and Office 365 Groups and Outlook and Skype
[Benjamin Niaulin]

I think it'll be hard to give as it'll vary based on each organizations needs. The SharePoint Team Site is still the "big" collaboration solution with many libraries, workflows, metadata, term store, etc... It's like the ECM.

Groups is more about Team Collaboration. It pulls things from different products and provides an easy to consume solution that works well from anywhere and on any device with the updates coming.

I have a Group for:

- Blogs

- A Specific project I am doing with others (O365 Guide)

- Sharegate Marketing (especially for Calendar and OneNote)

and anyone can create a new group and get started.

The Team Site is a little bigger and requires heavier thinking, Content Types, how to place everything so that it makes sense etc.

It'll come down to knowing SharePoint vs knowing O365 Experiences and compare to see which fits best for your customers individual needs.

The reality is the users in the organizations are already using other things all over the internet for free or low cost $ per user per month. Bypassing IT altogether.

Groups provides an alternative they can consume without having to go to IT to request a heavy duty Site that requires SharePoint Training (even though it's SharePoint behind it)

[Bill Baer]
On the development side, we'll continue to support FTC, invest more in hybrid apps via CAM, in addition to bringing much of the cloud experience to on-premises to draw parity between developing for the service and on-premises. Namely subscription apps, common consent, and Office 365 APIs as initial investment areas.

Sunday, May 3, 2015

Beware of script-dependencies with AMD loading

Asynchronous Module Definition is very useful to manage the loading of (larger) sets of libraries in the JavaScript runtime engine. Instead of explicit in own code take the responsibility for loading each needed javascript library one by one, it is more managable to delegate this to one of the AMD implementations. Require.js is by my knowledge most applied currently, but there are alternatives.
Our developers have implemented AMD loading in multiple of our custom build SharePoint Apps, utilizing Require.js. On inspecting (F12, Fiddler) the request/response traffic of our App-Model based intranet, we observed that sometimes a specific script file is requested twice, and the 2nd time from wrong url and not in minimized version. When I asked the App developer about it, he could not explain: from his code, he explicitly specified to load the minimized library. Also weird that the re-request does not occur always.
I decided to inspect runtime behavior plus the App code myself, and try to analyze (or rather, puzzle) what caused the behavior.

Code inspection

require(['../Scripts/AppHelpers'], function () { var spHostUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl')); var hostProtocol = spHostUrl.split("//")[0]; var hostRoot = spHostUrl.split("//")[1].split("/")[0]; spHostUrl = hostProtocol + "//" + hostRoot; require([spHostUrl + '/Style%20Library/Scripts/jquery-1.11.1.min.js'], function () { require([spHostUrl + '/_layouts/15/MicrosoftAjax.js', spHostUrl + '/_layouts/15/init.js', spHostUrl + '/_layouts/15/sp.runtime.js', spHostUrl + '/_layouts/15/sp.js', spHostUrl + '/_layouts/15/sp.requestexecutor.js', spHostUrl + '/_layouts/15/sp.core.js', spHostUrl + '/_layouts/15/sp.init.js', spHostUrl + '/_layouts/15/ScriptResx.ashx?culture=en%2Dus&name=SP%2ERes', spHostUrl + '/_layouts/15/sp.ui.dialog.js', "../Scripts/jquery.rotate.js", "../Scripts/moment.min.js", "../Scripts/moment-timezone.min.js", "../Scripts/ListController.js", "../Scripts/UserSettings.js", "../Scripts/sp.communica.js", "../Scripts/App.js"], function () { jQuery(document).ready(function () { initialize(function () { }); }); }); });
Basically, the above code instructs require.js to first load the library ‘/Scripts/AppHelpers.js’, once that is loaded to load jQuery library, and once that is loaded, load a bunch of other libraries that are a.o. dependent on jQuery. And when all libraries loaded, invoke a custom initialization function (not displayed here, as not relevant for the issue).

Runtime analysis, via Fiddler and F12

In Fiddler, often however not always, the following sequence of requests is visible.
So first ‘/Scripts/moment.min.js’ is succesful requested, followed by unsuccesful (HTTP 404) request for ‘/Pages/moment.js’. The initiator of the http-request is setting the ‘src’ property of a <Script> element. Likely this is initiated from require.js handling.
I also inspected the runtime DOM. Herein it becomes clear why the browser requests the library a second time. And also it is indeed inserted in the DOM by require.js handling, as visible via the require.js properties.

Explanation: asynchronous loading + library-dependency

In the above displayed App HTML code, you see that in 3rd require.js load handling, a set of libraries are requested for load on the same level. Crucial here is that:
RequireJS uses Asynchronous Module Loading (AMD) for loading files. Each dependent module will start loading through asynchronous requests in the given order. Even though the file order is considered, we cannot guarantee that the first file is loaded before the second file due to the asynchronous nature
In the App code, moment.min.js and moment-timezone.min.js are specified required at same level:
"../Scripts/moment.min.js", "../Scripts/moment-timezone.min.js",
But AMD thus does not guarantee that moment.min.js is loaded BEFORE moment-timezone.min.js; And as moment-timezone.min.js on its turns includes “define(["moment"],b)”, require.js resolves this to load the moment.js library in case not yet loaded. This explains why it does not always occur: sometimes moment.min.js is already loaded, sometimes not…

Solution

There are 2 alternative approaches to resolve the behavior. Essence of both is to make sure that moment.min.js is loaded before the dependent library moment-timezone.min.js:
  1. Extend on the above code-pattern of explicit separating the load of libraries: still retrieve moment.min.js in the 3rd level, and move the load of moment-timezone.min.js to a 4th level:
    require(['../Scripts/AppHelpers'], function () { var spHostUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl')); var hostProtocol = spHostUrl.split("//")[0]; var hostRoot = spHostUrl.split("//")[1].split("/")[0]; spHostUrl = hostProtocol + "//" + hostRoot; require([spHostUrl + '/Style%20Library/Scripts/jquery-1.11.1.min.js'], function () { require([spHostUrl + '/_layouts/15/MicrosoftAjax.js', spHostUrl + '/_layouts/15/init.js', spHostUrl + '/_layouts/15/sp.runtime.js', spHostUrl + '/_layouts/15/sp.js', spHostUrl + '/_layouts/15/sp.requestexecutor.js', spHostUrl + '/_layouts/15/sp.core.js', spHostUrl + '/_layouts/15/sp.init.js', spHostUrl + '/_layouts/15/ScriptResx.ashx?culture=en%2Dus&name=SP%2ERes', spHostUrl + '/_layouts/15/sp.ui.dialog.js', "../Scripts/jquery.rotate.js", "../Scripts/moment.min.js", function () { require(["../Scripts/moment-timezone.min.js", "../Scripts/ListController.js", "../Scripts/UserSettings.js", "../Scripts/sp.communica.js", "../Scripts/App.js"], function () { jQuery(document).ready(function () { initialize(function () { }); }); }); });
  2. Configure Require.js to be aware of the Module dependency
    requirejs.config({ shim: { 'moment-timezone.min': ['moment.min'] } });