another technical blog...technically

Tuesday, September 23, 2014

Restrict content types and related page layouts on list pages new button

This is one of the most recurrent tasks... dealing with pages list.
You created your pages list content types, associated page layouts to those content types, and customers want to use only those content type and page layouts or simply.
Ok, let's assume you have Example 04 deployed (if  it's your first time on this blog, download VLibs here), this is what you see in Pages list.

The key to accomplish this task is divide-et-impera, so what about the new page menu?
Work on this property
IList<SPContentType> contentTypeInstances2Show = ...
pagesList.RootFolder.UniqueContentTypeOrder = contentTypeInstances2Show;
pagesList.RootFolder.Update();
Assigning a  SPContentType list to UniqueContentTypeOrder, we give the order of the content type available for creating, and we can also exclude some of them from the menu.

The second step is to make a selection of page layouts available, the method is:
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web); 
PageLayout[] pageLayouts = publishingWeb.GetAvailablePageLayouts();

PageLayout[] pageLayouts2Show = ...
publishingWeb.SetAvailablePageLayouts(pageLayouts2Show .ToArray(), false);
So you have first to select content type related page layouts, just querying the publish website.
And this is what we will get
New document menu

Available page layouts

If you need the code, as usual this is the link. Go get it! and have a look at Example 06 ;) .

Monday, September 8, 2014

OnTaskCreated and DelayActivity epic fail: a inconsistent farm story

Some weeks ago a SP2010 workflow was driving me mad. I got a parallel activity and i need to wait the OnTaskCreated event on every task i was creating.
Workflow started having some strange behaviours:
  • Workflow suspended states;
  • OnTaskCreated never firing on created tasks;
I was trying to figure out what to do, and i start thinking about a porkaround, why don't simulate tha bugged OnTaskCreated with a while and a DelayActivity?
A while condition checks if task id has a value, if not, wait 5 seconds and re-check until condition is met.
Why not? It's not so elegant but it works... don't laugh please, i almost save a workflow project with this workaround.
In Italy we say: "Misfortunes always come in twos"
Delay activity seemed not to work because it put the workflow to sleep... forever. This leads me to think there were problems on customer server... and so it was (http://support.microsoft.com/kb/2553031/en-us) , in other words: update to the most recent cumulative update and cross your fingers.
In my case, i continued to have issues, so i have to develop a parachute solution to make my PM happy. When we switched to pre-production environment everything works without parachutes.
What can i say? Two subsequential MS bugs in a row... Murphy's law rulez.
P.S.:
Who laughed reading this workaround?
Read this: http://support.microsoft.com/kb/970548
No more laughs ah?

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!

Me, myself and I

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