HTML5 video with Sitecore


I worked with a client that was having trouble getting HTML5 video to play in Sitecore. Inside of our support tickets I found the trick to getting it to work is to add the following MIME Types to the App_Config/MIMETypes.config file.

  <mediaType extensions="ogg">
    <mimeType>video/ogg</mimeType>
  </mediaType>
  <mediaType extensions="m4v">
    <mimeType>video/mp4</mimeType>
  </mediaType>

After that I was able to play video with those two formats in IE9 and the latest versions of FF and Chrome. I have not tested on any mobile devices or older browsers.

With HTML5 you need to specify the source video for each codec you want to support because the browsers do not support the same codecs.

<video controls="controls"">
   <source src="~/media/Files/sample_h264.m4v" type="video/mp4" />
   <source src="~/media/Files/sample_ogg.ogg" type="video/ogg"/>
   <p>Your browser does not support the <code>video</code> element.</p>
 </video>
Advertisements

Multiple Sites with One Authoring Domain in Sitecore


Sitecore allows you to manage multiple sites by binding a hostname to a root content item via the webconfig. This works fine in the delivery enviornment but for authoring you would also now need a seperate entry for each site if your site is dependent on the Sitecore.Context.Site at all.

Adding the configs settings isn’t a huge deal but if you have 50 sites you now need 50 config settings for the authoring urls, 50 for the editing urls, 100 dns entries and 50 authoring bookmarks for your authors.

I came up with the following approach to resolving the site in page editor and preview modes to help alleviate some of this pain. I can now use one authoring domain and when I push the Page Editor or Preview buttons Sitecore will resolve the site for the item.

To achieve this I did four things on top of the normal setup :

  • Added SiteResolver to resolve site based on Item if sc_itemid is in the Query String and we are in Page Editor or Preview mode.
  • Override the LinkProvider to include the sc_site in the Query String when in Page Editor or Preview Mode.
  • Turn Off Rendering.SiteResolving
  • Add A Site Entry for the “Authroing” site with the root of the home node.

Normal Setup
Root Content Items

IIS Site Bindings

Added Sites to web.config (notice host names and start items)


      <site name="website" hostName="training65" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
      <site name="site2"  hostName="site2.training" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/Site2" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
      <site name="site3"  hostName="site3.training" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/site3" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />

With the setup above and I log into each domain and author the sites but I can’t author from one site and have the Site resolve correctly.
Authoring Multiple Sites from One Domain
First I add my new authoring site below my last site because I want the others to resolve first on the delivery side.

      <site name="website" hostName="training65" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
      <site name="site2"  hostName="site2.training" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/Site2" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
      <site name="site3"  hostName="site3.training" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/site3" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />
      <site name="authoring" hostName="" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="10MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="5MB" filteredItemsCacheSize="2MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" />

Now if I login to editing.training domain it resolves to this authoring site that doesn’t contain a domain name. I can log in and Edit all three of my sites but if the sites are relying on anything contained in the Sitecore.Context.Site this information will be pointing to the Authoring Site and it will be wrong.

I remembered how the cross site links resolve to the correct domain when Rendering.SiteResolving is set to true so I reflected on that code and came up with a class (mostly copy pasta’d….including the code with the Go To Label) to resolve the Site for an Item without looking at the host name.

This first class is the Link Helper Class and it’s ResolveTargetSite method is what we need.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Web;
using Sitecore.IO;
using Sitecore.Sites;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Prototypes.Links
{
    public static class LinkHelper
    {
        private static Dictionary<string, SiteInfo> _siteResolvingTable;
        private static List<SiteInfo> _sites;
        private static readonly object _syncRoot = new object();
        private delegate string KeyGetter(SiteInfo siteInfo);

        public static SiteInfo ResolveTargetSite(Item item)
        {
            SiteContext site = Context.Site;
            SiteContext context2 = site;
            SiteInfo info = (context2 != null) ? context2.SiteInfo : null;
            if ((context2 != null) && item.Paths.FullPath.StartsWith(context2.StartPath))
            {
                return info;
            }
            Dictionary<string, SiteInfo> siteResolvingTable = GetSiteResolvingTable();
            string key = item.Paths.FullPath.ToLowerInvariant();
        Label_00AE:
            if (siteResolvingTable.ContainsKey(key))
            {
                return siteResolvingTable[key];
            }
            int length = key.LastIndexOf("/");
            if (length > 1)
            {
                key = key.Substring(0, length);
                goto Label_00AE;
            }
            return info;
        }

        private static Dictionary<string, SiteInfo> GetSiteResolvingTable()
        {
            List<SiteInfo> sites = SiteContextFactory.Sites;
            if (!object.ReferenceEquals(_sites, sites))
            {
                lock (_syncRoot)
                {
                    if (!object.ReferenceEquals(_sites, sites))
                    {
                        _sites = sites;
                        _siteResolvingTable = null;
                    }
                }
            }
            if (_siteResolvingTable == null)
            {
                lock (_syncRoot)
                {
                    if (_siteResolvingTable == null)
                    {
                        _siteResolvingTable = BuildSiteResolvingTable(_sites);
                    }
                }
            }
            return _siteResolvingTable;
        }

        private static Dictionary<string, SiteInfo> BuildSiteResolvingTable(IEnumerable<SiteInfo> sites)
        {
            Dictionary<string, SiteInfo> dictionary = new Dictionary<string, SiteInfo>();
            List<string> list = new List<string>();
            KeyGetter[] getterArray = new KeyGetter[] { info => FileUtil.MakePath(info.RootPath, info.StartItem).ToLowerInvariant() };
            foreach (KeyGetter getter in getterArray)
            {
                foreach (SiteInfo info in sites)
                {
                    if (!string.IsNullOrEmpty(info.HostName) && string.IsNullOrEmpty(GetTargetHostName(info)))
                    {
                        Log.Warn(string.Format("LinkBuilder. Site '{0}' should have defined 'targethostname' property in order to take participation in site resolving process.", info.Name), typeof(LinkProvider.LinkBuilder));
                    }
                    else if ((!string.IsNullOrEmpty(GetTargetHostName(info)) && !string.IsNullOrEmpty(info.RootPath)) && !string.IsNullOrEmpty(info.StartItem))
                    {
                        string key = getter(info);
                        if (!list.Exists(new Predicate<string>(key.StartsWith)))
                        {
                            dictionary.Add(key, info);
                            list.Add(key);
                        }
                    }
                }
            }
            return dictionary;
        }

        private static string GetTargetHostName(SiteInfo siteInfo)
        {
            if (!string.IsNullOrEmpty(siteInfo.TargetHostName))
            {
                return siteInfo.TargetHostName;
            }
            string hostName = siteInfo.HostName;
            if (hostName.IndexOfAny(new char[] { '*', '|' }) < 0)
            {
                return hostName;
            }
            return string.Empty;
        }

    }
}

Next I create a custom SiteResolver and place it after the normal SiteResolver in the begin request pipeline.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Web;
using Sitecore.Sites;

namespace Sitecore.Prototypes.Links
{
    public class SiteResolver : Sitecore.Pipelines.HttpRequest.SiteResolver
    {

        public override void Process(Pipelines.HttpRequest.HttpRequestArgs args)
        {
            if ((Sitecore.Context.PageMode.IsPreview || Sitecore.Context.PageMode.IsPageEditor) 
                && HttpContext.Current.Request.QueryString["sc_itemid"] != null)
            {
                Database db = Sitecore.Context.Database;
                if (db != null)
                {
                    Item item = db.SelectSingleItem(HttpContext.Current.Request.QueryString["sc_itemid"]);
                    if (item != null)
                    {
                        SiteInfo site = LinkHelper.ResolveTargetSite(item);
                        SiteContext siteContext = SiteContextFactory.GetSiteContext(site.Name);
                        UpdatePaths(args, siteContext);
                        Sitecore.Context.Site = siteContext;
                    }
                }
            }          
        }
    }
}

Add that to the httpRequestBeing pipeline right after the normal site resolver

        <processor type="Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel" />
        <processor type="Sitecore.Prototypes.Links.SiteResolver, Sitecore.Prototypes.Links"/>

Now when I hit the Page Editor or Preview buttons the site is displayed using the correct Sitecore.Context.Site, but if I click on a link to another page it loses the context….

To fix this I added a custom link provider that appends the sc_site to the query string if we are in preview or pageditor mode.

Here is the class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Sites;

namespace Sitecore.Prototypes.Links
{
    public class LinkProvider : Sitecore.Links.LinkProvider
    {

        public override string GetItemUrl(Data.Items.Item item, Sitecore.Links.UrlOptions options)
        {
            string url = base.GetItemUrl(item, options);
            if (Sitecore.Context.PageMode.IsPageEditor || Sitecore.Context.PageMode.IsPreview)
            {
                url = AppendOption(url, "&sc_site=" + LinkHelper.ResolveTargetSite(item).Name);
            }
            return url;
        }

        public override string GetDynamicUrl(Data.Items.Item item, Sitecore.Links.LinkUrlOptions options)
        {
            string url  = base.GetDynamicUrl(item, options);
            if (Sitecore.Context.PageMode.IsPageEditor || Sitecore.Context.PageMode.IsPreview)
            {
                url = AppendOption(url, "&sc_site=" + LinkHelper.ResolveTargetSite(item).Name);
            }
            return url;
        }

        public string AppendOption(string url, string option)
        {
            return url.Contains("?") ? url + option : url + "?" + option;
        }
    }
}

And here is where I overrode it in the config section

		<linkManager defaultProvider="authoring">
			<providers>
				<clear />
				<add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" addAspxExtension="false" alwaysIncludeServerUrl="true" encodeNames="true" languageEmbedding="asNeeded" languageLocation="filePath" shortenUrls="false" useDisplayName="false" />
        <add name="authoring" type="Sitecore.Prototypes.Links.LinkProvider, Sitecore.Prototypes.Links" addAspxExtension="false" alwaysIncludeServerUrl="true" encodeNames="true" languageEmbedding="asNeeded" languageLocation="filePath" shortenUrls="false" useDisplayName="false"  />
			</providers>
		</linkManager>

Now my last problem is that because Rendering.SiteResolving is set to true so my domain name is switching so we need to set that to false in the Authoring Environment while we probably want it true on the delivery side.

       <setting name="Rendering.SiteResolving" value="false" />

Notes

  • I wouldn’t expect this to work with the Shared Source DB Site Resolver and I’m not sure about the other Shared Source Multi-Site piece.
  • Posting code in wordpress is painfull.
  • So is copying from it so I’ll try to get a download up

Get Random Item from XSLT Rendering


Had a request to write an XSLT Extensions that can select a random child of an Item.  I wrote this extension that takes in a Sitecore Query and randomly selects one of the times. You could use it to do something like randomly display a Teaser from a Teaser Folder.

After compiling the code you will have to register the XSLT extension in your web.config file and add the namespace to the top of your XSLT Transformation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Xml.Xsl;
using Sitecore.Data.Items;
using System.Xml.XPath;
using Sitecore.Configuration;
using Sitecore.Xml.XPath;

namespace Sitecore.Prototypes.Client.Xslt
{
    public class Extensions : XslHelper
    {
        private static readonly XPathNavigator emptyNavigator;
        private static Random randomInstance = new Random();

        public XPathNodeIterator GetRandomResult(string query)
        {
            Item[] items = Sitecore.Context.Database.SelectItems(query);
            if (items != null)
            {
                int skip = randomInstance.Next(items.Count());
                try
                {
                    Item item = items[skip];
                    if (item == null)
                    {
                        return CreateEmptyIterator();
                    }
                    ItemNavigator navigator = Factory.CreateItemNavigator(item);
                    if (navigator == null)
                    {
                        return CreateEmptyIterator();
                    }
                    return navigator.Select(".");

                }
                catch (Exception e)
                {
                    Sitecore.Diagnostics.Log.Fatal(e.Message, this);
                }
            }
            return null;
        }
        private XPathNodeIterator CreateEmptyIterator()
        {
            return emptyNavigator.Select("*");

        }
    }
}

Forms Posting to wrong URL in Sitecore 6.4.1 update 4


If you are running on IIS 6 or IIS with mode set to Classic forms will post to their layouts url instead of the item url.

To fix this you need to add the following module to the top of the system.web/httpmodules section of the web.config.

 

<add type=”Sitecore.Web.RewriteModule,
Sitecore.Kernel” name=”SitecoreRewriteModule”/>
If you are running IIS 7 in Integrated mode you mostly likely missed this upgrade step and need to add it to the top of the system.webserver/module sections.

Adding an Item Informaton button to the Sitecore Page Editor


I had a request to see Item information from the Page Editor without going into the split screen edit item window so I created an Item Information button and corresponding page.

It currently display the following

  • Item ID
  • Path
  • Latest Version
  • Current Workflow
  • Workflow State
  • References
  • Referrers
  • Published State in all Target Databases

One thing to note is it’s for Content Authors and not Dev’s so it’s does’t report on References or Referrers unless there path is one of a Content Item or Media Item.

Here’s the Code for the Command

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Shell.Applications.WebEdit.Commands;
using Sitecore.Web.UI.Sheer;
using Sitecore.Data;
using Sitecore.Data.Items;

namespace Sitecore.Prototypes.PageEditor.WebEdit.Commands
{
public class Info : WebEditCommand
{
public override void Execute(Shell.Framework.Commands.CommandContext context)
{
Item item = context.Items[0];

SheerResponse.ShowModalDialog("/MyApplications/PageEditorInfo/Info.aspx?ItemId=" + item.ID.ToString(), "640px", "480px", string.Empty, false);
return;
}
}
}

Here’s the Code for the Page located in /MyApplications/PageEditorInfo/Info.aspx (yeah lots of literals and inlines CSS)


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Info.aspx.cs" Inherits="Sitecore.Starterkit.MyApplications.PageEditorInfo.Info" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><asp:Literal runat="server" ID="litItemTitle" /></title>
<link rel="stylesheet" href="/sitecore/shell/themes/standard/default/Default.css"/>
<link rel="stylesheet" href="/sitecore/shell/themes/standard/default/Dialogs.css"/>
</head>
<body style="background: none repeat scroll 0% 0% rgb(233, 233, 233);" scroll="no">

<form id="form1" runat="server">
<div style="background-color: White; border-bottom: 1px solid black;padding: 6px;">
<div style="color: black; padding: 0px 0px 4px; font: bold 9pt tahoma;">Item Information for : <asp:Literal runat="server" ID="itemDisplayName" /></div>
<div style="color: rgb(51, 51, 51);">Displays Item Information from the Page Editor</div>
</div>
<div style="padding:6px;">
<table>
<tbody>
<tr>
<td align="right">Id : </td><td><asp:Literal runat="server" ID="itemId" /></td>
</tr>
<tr>
<td align="right">Path : </td><td><asp:Literal runat="server" ID="itemPath" /></td>
</tr>
<tr>
<td align="right">Latest Version : </td><td><asp:Literal runat="server" ID="itemVersion" /></td>
</tr>
<tr>
<td align="right">Workflow : </td><td><asp:Literal runat="server" ID="itemWorkflow" /></td>
</tr>
<tr>
<td align="right">Worfklow State : </td><td><asp:Literal runat="server" ID="itemWorkflowState" /></td>
</tr>
<tr>
<td align="right" valign="top">Referrers : </td><td><asp:Literal runat="server" ID="itemReferrers" /></td>
</tr>
<tr>
<td align="right" valign="top">References : </td><td><asp:Literal runat="server" ID="itemReferences" /></td>
</tr>
<tr>
<td align="right" valign="top">Publishing Information : </td><td><asp:Literal runat="server" ID="itemPublishingInformation" /></td>
</tr>
</tbody>
</table>
</div>
</form>
</body>
</html>

and the code behind


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Workflows;
using Sitecore.Data;

namespace Sitecore.Starterkit.MyApplications.PageEditorInfo
{
public partial class Info : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Assert.IsNotNullOrEmpty(Request.QueryString["ItemId"],"ItemId must not be null");

Item item = Sitecore.Context.Database.SelectSingleItem(Request.QueryString["ItemId"]);

Assert.IsNotNull(item,"Item must not be null");
Assert.IsTrue(item.Access.CanRead(), "Read Access Denied");

itemId.Text = item.ID.ToString();
itemPath.Text = item.Paths.FullPath;
litItemTitle.Text = item.DisplayName;
itemDisplayName.Text = item.DisplayName;
itemVersion.Text = item.Versions.GetLatestVersion().Version.Number.ToString();

IWorkflow workflow = Sitecore.Context.Database.WorkflowProvider.GetWorkflow(item);
if (workflow != null)
{
WorkflowState state = workflow.GetState(item);
itemWorkflow.Text = workflow.Appearance.DisplayName;
itemWorkflowState.Text = state.DisplayName;

}
var refrences = Globals.LinkDatabase.GetReferences(item);
foreach (var link in refrences)
{
var linkItem = link.GetTargetItem();
if (linkItem != null && (linkItem.Paths.IsContentItem || linkItem.Paths.IsMediaItem))
itemReferences.Text += linkItem.Paths.FullPath + "<br/>";
}
var referrers = Globals.LinkDatabase.GetReferrers(item);
foreach (var link in referrers)
{
var linkItem = link.GetSourceItem();
if (linkItem != null && (linkItem.Paths.IsContentItem || linkItem.Paths.IsMediaItem))
itemReferrers.Text += linkItem.Paths.FullPath + "<br/>";
}

ShowPublishingInfo(item);
}
void ShowPublishingInfo(Item currentItem)
{
var publishingTargets = Sitecore.Context.Database.SelectSingleItem("/sitecore/system/Publishing targets").Children;

foreach (Item publishingTarget in publishingTargets)
{
string dbName = publishingTarget.Fields["Target database"].GetValue(true);
if (!string.IsNullOrEmpty(dbName))
{
var database = Sitecore.Configuration.Factory.GetDatabase(dbName);

Item item = database.SelectSingleItem(currentItem.ID.ToString());

string htmlOutput = string.Empty;
if (item != null)
{

foreach (var language in item.Languages)
{
Item languageVersion = item.Versions.GetLatestVersion(language);
if (languageVersion != null && languageVersion.Versions.Count > 0)
{
htmlOutput += string.Format("<div>{0} - {1}</div>", languageVersion.Version.Number.ToString(), languageVersion.Language.GetDisplayName());
}
}

}
if (htmlOutput == string.Empty)
{
htmlOutput = "This item is not currently published to " + publishingTarget.DisplayName + ".";
}

htmlOutput = string.Format("<div><div style='font-weight:bold'>Published to {1}</div>{0}</div>", htmlOutput,publishingTarget.DisplayName);

itemPublishingInformation.Text += (htmlOutput);
}
}
}
}
}

You have to add the command to the commands.config file under the app_config folder

<command name=”webedit:info” type=”Sitecore.Prototypes.PageEditor.WebEdit.Commands.Info,Sitecore.Prototypes.PageEditor”/>

Add add the Item to the Core Database

Path : /sitecore/content/Applications/WebEdit/Common Field Buttons/Info

Template : /sitecore/templates/System/WebEdit/WebEdit Button

Header : Item Info

Click : chrome:common:edititem({command:”webedit:info”})

Type : common

Icon : Applications/16×16/information.png

I’ll try to get in a nice downloadable zip format.

Update : I fixed some bugs in the code and created a Sitecore Package available here

Displaying Item Name in Page Editor via Chrome Data


When working with the Sitecore Page Editor there isn’t anything telling you what Item the Field you are editing belongs to. A content author pointed this out to me so I thought it would be nice to come up with a solution.

I found it was quite easy to add this functionality along with the number of items referencing the Item by manipulating display name that is set in the GetChromeData Pipeline.

Here’s the code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.GetChromeData;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;

namespace Sitecore.Prototypes.PageEditor.Pipelines.ChromeData
{
class FieldName : GetChromeDataProcessor
{
// Fields
public const string ChromeType = "field";
public const string FieldKey = "field";

public override void Process(GetChromeDataArgs args)
{
Assert.ArgumentNotNull(args, "args");
Assert.IsNotNull(args.ChromeData, "Chrome Data");
if ("field".Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase))
{
Field argument = args.CustomData["field"] as Field;
string format = "{0}";
Item item = args.Item;
if (item != null)
{
format = item.DisplayName + " ({0})";
var referenceCount = Globals.LinkDatabase.GetReferrers(item).Count();
if (referenceCount > 0)
{
format += " " + referenceCount.ToString() + " References";
}
}

args.ChromeData.DisplayName = string.Format(format,argument.DisplayName);
if (!string.IsNullOrEmpty(argument.ToolTip))
{
args.ChromeData.ExpandedDisplayName = string.Format(format,argument.ToolTip);
}
}
}

}
}

To configure it add the processor to the pipeline

<getChromeData>
<processor type=”Sitecore.Pipelines.GetChromeData.Setup, Sitecore.Kernel”/>
<processor type=”Sitecore.Pipelines.GetChromeData.GetFieldChromeData, Sitecore.Kernel”/>
<processor type=”Sitecore.Prototypes.PageEditor.Pipelines.ChromeData.FieldName,Sitecore.Prototypes.PageEditor”/>
<processor type=”Sitecore.Pipelines.GetChromeData.GetWordFieldChromeData, Sitecore.Kernel”/>
<processor type=”Sitecore.Pipelines.GetChromeData.GetRenderingChromeData, Sitecore.Kernel”/>
<processor type=”Sitecore.Pipelines.GetChromeData.GetEditFrameChromeData, Sitecore.Kernel”/>
<processor type=”Sitecore.Pipelines.GetChromeData.GetPlaceholderChromeData, Sitecore.Kernel”/>
</getChromeData>

Also consider adding this customization by John West and Alex Shyba that fixes an issue with Sitecore not including Datasource References in the Link Database.

http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2011/07/Add-Presentation-Component-Data-Sources-to-the-Links-Database-in-the-Sitecore-ASPNET-CMS.aspx

Sitecore Ribbon that displays published state of an item


To see if an item is published in Sitecore I would normally have to switch to the ‘web’ database and browse to the item. I created a ribbon panel that shows the ‘Status’ of an item in the ‘web’ database from the ‘master’ database.

View of Published Status

It displays the version number published for each language or tells you the item has not been published.

Here’s the code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Shell.Web.UI.WebControls;
using Sitecore.Diagnostics;
using Sitecore.Data.Items;
using Sitecore.Data;

namespace Sitecore.Prototypes.WebControls.ContentEditor.Panels
{
     public class PublishingInfo : RibbonPanel
    {
         public override void Render(System.Web.UI.HtmlTextWriter output, Web.UI.WebControls.Ribbons.Ribbon ribbon, Data.Items.Item button, Shell.Framework.Commands.CommandContext context)
         {
           Assert.ArgumentNotNull(output, "output");
           Assert.ArgumentNotNull(ribbon, "ribbon");
           Assert.ArgumentNotNull(button, "button");
           Assert.ArgumentNotNull(context, "context");
           if (context.Items.Length >= 1)
           {
               Item contextItem = context.Items[0];
               Database database = Sitecore.Configuration.Factory.GetDatabase("web");

               Item item = database.SelectSingleItem(contextItem.ID.ToString());
               
               string htmlOutput = string.Empty;
               if (item != null)
               {
                   
                   foreach (var language in item.Languages)
                   {
                       Item languageVersion = item.Versions.GetLatestVersion(language);
                       if (languageVersion != null && languageVersion.Versions.Count > 0)
                        {
                            htmlOutput += string.Format("<div>{0} - {1}</div>", languageVersion.Version.Number.ToString(), languageVersion.Language.GetDisplayName());
                        }
                   }
                   
               }
               if (htmlOutput == string.Empty)
               {
                   htmlOutput = "This item is not currently published.";
               }

               htmlOutput = string.Format("<div class='scRibbonToolbarText' style='overflow-y:scroll;padding-right: 20px;font-size:80%;'><div style='font-weight:bold'>Published to Web</div>{0}</div>", htmlOutput);

               output.Write(htmlOutput);
           }

           
         }
    }
}

To configure it :
1. Switch to Core database
2. Browse to /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Publish
3. Insert a new /sitecore/templates/System/Ribbon/Panel
4. See the type according to your class name and assembley
sample : Sitecore.Prototypes.WebControls.ContentEditor.Panels.PublishingInfo, Sitecore.Prototypes.WebControls