First of all in this blog post I am going to explain how I resolved this little issue, but if this is the best solution I don’t know feel free to comment and share your idea’s with me.
OOB SharePoint 2010 offers a refinement panel that works great with the if you don’t use multiple languages. Unfortunately the project where I’m currently working on is a SharePoint environment with multiple languages. After some testing with search and the refinement panel I noticed that the Managed Metadata wasn’t changing when I was switching languages. This also was confirmed at the BIWUG 30/11 by Joris Poelmans.
After some thinking and testing I came to the conclusion that if I can edit the xml that most of my issues would be solved.
Here is a piece of the XML file. For getting the right term we can get the Id from the Url property.
<FilterCategory Id="Metadata_Groentenx0020_x0020Lx" ConfigId="Microsoft_Office_Server_Search_WebControls_TaxonomyFilterGenerator__-52273983" Type="Microsoft.Office.Server.Search.WebControls.TaxonomyFilterGenerator" DisplayName="Groenten - Légumes" ManagedProperty="ows_MetadataFacetInfo" ShowMoreLink="True" MoreLinkText="show more" LessLinkText="show fewer" ShowCounts="" ShowTaggingControl="false">
<Filters>
<Filter>
<Value>Any Groenten - Légu…Value>
<Tooltip>Any Groenten - LégumesTooltip>
<Url>/Search/Pages/XMLResults.aspx?k=ui&r=Url>
<Selection>ImpliedSelection>
<Count>Count>
Filter>
<Filter>
<Value>UiValue>
<Url>/Search/Pages/XMLResults.aspx?k=ui&r=%22owstaxIdGroentenx0020%2Dx0020Lx%22%3D%23560159a2%2D4d98%2D4722%2Da0b1%2D1b06893769e6%3A%22Ui%22Url>
<Selection>DeselectedSelection>
<Count>3Count>
Filter>
<Filter>
<Value>PaprikaValue>
<Url>/Search/Pages/XMLResults.aspx?k=ui&r=%22owstaxIdGroentenx0020%2Dx0020Lx%22%3D%23e216effa%2D7bac%2D4f99%2Da762%2Dcf18ee2810bb%3A%22Paprika%22Url>
<Selection>DeselectedSelection>
<Count>2Count>
Filter>
<Filter>
<Value>TomaatValue>
<Url>/Search/Pages/XMLResults.aspx?k=ui&r=%22owstaxIdGroentenx0020%2Dx0020Lx%22%3D%23b4238ff9%2D6e9c%2D43d1%2Db9ad%2Dbad7317221b4%3A%22Tomaat%22Url>
<Selection>DeselectedSelection>
<Count>2Count>
Filter>
Filters>
<MoreFilters>
<Filter>
<Value>AardappelenValue>
<Url>/Search/Pages/XMLResults.aspx?k=ui&r=%22owstaxIdGroentenx0020%2Dx0020Lx%22%3D%23efad4c07%2Df37c%2D4bf5%2Da602%2Dd1ed5ff479a7%3A%22Aardappelen%22Url>
<Selection>DeselectedSelection>
<Count>1Count>
Filter>
MoreFilters>
<CustomData>
<AssociateTermSets>e1092ea9-35df-41fd-b790-d29c469a95a5|320d82f1-6649-43c6-96a5-8bfedeb6c9f0AssociateTermSets>
<EscapedDisplayName>Groenten\ -\ LégumesEscapedDisplayName>
CustomData>
FilterCategory>
The following points are important to know.
-
<FilterCategory Id="Metadata_ ….. When a category starts with “Metadata_” it is metadata :)
- The difference between “Filters” and “MoreFilters”
- The id can be retrieved from the “Url”
My Fix
Step 1:
Create a new SharePoint Empty project and name it MUIRefinement and choose farm solution.
Step 2:
Add the following reference Microsoft.Office.Server.Search can be found at “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.Office.Server.Search.dll”
Step 3:
Add a new webpart NOT A VISUAL WEBPART and name it ExtendedMUIRefinementPanel
Step 4:
Add a the following usings
using Microsoft.Office.Server.Search.WebControls;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
Step 5:
Extend the webpart to RefinementWebpart
public class ExtendedMUIRefinementPanel : RefinementWebPart
Step6:
Create the following extension methods. These methods converts XmlDocument To XDocument and XDocument to XmlDocument.
public static class DocumentExtensions
{
public static XmlDocument ToXmlDocument(this XDocument xDocument)
{
var xmlDocument = new XmlDocument();
using (var xmlReader = xDocument.CreateReader())
{
xmlDocument.Load(xmlReader);
}
return xmlDocument;
}
public static XDocument ToXDocument(this XmlDocument xmlDocument)
{
using (var nodeReader = new XmlNodeReader(xmlDocument))
{
nodeReader.MoveToContent();
return XDocument.Load(nodeReader);
}
}
}
Step 7:
Create a private method TranslateMetadata
private XmlDocument TranslateMetadata(XmlDocument xmlDocument)
{
XElement element = xmlDocument.ToXDocument().Element("FilterPanel");
var query = from p in element.Elements()
where p.Attribute("Id").Value.StartsWith("Metadata_")
select p;
foreach (XElement item in query)
{
TaxonomySession sess = new TaxonomySession(SPContext.Current.Site, false);
foreach (XNode node in item.Element("Filters").Nodes())
{
XElement cnode = (XElement)node;
SetXmlValue(sess, cnode);
}
if (item.Element("MoreFilters") != null)
{
foreach (XNode node in item.Element("MoreFilters").Nodes())
{
XElement cnode = (XElement)node;
SetXmlValue(sess, cnode);
}
}
}
return element.Document.ToXmlDocument();
}
First thing we do is convert XmlDocument to a XElement. The reason for this is, I think it is easier to use linq to xml for filtering certain data.
XElement element = xmlDocument.ToXDocument().Element("FilterPanel");
Get all the elements that starts with an id: “Metadata_”
var query = from p in element.Elements()
where p.Attribute("Id").Value.StartsWith("Metadata_")
select p;
Loop the following elements “Filters” and “MoreFilters”
foreach (XElement item in query)
{
TaxonomySession sess = new TaxonomySession(SPContext.Current.Site, false);
foreach (XNode node in item.Element("Filters").Nodes())
{
XElement cnode = (XElement)node;
SetXmlValue(sess, cnode);
}
if (item.Element("MoreFilters") != null)
{
foreach (XNode node in item.Element("MoreFilters").Nodes())
{
XElement cnode = (XElement)node;
SetXmlValue(sess, cnode);
}
}
}
Step 8:
Create the method SetXmlValue.
private static void SetXmlValue(TaxonomySession sess, XElement cnode)
{
var url = Uri.UnescapeDataString(cnode.Element("Url").Value);
if (url.Contains("#"))
{
var id = url.Substring(url.LastIndexOf("#") + 1).Split(':')[0];
if (id.Length == 37)
{
id = id.Substring(1);
}
Term term = sess.GetTerm(new Guid(id));
string result = string.Empty;
result = term.GetDefaultLabel(CultureInfo.CurrentUICulture.LCID);
if (result.Length > 14)
cnode.Element("Value").SetValue(result.Substring(0, 13) + "...");
else
cnode.Element("Value").SetValue(string.IsNullOrEmpty(result) ? term.Name : result);
if (cnode.Element("Tooltip") != null)
{
cnode.Element("Tooltip").SetValue(result);
}
}
}
First thing we need to do is get the url and unescape it so it is readable.
var url = Uri.UnescapeDataString(cnode.Element("Url").Value);
We will convert /Search/Pages/XMLResults.aspx?k=ui&r=%22owstaxIdGroentenx0020%2Dx0020Lx%22%3D%23560159a2%2D4d98%2D4722%2Da0b1%2D1b06893769e6%3A%22Ui%22 to /Search/Pages/XMLResults.aspx?k=ui&r="owstaxIdGroentenx0020-x0020Lx"=#560159a2-4d98-4722-a0b1-1b06893769e6:"Ui"
Then we check if the url we got contains an Guid. If we look at the url we see owstaxIdGroentenx0020-x0020Lx"=#560159a2-4d98-4722-a0b1-1b06893769e6:"Ui". owstaxIdGroentenx0020-x0020Lx is the metadata property that is used to filter that certain field. #560159a2-4d98-4722-a0b1-1b06893769e6:"Ui" is the termguid with the term label(the label depends on how it is saved more about that in my previous post ).
Thus if the url contains “#” the url contains a termguid.
To get the id we have to know that the last id we find is the id we are going to filter.
var id = url.Substring(url.LastIndexOf("#") + 1).Split(':')[0];
if (id.Length == 37)
{
id = id.Substring(1);
}
We have to check the length of the id. sometimes the XML return the property Metadata_Tags. In the Metadata_Tags there will be an extra 0 in the id. e.g. “0560159a2-4d98-4722-a0b1-1b06893769e6”
When we have the id, we can get the term label in the current language.
Term term = sess.GetTerm(new Guid(id));
string result = string.Empty;
result = term.GetDefaultLabel(CultureInfo.CurrentUICulture.LCID);
For branding reasons if have chosen to limit the length of the of the term to display. After we get the result we can now change the value of the Value property with the term label.
if (result.Length > 14)
cnode.Element("Value").SetValue(result.Substring(0, 13) + "...");
else
cnode.Element("Value").SetValue(string.IsNullOrEmpty(result) ? term.Name : result);
if (cnode.Element("Tooltip") != null)
cnode.Element("Tooltip").SetValue(result);
Step 9:
Create a global property.
private RefinementManager _manager;
Step 10:
Override the onit method to set the refinementmanager
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//get the refinementManager
_manager = RefinementManager.GetInstance(this.Page, this.QueryNumber);
}
Step 11:
Override the GetXPathNavigator to send the altered XML to the refinementpanel.
protected override XPathNavigator GetXPathNavigator(string viewPath)
{
XmlDocument xmlDocument = _manager.GetRefinementXml();
if (xmlDocument == null)
return base.GetXPathNavigator(viewPath);
xmlDocument = TransLateMetadata(xmlDocument);
return xmlDocument.CreateNavigator();
}
Step 12:
Build and deploy your solution.
Result
Refinementpanel in Dutch
Refinementpanel in French