Enjoy!
This blog is a collection of challenges that I had a chance to work through in Sitecore.
Thursday, March 19, 2009
YouTube Integration source code
For those who's interested, the source code for YouTube data provider is available on Sitecore Trac system.
Friday, March 6, 2009
YouTube Integration with Sitecore 6.0
Recently I got a quite interesting task to build an integration between Sitecore CMS and YouTube repository. I finished that task and I'm willing to share my experience with public. I will describe code only partially. Only those parts I consider the most interesting.
We've been thinking about different approaches and have chosen Sitecore Data Provider approach. We decided to have items in CMS that would represent YouTube videos. One of the challenges was to integrate YouTube videos on the fly when an editor wants to add videos from one or other YouTube author.
This data provider works in read-only mode. If you want to extend this solution, you're welcome to do so.
So let's begin....
Templates
I created two templates that will represent root item for YouTube videos and YouTube video item itself. First one is called "YouTube branch" and the other one is "YouTube video".
YouTube branch is going to be a folder for YouTube video items. Unlike usual folders it has a field that determinates videos of what YouTube author to show beneath it.
YouTube video template extends Flash media template and adds two additional fields: Url and Preview. Url is obviously intended for a YouTube video url. Preview is an iframe field that will show us the preview of YouTube video.
Implementation
There is a required list of methods you must implement to get data provider working. Here are those methods:
- GetItemDefinition
- GetItemFields
- GetChildIDs
- GetParentID
- GetItemVersions
I'm not going to describe what they are for. You can find a detailed explanation on SDN.
One method is missing in this list. It's "GetTemplates" method. We can avoid implementing this method if required templates already exist in our data source.
I'm going to use master database as a source for YouTube video items. I created a couple of templates for this approach so that we can skip implementation of the "GetTemplates" method.
Also I made this data provider publish its items by implementing one publishing method:
- GetPublishQueue
This is how constructor looks for the data provider:
public YouTubeDataProvider(string rootTID, string resourceTID, string videoOwnerField, string contentDB)
{
prefix = ToString();
rootTemplateID = rootTID;
resourceTemplateID = resourceTID;
contentDatabase = contentDB;
videoOwnerFieldName = videoOwnerField;
_items = new Hashtable();
}
rootTemplateID contains an ID of YouTube branch template
resourceTemplateID contains an ID of YouTube video template
contentDatabase is a source database for our YouTube provider (it's master database)
videoOwnerFieldName is a field name of YouTube branch template where you specify an owner of YouTube videos.
_items is a temporary cache for our YouTube videos.
Since we need to use some sort of mapping between YouTube videos and Sitecore items, I'm going to use existing functionality of IDTable. That's what we need "prefix" variable for.
Now let's look at implementation of first method:
public override ItemDefinition GetItemDefinition(ID itemId, CallContext context)
{
ItemDefinition itemDef = null;
string itemName = string.Empty;
if (CanProcessYouTubeItem(itemId, context))
{
// method body
}
return itemDef;
}
The first thing we need to think of is how to determine if the item we are getting from data provider is our YouTube item. That's what CanProcessYouTubeItem method for. This method uses IDTable API to find out if an item comes from YouTube rather than SQL database. Here is the code for the method:
bool CanProcessYouTubeItem(ID id, CallContext context)
{
if (IDTable.GetKeys(prefix, id).Length > 0)
{
return true;
}
return false;
}
The same technic is used for GetItemFields, GetParentID and GetItemVersions methods. For GetChildIDs we had to use different code because we had to work with parent item. Here is a code for the method:
public override Sitecore.Collections.IDList GetChildIDs(ItemDefinition itemDefinition, CallContext context)
{
if (CanProcessParent(itemDefinition.ID, context))
{
// method body
}
return null;
}
CanProcessParent method checks if the item is our YouTube branch item. If it indicates that it is then we start processing our child items that are YouTube videos.
This is the code of the method:
bool CanProcessParent(ID id, CallContext context)
{
Item item = ContentDB.Items[id];
bool canProduce = false;
if (item != null && (ID.Parse(rootTemplateID) == item.TemplateID))
{
canProduce = true;
}
return canProduce;
}
After we get our items, we have to fill out their fields to finish our integration process. Information from YouTube video is mapped in GetItemFields method. This is the code:
public override FieldList GetItemFields(ItemDefinition itemDefinition, VersionUri versionUri, CallContext context)
{
FieldList fields = new FieldList();
if (CanProcessYouTubeItem(itemDefinition.ID, context))
{
string originalID = GetOriginalRecordID(itemDefinition.ID);
TemplateItem templateItem = ContentDB.Templates[resourceTemplateID];
if (templateItem != null)
{
YTItemInfo ytItemInfo = GetYTItemInfo(itemDefinition.ID);
if (ytItemInfo != null)
{
foreach (var field in templateItem.Fields)
{
fields.Add(field.ID, GetFieldValue(field, ytItemInfo));
}
}
}
}
return fields;
}
As you can see it's simply iterates through template fields and maps data from YouTube Entry to Sitecore YouTube video item.
We got YouTube video items in our content tree. But we cannot do anything with them on front-end. One option is to add our data provider to web database to get it working on front-end. But what if we make main data provider know about our items and just publish them as a real items. All I need to do is to get list of IDs of our items in GetPublishQueue method.
This is how the code looks:
public override IDList GetPublishQueue(DateTime from, DateTime to, CallContext context)
{
IDList list = new IDList();
foreach (var item in _items)
{
YTItemInfo ytItemInfo = (YTItemInfo) item;
list.Add(ytItemInfo.ItemID);
}
return list;
}
Since I did not use a publish queue table, YouTube items are going to be published every time to run publish operation on them. You can avoid this if you implement AddToPublishQueue and CleanupPublishQueue methods.
Here is a screen dump the way YouTube item looks in Sitecore media library:

This is a sitecore package that will install data provider on your Sitecore 6.0 CMS. I do not described provider configuration in this article. You can find it in /App_Config/Include folder after installing the package.
NOTE: if installation fails, that means that you run into a known issue. Reinstalling the package should solve it.
We've been thinking about different approaches and have chosen Sitecore Data Provider approach. We decided to have items in CMS that would represent YouTube videos. One of the challenges was to integrate YouTube videos on the fly when an editor wants to add videos from one or other YouTube author.
This data provider works in read-only mode. If you want to extend this solution, you're welcome to do so.
So let's begin....
Templates
I created two templates that will represent root item for YouTube videos and YouTube video item itself. First one is called "YouTube branch" and the other one is "YouTube video".
YouTube branch is going to be a folder for YouTube video items. Unlike usual folders it has a field that determinates videos of what YouTube author to show beneath it.
YouTube video template extends Flash media template and adds two additional fields: Url and Preview. Url is obviously intended for a YouTube video url. Preview is an iframe field that will show us the preview of YouTube video.
Implementation
There is a required list of methods you must implement to get data provider working. Here are those methods:
- GetItemDefinition
- GetItemFields
- GetChildIDs
- GetParentID
- GetItemVersions
I'm not going to describe what they are for. You can find a detailed explanation on SDN.
One method is missing in this list. It's "GetTemplates" method. We can avoid implementing this method if required templates already exist in our data source.
I'm going to use master database as a source for YouTube video items. I created a couple of templates for this approach so that we can skip implementation of the "GetTemplates" method.
Also I made this data provider publish its items by implementing one publishing method:
- GetPublishQueue
This is how constructor looks for the data provider:
public YouTubeDataProvider(string rootTID, string resourceTID, string videoOwnerField, string contentDB)
{
prefix = ToString();
rootTemplateID = rootTID;
resourceTemplateID = resourceTID;
contentDatabase = contentDB;
videoOwnerFieldName = videoOwnerField;
_items = new Hashtable();
}
rootTemplateID contains an ID of YouTube branch template
resourceTemplateID contains an ID of YouTube video template
contentDatabase is a source database for our YouTube provider (it's master database)
videoOwnerFieldName is a field name of YouTube branch template where you specify an owner of YouTube videos.
_items is a temporary cache for our YouTube videos.
Since we need to use some sort of mapping between YouTube videos and Sitecore items, I'm going to use existing functionality of IDTable. That's what we need "prefix" variable for.
Now let's look at implementation of first method:
public override ItemDefinition GetItemDefinition(ID itemId, CallContext context)
{
ItemDefinition itemDef = null;
string itemName = string.Empty;
if (CanProcessYouTubeItem(itemId, context))
{
// method body
}
return itemDef;
}
The first thing we need to think of is how to determine if the item we are getting from data provider is our YouTube item. That's what CanProcessYouTubeItem method for. This method uses IDTable API to find out if an item comes from YouTube rather than SQL database. Here is the code for the method:
bool CanProcessYouTubeItem(ID id, CallContext context)
{
if (IDTable.GetKeys(prefix, id).Length > 0)
{
return true;
}
return false;
}
The same technic is used for GetItemFields, GetParentID and GetItemVersions methods. For GetChildIDs we had to use different code because we had to work with parent item. Here is a code for the method:
public override Sitecore.Collections.IDList GetChildIDs(ItemDefinition itemDefinition, CallContext context)
{
if (CanProcessParent(itemDefinition.ID, context))
{
// method body
}
return null;
}
CanProcessParent method checks if the item is our YouTube branch item. If it indicates that it is then we start processing our child items that are YouTube videos.
This is the code of the method:
bool CanProcessParent(ID id, CallContext context)
{
Item item = ContentDB.Items[id];
bool canProduce = false;
if (item != null && (ID.Parse(rootTemplateID) == item.TemplateID))
{
canProduce = true;
}
return canProduce;
}
After we get our items, we have to fill out their fields to finish our integration process. Information from YouTube video is mapped in GetItemFields method. This is the code:
public override FieldList GetItemFields(ItemDefinition itemDefinition, VersionUri versionUri, CallContext context)
{
FieldList fields = new FieldList();
if (CanProcessYouTubeItem(itemDefinition.ID, context))
{
string originalID = GetOriginalRecordID(itemDefinition.ID);
TemplateItem templateItem = ContentDB.Templates[resourceTemplateID];
if (templateItem != null)
{
YTItemInfo ytItemInfo = GetYTItemInfo(itemDefinition.ID);
if (ytItemInfo != null)
{
foreach (var field in templateItem.Fields)
{
fields.Add(field.ID, GetFieldValue(field, ytItemInfo));
}
}
}
}
return fields;
}
As you can see it's simply iterates through template fields and maps data from YouTube Entry to Sitecore YouTube video item.
We got YouTube video items in our content tree. But we cannot do anything with them on front-end. One option is to add our data provider to web database to get it working on front-end. But what if we make main data provider know about our items and just publish them as a real items. All I need to do is to get list of IDs of our items in GetPublishQueue method.
This is how the code looks:
public override IDList GetPublishQueue(DateTime from, DateTime to, CallContext context)
{
IDList list = new IDList();
foreach (var item in _items)
{
YTItemInfo ytItemInfo = (YTItemInfo) item;
list.Add(ytItemInfo.ItemID);
}
return list;
}
Since I did not use a publish queue table, YouTube items are going to be published every time to run publish operation on them. You can avoid this if you implement AddToPublishQueue and CleanupPublishQueue methods.
Here is a screen dump the way YouTube item looks in Sitecore media library:

This is a sitecore package that will install data provider on your Sitecore 6.0 CMS. I do not described provider configuration in this article. You can find it in /App_Config/Include folder after installing the package.
NOTE: if installation fails, that means that you run into a known issue. Reinstalling the package should solve it.
Friday, December 19, 2008
Adding buttons to inline-editing frame
Today's post is dedicated to Sitecore inline-editor and some customization you can put into it. The question I'm going to address is:
"Is it possible to add custom buttons to inline-editor frame?"
The answer is simple as is: "Yes, it is" :). Wait... don't go away. Read further to find out the solution.
Our challenge is to add a button to inline-editor. Let's do that for Rich Text field.
I put this approach into the steps to outline this process. Let's start:
1) Open Sitecore Client and go to the Core database.
As you know (or maybe you're going to find out right away) Rich Text field has several profiles to give different sets of functionality to logged in users. Let's go to default profile and check its configuration, here is the path in content tree: "/sitecore/system/Settings/Html Editor Profiles/Rich Text Default".
2) The configuration node we're interested in is "WebEdit Buttons". Go ahead and expand it.
Beneath the node you can see all available buttons which are going to show up as soon as you put mouse cursor over the rich text data in Page Editor.
3) Since we're already here let's add our own button to the current set. A button should be created from "/sitecore/templates/System/WebEdit/WebEdit Button" template.
Header field contains the name of the button in inline-editor frame.
Icon field has the icon for the button
Click field is the most important part. It has command message that should be sent when you hit the button.
a) Here is the format of this field for the custom command: "javascript:return Sitecore.WebEdit.editControl($JavascriptParameters, "webedit:mybutton:message")"
Javascript command is required to set you command message that is passed as second parameter for the "editControl" function.
"webedit:mybutton:message" is command message. Don't be concerned about the format of this message. It can have any format you want. For instance: "webedit:mymessage" or "webedit:mybutton:mymessage". It's good when the first part of the message describes an area the message belongs to, e.g. "webedit:".
b) Here is another example to run JavaScript function through the button.
Click field should look like this: javascript:Sitecore.WebEdit.execute("js_function_name", true, "parameter_for_js_function");
This example changes the font size:
javascript:Sitecore.WebEdit.execute("fontsize", true, "20");
NOTE: if you use javascript function, the Header field for the button item should be left empty.
Tooltip field describes itself :).
4) This is the final step. You have to create a command handler that will do action you want.
I'm not going to touch command creation process here. You can find sample in John's article here.
NOTE: webedit command handler class must be inherited from Sitecore.Shell.Applications.WebEdit.Commands.WebEditCommand one.
"Is it possible to add custom buttons to inline-editor frame?"
The answer is simple as is: "Yes, it is" :). Wait... don't go away. Read further to find out the solution.
Our challenge is to add a button to inline-editor. Let's do that for Rich Text field.
I put this approach into the steps to outline this process. Let's start:
1) Open Sitecore Client and go to the Core database.
As you know (or maybe you're going to find out right away) Rich Text field has several profiles to give different sets of functionality to logged in users. Let's go to default profile and check its configuration, here is the path in content tree: "/sitecore/system/Settings/Html Editor Profiles/Rich Text Default".
2) The configuration node we're interested in is "WebEdit Buttons". Go ahead and expand it.
Beneath the node you can see all available buttons which are going to show up as soon as you put mouse cursor over the rich text data in Page Editor.
3) Since we're already here let's add our own button to the current set. A button should be created from "/sitecore/templates/System/WebEdit/WebEdit Button" template.
Header field contains the name of the button in inline-editor frame.
Icon field has the icon for the button
Click field is the most important part. It has command message that should be sent when you hit the button.
a) Here is the format of this field for the custom command: "javascript:return Sitecore.WebEdit.editControl($JavascriptParameters, "webedit:mybutton:message")"
Javascript command is required to set you command message that is passed as second parameter for the "editControl" function.
"webedit:mybutton:message" is command message. Don't be concerned about the format of this message. It can have any format you want. For instance: "webedit:mymessage" or "webedit:mybutton:mymessage". It's good when the first part of the message describes an area the message belongs to, e.g. "webedit:".
b) Here is another example to run JavaScript function through the button.
Click field should look like this: javascript:Sitecore.WebEdit.execute("js_function_name", true, "parameter_for_js_function");
This example changes the font size:
javascript:Sitecore.WebEdit.execute("fontsize", true, "20");
NOTE: if you use javascript function, the Header field for the button item should be left empty.
Tooltip field describes itself :).
4) This is the final step. You have to create a command handler that will do action you want.
I'm not going to touch command creation process here. You can find sample in John's article here.
NOTE: webedit command handler class must be inherited from Sitecore.Shell.Applications.WebEdit.Commands.WebEditCommand one.
Friday, July 25, 2008
Migrate from 5.3 to 6.0 (alpha)
Today was my first experience with "alpha" version of Sitecore migration from 5.3 to 6.0.
What I can say, that was not so bad :-). Anyways, I have to say that my site was pretty simple and did not have heavy customization (no custom fields, default devices, no proxies).
I migrated only master database because I had only default security objects in my solution.
I followed all steps from documentation and did not face any problems. Altogether it took about 3 hours to migrate my content and clean up my solution from migration tool. Not bad! Again that was the first time when I saw this tool.
In addition I spent about 2+ hours to adjust my code to spin it in Sitecore 6.0.
The most common issue for my renderings was related to pulling out media url using "sc:fld()" function and media item variable.
I had to substitute "sc:fld()" for "sc:GetMediaUrl()".
I thought I would have to do some modifications to get in-line editing working in V6. I was surprised that old buddy "sc:dot" represented this opportunity.
So, as a result it is not so bad as it sounds at the first glance ;-).
You should try it out!
What I can say, that was not so bad :-). Anyways, I have to say that my site was pretty simple and did not have heavy customization (no custom fields, default devices, no proxies).
I migrated only master database because I had only default security objects in my solution.
I followed all steps from documentation and did not face any problems. Altogether it took about 3 hours to migrate my content and clean up my solution from migration tool. Not bad! Again that was the first time when I saw this tool.
In addition I spent about 2+ hours to adjust my code to spin it in Sitecore 6.0.
The most common issue for my renderings was related to pulling out media url using "sc:fld()" function and media item variable.
I had to substitute "sc:fld()" for "sc:GetMediaUrl()".
I thought I would have to do some modifications to get in-line editing working in V6. I was surprised that old buddy "sc:dot" represented this opportunity.
So, as a result it is not so bad as it sounds at the first glance ;-).
You should try it out!
Thursday, April 17, 2008
Share site resources among several sites
If you have lots of sites with similar structure and presentation layer you can make them use resources of one Sitecore site from web.config file. To do this you can create custom SiteResolver and insert it in <httprequestbegin> pipeline just after the native SiteResolver processor.
Using this approach you should have custom SiteResolver processor and special config file where you can map your sites to the same instance of Sitecore site from web.config file. It shows you how you can set several host names for the same Sitecore site in web.config file.
NOTE: This approach is supposed to have “website” site in your web.config file.
Using this idea you can set not only host names but the whole site configuration for the same Sitecore site. In that case you will be able to share cache and other resources among several sites.
Below are the steps how to apply this approach:
1) Create custom SiteResolver and set add it in web.config file just after the native SiteResolver processor, as shown below:
<httprequestbegin>
………………
<processor type="Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel" />
<processor type="SiteMapper.Pipelines.HttpRequest. SimilarSitesResolver, SiteMapper" />
………………
</httprequestbegin>
2) Create special config file (similar.sites.config) to map your sites with the following structure:
<sites>
<site mode="on" name="www.mysite" hostname="www.mysite.dk" language="da-DK" />
<site mode="on" name="www.mysite" hostname="www.mysite.uk" language="en-GB" />
<site mode="on" name="www.mysite" hostname="www.mysite.com" language="en" />
</sites>
As you can see all three sites have the same “name”. This name determine which site from web.config file should be used when you request any of these sites.
Example of web.config file:
<!-- ***** www.mysite group ***** -->
<site mode="on" name="www.mysite" hostname=" www.mysite.com" language="en" virtualfolder="/" physicalfolder="/" rootpath="/sitecore/content" startitem="/www.mysite/home" database="web" domain="extranet" allowdebug="true" cachehtml="true" htmlcachesize="10MB" enablepreview="true" enablewebedit="true" enabledebugger="true" disableclientdata="false" />
<!-- “website” is compulsory for this solution -->
<site name="website" virtualfolder="/" physicalfolder="/" rootpath="/sitecore/content" startitem="/flugger/home" database="master" domain="extranet" allowdebug="true" cachehtml="true" htmlcachesize="10MB" enablepreview="true" enablewebedit="true" enabledebugger="true" disableclientdata="false" />
NOTE: each Sitecore site in web.config file must have hostName attribute. Otherwise Sitecore will classify this site as a default website and you won’t get required results.
Here is Sitecore package.
Using this approach you should have custom SiteResolver processor and special config file where you can map your sites to the same instance of Sitecore site from web.config file. It shows you how you can set several host names for the same Sitecore site in web.config file.
NOTE: This approach is supposed to have “website” site in your web.config file.
Using this idea you can set not only host names but the whole site configuration for the same Sitecore site. In that case you will be able to share cache and other resources among several sites.
Below are the steps how to apply this approach:
1) Create custom SiteResolver and set add it in web.config file just after the native SiteResolver processor, as shown below:
<httprequestbegin>
………………
<processor type="Sitecore.Pipelines.HttpRequest.SiteResolver, Sitecore.Kernel" />
<processor type="SiteMapper.Pipelines.HttpRequest. SimilarSitesResolver, SiteMapper" />
………………
</httprequestbegin>
2) Create special config file (similar.sites.config) to map your sites with the following structure:
<sites>
<site mode="on" name="www.mysite" hostname="www.mysite.dk" language="da-DK" />
<site mode="on" name="www.mysite" hostname="www.mysite.uk" language="en-GB" />
<site mode="on" name="www.mysite" hostname="www.mysite.com" language="en" />
</sites>
As you can see all three sites have the same “name”. This name determine which site from web.config file should be used when you request any of these sites.
Example of web.config file:
<!-- ***** www.mysite group ***** -->
<site mode="on" name="www.mysite" hostname=" www.mysite.com" language="en" virtualfolder="/" physicalfolder="/" rootpath="/sitecore/content" startitem="/www.mysite/home" database="web" domain="extranet" allowdebug="true" cachehtml="true" htmlcachesize="10MB" enablepreview="true" enablewebedit="true" enabledebugger="true" disableclientdata="false" />
<!-- “website” is compulsory for this solution -->
<site name="website" virtualfolder="/" physicalfolder="/" rootpath="/sitecore/content" startitem="/flugger/home" database="master" domain="extranet" allowdebug="true" cachehtml="true" htmlcachesize="10MB" enablepreview="true" enablewebedit="true" enabledebugger="true" disableclientdata="false" />
NOTE: each Sitecore site in web.config file must have hostName attribute. Otherwise Sitecore will classify this site as a default website and you won’t get required results.
Here is Sitecore package.
Subscribe to:
Posts (Atom)