Get Media Item file size

I recently worked at a project where I need to return the file size of the selected media item. It is actually very simple, as Sitecore has encapsulated the media item in a Sitecore.Data.Items.MediaItem class. This class contains the default fields for a media item, hence also the file size in bytes as an integer.

This small function takes a media item id (I am calling the function from an XSL, where I have the media item id in hand) and returns the size of the item in megabytes.

using Sitecore;
using Sitecore.Data.Items;

public string GetMediaItemSizeMB(string mediaItemID)
{
  Item item = Context.Database.GetItem(mediaItemID);
  Diagnostics.Assert.IsNotNull(item, "Could not find item with id '" + mediaItemID + "'");
  MediaItem mediaItem = new MediaItem(item); 
  // You can always make another conversion
  double mb = (double)mediaItem.Size/1024/1024;
  // This format returns the mb with one digit.
  return mb.ToString("##.#");
}

Sitecore XslHelper GetItem()

I am using the XslHelper from some C# code. I use the GetItem() function to get an item from a NodePathIterator.
But the GetItem() generates an error “SystemInvalidOperationException: Enumeration has not started. Call MoveNext()“. The following code will fail with that error:

public static string GetDisplayName(System.Xml.XPath.XPathNodeIterator iterator)
{
  Sitecore.Xml.Xsl.XslHelper helper = new Sitecore.Xml.Xsl.XslHelper();
  Item item = helper.GetItem(iterator);
  if (item.DisplayName != String.Empty)
    return item.DisplayName;
  return item.Name;
}

The solution is to call f.ex. the fld() function before GetItem. This will work:

public static string GetDisplayName(System.Xml.XPath.XPathNodeIterator iterator)
{
  Sitecore.Xml.Xsl.XslHelper helper = new Sitecore.Xml.Xsl.XslHelper();
  // Call fld(".") to aviod the Enumeration has not started error.
  helper.fld(".", iterator);
  Item item = helper.GetItem(iterator);
  if (item.DisplayName != String.Empty)
    return item.DisplayName;
  return item.Name;
}

Sitecore support suggested another fix, which I haven’t tried yet:

public static string GetDisplayName(System.Xml.XPath.XPathNodeIterator iterator)
{
  Item item = null;
  if (iterator.MoveNext())
  {
    string ID = iterator.Current.GetAttribute("id", string.Empty);
    if (ID != string.Empty)
    {
      item = Context.Database.Items[ID];
      if (item.DisplayName == String.Empty)
        item.Name;
    }
  }
  return item.DisplayName;
}

Vertical scroll bar in Firefox

The vertical scroll bar in Firefox is a menace. Internet Explorer just grays it out when there is no need for scrolling. Firefox removes it, making the design “jump” from side to side when swiching pages. I have customers calling me, claiming there is an error with the design because it jumps from side to side.

Is there a solution for this? Yes. There are 2, a good and a better one.

You can add the Mozilla specific -moz-scrollbars-vertical to the HTML tag in your css:

html {
  overflow: -moz-scrollbars-vertical;
}

Unfortunately this eliminates the horizontal scroll bar so users can’t scroll horizontally if need be.
Another solution is to use the overflow-y on the HTML tag in the css:

html {
  overflow-y: scroll;
}

This solution has the disadvantage that the CSS will not validate, as the overflow-y does not exist i CSS 2 but only in CSS3.

There is a 3rd solution which is to modify your local C:\Program Files\Mozilla Firefox\res\html.css file with one of the 2 solutions above. This only works on your machine though.

Render XSLT’s from another Item

Rendering XSLT’s (or sublayouts for that matter) from another item can be very usefull. For example, if you have a spots region on your page, and each spot is rendered by different XSLT’s, you can place the XSLT rendering on each spot item (instead of placing them on the page itself) and the let a sublayout render the renderings for you.

First you need to get hold of the item containing the renderings to render. In this case I call the item “spot”.
Then you need to get the renderings from that item for a specific context. I use the current context.
At last you can iterate over each rendering and render them to the page.

To do all this you will need to override the CreateChildControls of the ascx rendering the renderings.
Also, you need a control to add the renderings to. I use a panel.

So, lets take it from the beginning. I create a .ascx page with a panel on it called panDynamicSpots:

<asp:Panel ID="panDynamicSpots" runat="server">
</asp:Panel>

Then I override the CreateChildControls of the .ascx to render my XSLT’s on the panDynamicSpots:

using Sitecore.Data.Items;
using Sitecore.Layouts;
using Sitecore.Web.UI.WebControls;

protected override void CreateChildControls()
{
  // Somewhere in my code I have got hold of the item
  // containing the renderings to render. The item is called "spot"
  Item spot = GetItemContainingSpotToRender(); // Fictive function
  // Then call the RenderSpot to render the XSLT's on the spot
  RenderXsltSpot(spot);
  base.CreateChildControls();
}

private void RenderXsltSpot(Item spot)
{
  // Loop through all renderings on the item
  foreach (RenderingReference rendering in spot.Visualization.GetRenderings(Sitecore.Context.Device, false))
  {
    // Get the path to the XSLT
    string path = rendering.RenderingItem.InnerItem["Path"];
    // Create an instance of a XslFile
    XslFile xslSpot = new XslFile();
    xslSpot.Path = path;
    xslSpot.DataSource = spot.Paths.FullPath;
    // Add the XSL to the panel so it can be rendered
    panDynamicSpots.Controls.Add(xslSpot);
  }
}

It is the function GetRenderings that returns me a list of all renderings on an item.

You could also extend the function to render sublayouts as well. You will need to find out which template the rendering is based on using the rendering.RenderingItem.InnerItem.TemplateName. Instead of instantiating a XslFile you would then instantiate a Sublayout().