Wednesday, September 17, 2014

Sitecore MVC Request Pipeline Execution Lifecycle

When I looked for description of Sitecore.MVC pipelines I faced with great post of David Morrison where was shown Sitecore MVC request pipeline execution lifecycle. I extended this diagram with Sitecore Analytics pipelines.

Sitecore.Mvc

When you’ll look at your showconfig.aspx you’ll find out that you have more Sitecore Mvc pipeline processors. Diagram does not include Sitecore Mvc pipelines for Experience Editor.

Tuesday, September 9, 2014

SQL Code Snippet for Getting Paths of Sitecore Items

This post is based on my answer of SO question regarding getting Sitecore items paths.

It is rare situation when you need to use recursive SQL queries, I decided to practice.

with Items1 (nm, id1, pathstr)
as (select Name, id, cast('/sitecore' as nvarchar(MAX))
   from Items
   where ParentID = '00000000-0000-0000-0000-000000000000'
union all
   select Items.Name, Items.id, Items1.pathstr +'/'+ Items.Name
   from Items
     inner join Items1 on Items1.id1 = Items.ParentID)
select id1 as ItemId, nm as Name, pathstr as [Path]
from Items1

This query allow you to get ids and paths for all items in your Sitecore master(web, core) database.

Thursday, September 4, 2014

AJAX Lazy Loading with Sitecore MVC

There was request to implement search with lazy loading for Sitecore MVC project. Unfortunately there is no such option in Sitecore out of the box. But I found great article how it could be done on Sitecore project with web forms. That idea works fine for web forms, but it does not work for MVC.

I modified processor proposed in article to work with MVC layouts.

var url = "http://myurl/myItem?UseAjax=true&PresentationId=BACBFEE1-C0BB-4D8E-BA1A-D9F50CCA5B7F";  

$.ajax({
type: 'GET',
url: url
}).done(function (data) {
//Your code
});

We will also need a MVC layout for the ajax queries (in the following code the MY_AJAX_LAYOUT_ID is the id of this layout) and this layout will only contain one placeholder (MY_AJAX_PLACEHOLDER in the code)

Now here is the custom layout resolver (look at the comments into the code):

namespace MyNamespace
{
using System;
using System.Linq;
using System.Web;
using Sitecore.Diagnostics;
using Sitecore.Mvc.Pipelines;
using Sitecore.Mvc.Pipelines.Response.GetPageRendering;
using Sitecore.Mvc.Pipelines.Response.GetRenderer;
using Sitecore.Mvc.Presentation;
using System.Web.Routing;

public class GetAjaxLayoutRendering : GetPageRenderingProcessor

{
public override void Process(GetPageRenderingArgs args)

{
bool useAjax;
//First of all let's check if we are in ajax mode or not if not don't continue
var result = bool.TryParse(HttpContext.Current.Request.Params["UseAjax"], out useAjax);
if (!result || !useAjax)
{
return;
}

//The second parameter we need to pass is the PresentationId if not present the query if not valid -> don't continue
var presentationId = HttpContext.Current.Request.Params["PresentationId"];
if (string.IsNullOrEmpty(presentationId))
{
return;
}

//If the current item is null return
if (Sitecore.Context.Item == null)
{
return;
}

//Let's resolve the rendering
try
{
//Get the list of rendering for the current item
var renderings = args.PageDefinition.Renderings;
//If found
if (renderings != null && renderings.Any())
{
//Get the first rendering corresponding to the requested one
var rendering = renderings.First(r => r.RenderingItem.ID.ToString().Equals(presentationId));

if (rendering != null)
{
args.PageDefinition.Renderings.Remove(rendering);
//Put this rendering into ajax layout
rendering.Placeholder = "MY_AJAX_PLACEHOLDER";

rendering.LayoutId = new Guid(JetstreamShops.Business.Constants.Ids.AjaxLayoutItemId);
var layout = renderings.First(x => x.RenderingType == "Layout");
if (layout != null)
{
args.PageDefinition.Renderings.Remove(layout);
for ( int i=0; i< args.PageDefinition.Renderings.Count; i++)
{
args.PageDefinition.Renderings[i].Placeholder =
args.PageDefinition.Renderings[i].Placeholder.Split('/').Last();
args.PageDefinition.Renderings[i].LayoutId =
new Guid(MY_AJAX_LAYOUT_ID);
}
layout.LayoutId = new Guid(MY_AJAX_LAYOUT_ID);
var getRedererArgs = new GetRendererArgs(new Rendering());
getRedererArgs.LayoutItem =
Sitecore.Context.Database.GetItem(MY_AJAX_LAYOUT_ID);

layout.Renderer = PipelineService.Get().RunPipeline<GetRendererArgs, Renderer>(PipelineNames.GetRenderer, getRedererArgs, a => a.Result);

args.PageDefinition.Renderings.Add(layout);
args.PageDefinition.Renderings.Add(rendering);
}
}
}
}
catch (Exception exception)
{
Log.Warn("Cannot render this!", exception, this);
}
}
}
}

Configuration will look in next way:
<mvc.getPageRendering>
<processor patch:after="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="MyNamespace.GetAjaxLayoutRendering, MyAssembly"/>
</mvc.getPageRendering>
Many thanks to Vangansewinkel Benjamin whose idea was modified.