another technical blog...technically

Monday, September 1, 2014

Device channel panels and web parts walktrough: the JS and content search query BUG

Let's continue from the first episode, haven't you read it? Check this link, download the code and load Example 04... Here we are and here is a smal recap from the last article...
A long time ago in a galaxy far, far away... A young SharePoint padawan was trying to use device channel panels to contitionally load web parts and app parts...
He discovers some unexpected behaviours and he found workarounds, and he was able to accomplish this task, but then he had to add javascript to the web parts and a new challenge begun...
/mode joke off
Why JS in web parts? Do you know content search query web part (we are dealing one of the new feature of SP2013) ? Well, it uses JS a lot. It means that, if you want to load content search query web part, choosing what to load according with the device channel, you are in a big trouble.

Problem repro steps
Let's use the DeviceChannelArticle_OOB page layouts i developed in Example 04 (i prefer the OOB workaround in order not to introduce unsupported code in this demo).
Let's simplify the problem and, starting from the Example 04, write a stupid alert JS script in every web part.
alert('This is the Internet Explorer web part script');
alert('This is the Mozilla Firefox web part script');
Now display the page (it's up to you to use Internet Exlorer or Mozilla Firefox).

What's happening? Even if you load the web part using device channel panels, you see alerts for both web parts... again, not a expected behaviour. The risk is to load extra data, extra scripts and extra stuffs... not so good.
Solutions
The first solution is to load JS detecting user agent from JS itself... yeah that's quite simple but... why use device channels? Rework web parts in order to use just server side code? It can be done, but you lose functionalities. I got an idea, why don't use a HttpModule to cut all the code of the wrong device channel panel? Since we are talking about a SP2013 recognized BUG (so it will be solved), we can think about HttpModule we can just plug'n'play until official fix will be realeased.

First, change page layout, switch to DeviceChannelArticle_JS, what's different from DeviceChannelArticle_OOB?

No more display/edit panel, just fields wrapped with HTML comment tags and no more device channel panels.

HttpModule will recognize user agent, understand what HTML code to cut according to tags above... yes i am reinventing the wheel.
Let's load the HttpModule using Example 05 feature to see the code in action, please be aware you need to run these powershell commands otherwise you have to edit web.config with your hands.
$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$contentService.RemoteAdministratorAccessDenied = $false
$contentService.Update()
You can see i created a abstract class named DeviceChannelPanelHttpModule in VLibs project i've realized with DeviceChannelArticleJSHttpModule in VLibs.Examples, in order to make the module aware about device channels, inclusion rules and wrapping tags.
Let's have a look to the astract class...
public abstract class DeviceChannelPanelHttpModule : IHttpModule
{
 #region Public methods
 public void Init(HttpApplication application)
 {
  application.PostRequestHandlerExecute += new EventHandler(ApplicationPostRequestHandlerExecute);
 }

 public void Dispose()
 {
 }
 #endregion 

 #region Protected methods
 protected void ApplicationPostRequestHandlerExecute(object sender, EventArgs e)
 {
  HttpContext httpContext = HttpContext.Current;

  if (httpContext.Response.ContentType == "text/html" && BrowserRequestNeedsToBeFiltered(httpContext.Request))
  {
   httpContext.Response.Filter = LoadFilter(httpContext);                
  }
 }

 protected abstract bool BrowserRequestNeedsToBeFiltered(HttpRequest httpRequest);

 protected abstract Stream LoadFilter(HttpContext httpContext);
 #endregion
}
  • Init: just attaches handler;
  • ApplicationPostRequestHandlerExecute: it decides if page has to be filtered, it calls BrowserRequestNeedsToBeFiltered to understand what to do, and if the request has to be filtered, instantiates the filter;
  • BrowserRequestNeedsToBeFiltered: abtract method, you implement this one in order to make the module aware of the filtering rules;
  • LoadFilter: abstract method, you implement this one to decide what filter to instantiate;
Follow the code example i developed and everything will become clear.
P.S.: May thanks to Sam Betts and Stefano Merotta, they helped me with this project of doom!

written in: Milano, Italia

0 commenti:

Post a Comment

Because of a lot of SPAM about courses, I need to moderate all comments here.
I ensure you that I will answer whenever possible (if you are not a spammer).

Me, myself and I

My Photo
I'm just another IT guy sharing his knowledge with all of you out there.
Wanna know more?