Create dynamic menu with page sections

Since Sitecore Habitat has been released I was looking for a Helix based solution which can be use for Greenfield project. Once again – Habitat is only an example of project implementing Helix architecture.  I looked at Helixbase and I tried to  implement one of bootstrap free template sites to evaluate it as a candidate.

agency

I thought it would be nice to add automatically to the menu new section item each time that editor is adding a new one and create an anchor to that section.

 

@model RenderingModel
@{
 var currentItem = Sitecore.Context.Item;
 RenderingReference[] myRenderings = currentItem.Visualization.GetRenderings(Sitecore.Context.Device, true);
 var layoutField = new LayoutField(currentItem);
 LayoutDefinition layoutDef = LayoutDefinition.Parse(layoutField.Value);
 DeviceDefinition deviceDef = layoutDef.GetDevice(Sitecore.Context.Device.ID.ToString());
 string sectionId = string.Empty;

}
<!-- Collect the nav links, forms, and other content for toggling -->

<!-- /.navbar-collapse -->

Dynamic Multi Layer Image using Sitecore Custom Handler

In a few Sitecore projects that I was working on, we needed an image based on unlimited combination of sub layers.

For example, for one of our clients, on every product page, we had an image with a map of the United States and Canada with highlighted regions where their products were available (see image below). To render this map and generate the image on the fly, we needed a mechanism. We couldn’t afford to have combinations for each 50 states, 10 provinces and 3 territories.

regional availability

Map of North America with Highlighted Regions

In order to display this image I had to identify the regions to be highlighted as a parameter.

<img src="/~/map/all,ca_quebec,ca_atlantic,ca_ontario,ca_western,us_west,us_north_west,us_south_west,us_central,us_north_east,South%20East.jpg" alt="">

Another example is a shower configuration tool. For the customers/end-users, our vision was to create an immersive, engaging and personalized user experience with the shower/tub shower configuration tool (see pictures below). Users can thus build their dream shower by using all available parts and preview it in real time while building their shopping list. Once again, think about all the variants involved to be combined (bases, tiles on the wall, and shower doors).

Here is a solution that I came with in order to ease the code and save time :

Register a new custom handler

1.       Add a new custom handler configuration into node

<customHandlers>
      <handler trigger="~/map/" handler="maax_map.ashx"/>
 </customHandlers>

2.  Add a new custom handler configuration into handlers node

<handlers>
.
.
.
   <add verb="*" path="maax_map.ashx" type="Maax.Classes.ImageHandler, MaaxWebsite" name="Maax.ImageHandler" />
.
.
.
 </handlers>

3.Add custom halnder into node

 <httpHandlers>
.
.
.
 <add verb="*" path="maax_map.ashx" type="Maax.Classes.ImageHandler, MaaxWebsite" />
.
.
.
</httpHandlers>

4. Code your own handler

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections;
using System.IO;
using System.Web.SessionState;
using System.Drawing;
using System.Drawing.Imaging;

using Sitecore.Data.Items;
using Sitecore.Data.Fields;

namespace Maax.Classes
{
    public class ImageHandler : IHttpHandler, IRequiresSessionState
    {
        private const string sessionMAP = "MAP";
        private string XLen = "110";
        private string YLen = "110";

        /// <summary>
        /// Get All Regions
        /// </summary>
        public List<Item> AllRegions
        {
            get
            {
                string query = "/sitecore/content/Meta-Data/Regional Contacts/Regions/*";
                return MaaxProductRepository.CurrentDatabase.SelectItems(query).ToList();
            }
        }

        #region IHttpHandler Members

        public bool IsReusable
        {
            get { return true; }
        }
        /// <summary>
        /// Respond to all request of /~/map/  This params is set in web.config file
        /// </summary>
        /// <param name="context"></param>
        public void ProcessRequest(HttpContext context)
        {
            //Hashtable used to store images created on the fly in session to be reuse later
            Hashtable ht;

            if (context.Request.FilePath != null)
            {
                //extract of the key from url t
                string key = context.Request.FilePath.Substring(7, (context.Request.FilePath.Length - 11));

                if (context.Session[sessionMAP] != null)
                {
                    //check if hashtable allready exist in session
                    ht = (Hashtable)context.Session[sessionMAP];
                    if (!ht.ContainsKey(key))
                    {
                        byte[] bitmapData = CreateImage(key);
                        ht.Add(key, bitmapData);
                    }
                }
                else//if not we have to create it
                {
                    ht = new Hashtable();
                    byte[] bitmapData = CreateImage(key);
                    ht.Add(key, bitmapData);
                    context.Session[sessionMAP] = ht;

                }

                Byte[] arrImg = (byte[])ht[key];
                if (arrImg != null)
                {
                    context.Response.Clear();
                    context.Response.ContentType = "image/jpeg";
                    context.Response.BinaryWrite(arrImg);
                    context.Response.End();
                }
            }
        }
        /// <summary>
        /// Create map image based on regions keyes
        /// </summary>
        /// <param name="key"></param>
        /// <returns>JPEG image in binairies array</returns>
        private byte[] CreateImage(string key)
        {
            try
            {

                string[] regions = key.Split(',');
                string background = regions[0];
                //verifying dimention of background image

                Item itm = AllRegions.First(s => s["Code"] == background);
                ImageField backgroundImageField = itm.Fields["ImageOverlay"];
                int width = int.Parse(backgroundImageField.Width);
                int hight = int.Parse(backgroundImageField.Height);

                using (Bitmap bit = new Bitmap(width, hight))
                {
                    Graphics g = Graphics.FromImage(bit);
                    // fill with background
                    g.Clear(Color.White);

                    foreach (string region in regions)
                    {
                        //extraction png images from region item
                        Item found = AllRegions.First(s => s["Code"] == region);
                        ImageField imageField = found.Fields["ImageOverlay"];
                        MediaItem mediaItem = imageField.MediaItem;
                        if (mediaItem != null)
                        {
                            Stream fStream = mediaItem.GetMediaStream();
                            if (fStream != null)
                            {
                                //drawing region image on background
                                Image img = Image.FromStream(fStream);
                                g.DrawImage(img, 0, 0, width, hight);

                            }
                        }
                    }

                    MemoryStream ms = new MemoryStream();
                    bit.Save(ms, ImageFormat.Jpeg);

                    byte[] bitmapData = ms.ToArray();
                    return bitmapData;
                }
            }
            catch
            {
                return null;
            }
        }

        #endregion
    }
}