Blogger

  • This email address is being protected from spambots. You need JavaScript enabled to view it.

    Recent items

Login

In these days, me and This email address is being protected from spambots. You need JavaScript enabled to view it. had working on an easy requirement but that to realized it, we we thought too much.

What we need to realize it is a simple Item type refinement panel that works with a multiple selection, easy.

A first idea was to change the settings on the refinement from single selection to multiple selection. But it not produced what we expected:

  1. The label was changed from Type to extentions. Example: from Web Pages to html, etc.
  2. some items that were found they not had the respective item in the refinement panel and some associations were wrong: example the videos are grouped with the html filter, etc.

After some analysis phases, we were able to make these assumptions:

  • a lot of items were crawled with the dispform.aspx, so they were grouped as html. They had another Managed Property (MP) "SecondaryFileExtention" that contained the correct file extention of the item
  • Some items, for example the video, are managed from SharePoint with a particular Content Type (ContentTypeId=). These items appears in the search both FileType MP and SecondaryFileExtention MP empty
  • to complete the work, the File Type filter need to group on:
    • for some items on the FileType MP
    • for some items on the SecondaryFileExtention MP
    • for some items on the ContentType MP
  • The logic of the refinement panel is not able to apply OR rules (written by himself by the respective refinement panel display template) on different Managed Properties.

At the end of a long week of tests, we had the solution:

  • We created a new Managed Property with the name LC.ResultType that contains the follow crawled properties (all of the single MP)
    • ows_ContentTypeId
    • ows_FileType
    • ows_File_x0020_Type
  • After a full crawl to allow the Managed Property to be filled, we modified the OOB Multi Selection Display Template to point to our Custom Managed Property in each occurrences
  • We creted the follow mapping of the item types, save and publish the new Display Template

Custom Multi selection Display Template example that use the custom Managed Property LC..ResultType:

Show/Hidden sql code

View source
 
 
 
<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:A29FA29F-C1488-11d1-1010-C2F400AA002"> 
<head>
<title>Multi-value</title>
 
<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:CompatibleManagedProperties msdt:dt="string"></mso:CompatibleManagedProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:CompatibleSearchDataTypes msdt:dt="string"></mso:CompatibleSearchDataTypes>
<mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106604</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#Refinement;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:CrawlerXSLFile msdt:dt="string"></mso:CrawlerXSLFile>
<mso:HtmlDesignPreviewUrl msdt:dt="string"></mso:HtmlDesignPreviewUrl>
<mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
<mso:HtmlDesignStatusAndPreview msdt:dt="string">http://SEARCH.lucacostante.com/_catalogs/masterpage/Display Templates/Filters/MultiValue_ResultType.html, Conversion successful.</mso:HtmlDesignStatusAndPreview>
</mso:CustomDocumentProperties></xml><![endif]-->
</head>
<body>
<div id="Filter_MultiValue">
<!--#_
    var Options = {
        ShowCounts: TRUE
    };
 
    var listData = ctx.ListData;
    var hasControl = !$isNull(ctx.RefinementControl) && !$isNull(ctx.ClientControl);
 
    IF(hasControl) {
        var hasNoListData = ($isEmptyArray(listData));
        var propertyName = ctx.RefinementControl.propertyName;
        var displayTitle = Srch.Refinement.getRefinementTitle(ctx.RefinementControl);
        var isExpanded = Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);
        var useContains = FALSE;
        var useKQL = FALSE;
        var refiners = [];
        var refinersAll = [];
        
        IF (hasNoListData) {
            listData = [];
        }
        
        // TODO: unify same FileType
        listData = mapResultType(listData);
        
        IF (ctx.RefinementControl.csr_filters) {
            FOR(var t = 0; t < ctx.RefinementControl.csr_filters.LENGTH; t++) {
                refinersAll.push({
                    PropertyName: ctx.RefinementControl.csr_filters[t].PropertyName,
                    RefinementName: ctx.RefinementControl.csr_filters[t].RefinementName,
                    RefinementTokens: ctx.RefinementControl.csr_filters[t].RefinementTokens,
                    RefinementTokensMap: ctx.RefinementControl.csr_filters[t].RefinementTokensMap,
                    RefinementCount: ctx.RefinementControl.csr_filters[t].RefinementCount,
                    IsSelected: FALSE
                });
            }
        }
 
        var currentRefinementCategory = ctx.ClientControl.getCurrentRefinementCategory(ctx.RefinementControl.propertyName);
        var hasAnyFilterTokens = (!$isNull(currentRefinementCategory) && currentRefinementCategory.get_tokenCount() > 0);
        var renderEmptyContainer = hasControl && (hasNoListData && !hasAnyFilterTokens);
        
        IF(!renderEmptyContainer) {
            var listDataTokenToDisplayMap = {};
            var listDataTokenToCountMap = {};
            IF(!hasNoListData) {
                FOR (var i = 0; i < listData.LENGTH; i++) {
                    var FILTER = listData[i];
                    IF(!$isNull(FILTER)) {
                        FOR (var j = 0; j < FILTER.RefinementTokens.LENGTH; j++) {
                            listDataTokenToDisplayMap[FILTER.RefinementTokens[j]] = FILTER.RefinementName;
                            listDataTokenToCountMap[FILTER.RefinementTokens[j]] = FILTER.RefinementCount;
                        }
                        
                        IF(!hasAnyFilterTokens && !$isEmptyString(FILTER.RefinementName) && !$isEmptyString(FILTER.RefinementTokens)) {
                            refiners.push(
                            {
                                PropertyName: FILTER.RefinerName,
                                RefinementName: FILTER.RefinementName,
                                RefinementTokens: FILTER.RefinementTokens,
                                RefinementTokensMap: FILTER.RefinementTokensMap,
                                RefinementCount: FILTER.RefinementCount,
                                IsSelected: FALSE
                            });
                        }
                    }
                }
            }
            
            IF(hasAnyFilterTokens) {
                FOR(var j = 0; j < currentRefinementCategory.get_tokenCount(); j++) {
                    var token = currentRefinementCategory.t[j];
                    var displayValue = listDataTokenToDisplayMap[token];
                    IF($isEmptyString(displayValue) && !$isNull(currentRefinementCategory.m)) {
                        displayValue = currentRefinementCategory.m[token];
                    }
                    IF(!$isEmptyString(displayValue) && !$isEmptyString(token)) {
                        IF (refinersAll.LENGTH > 0) {
                            FOR (var k = 0; k < refinersAll.LENGTH; k++) {
                                FOR (var t = 0; t < refinersAll[k].RefinementTokens.LENGTH; t++) {
                                    IF (refinersAll[k].RefinementTokens[t] == token) {
                                        refinersAll[k].IsSelected = TRUE;
                                        break;
                                    }
                                }
                            }
                        }
                        ELSE {
                            refiners.push(
                            {  
                                PropertyName: FILTER.RefinerName,
                                RefinementName: displayValue,
                                RefinementTokens: token,
                                RefinementCount: !$isNull(listDataTokenToCountMap[token]) ? listDataTokenToCountMap[token] : 0,
                                IsSelected: TRUE
                            });
                        }
                    }
                }
            }
        }
        
        ctx.RefinementControl["csr_propertyName"] = propertyName;
        ctx.RefinementControl["csr_displayTitle"] = displayTitle;
        //ctx.RefinementControl["csr_filters"] = refiners;
        ctx.RefinementControl["csr_filters"] = hasAnyFilterTokens && refinersAll.LENGTH > 0 ? refinersAll : refiners;
        ctx.RefinementControl["csr_isExpanded"] = isExpanded;
        ctx.RefinementControl["csr_renderEmptyContainer"] = renderEmptyContainer;
        ctx.RefinementControl["csr_useContains"] = useContains;
        ctx.RefinementControl["csr_useKQL"] = useKQL;
        ctx.RefinementControl["csr_showCounts"] = Options.ShowCounts;
 
        // SHOW Item
        renderRefinerItems(ctx);
    }
_#-->
 
<!--#_
 
    FUNCTION mapResultType(listData) {
        var map = { };
        // Standard ResultType...
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSPowerPoint")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["ppt", "pptx", "pps", "ppsx", "odp", "pptm", "potm", "potx", "ppam", "ppsm"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSWord")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["docx", "doc", "dot", "rtf", "txt", "docm", "nws", "dotx"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_MSExcel")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["xls", "xlsx", "csv", "odc", "ods", "xlsb", "xlsm", "xltm", "xltx", "xlam"]
        }; 
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_AdobePDF")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["pdf"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Video")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x0120D520A8*", "wmv", "avi", "flv", "mov", "mpeg"]
        };
        map["Audio File"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["mp4", "mp3", "wma", "ra", "Podcast"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Archive")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["zip", "rar"]
        };
        map[Srch.U.loadResource("rf_ResultTypeRefinerValue_Webpage")] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["HTML", "xml", "MHTML"]
        };
 
 
 
        // Specific ResultType 
        map["News"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039E7E22DFD724B12B47BFBC3F5AA086D000580873f397b481ab8c1115a1cfc37a8*", "0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039E7E22DFD724B12B47BFBC3F5AA086D000580873F397B481AB8C1115A1CFC37A800a3b3a6c651184e438510779fe223a2a6*"]
        };
        map["Tool"] ={
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x01000268E795EAEA4ABB8E5C2E8D0AB2485C*"]
        };
        map["PhotoGallery"] = {
            "RefinerName": "LC.ResultType",
            "RefinementValues": ["0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390039e7e22dfd724b12b47bfbc3f5aa086d00b1d929c83a1a4f37ae818af558a4898200116ce8a6eddf43c9af19c678407d4166*"]
        };
        
        var retListData = NEW Array();
        var assocListData = NEW Array();
        FOR (var i = 0; i < listData.LENGTH; i++) {
            var FILTER = listData[i];
            var mappedRefinementName = NULL;
            FOR(var KEY IN map) {
                IF (map[KEY].RefinerName == FILTER.RefinerName) {
                    FOR (var j = 0; j < map[KEY].RefinementValues.LENGTH; j++) {
                        var actualValue = FILTER.RefinementValue.toLowerCase();
                        var candidateValue = map[KEY].RefinementValues[j].toLowerCase();
 
                        IF (actualValue == candidateValue ||
                            (actualValue.startsWith("0x") && actualValue.startsWith(candidateValue.SUBSTRING(0, candidateValue.LENGTH - 1))))
                        {
                            mappedRefinementName = KEY;
                            break;
                        }
                    }
                    IF (!$isNull(mappedRefinementName)) {
                        break;
                    }
                }
            }
 
            var mappedFilter = NEW Object();
            
            IF (!$isNull(mappedRefinementName)) {
                mappedFilter.RefinerName = map[mappedRefinementName].RefinerName;
                mappedFilter.RefinementCount = FILTER.RefinementCount;
                mappedFilter.RefinementName = mappedRefinementName;
                mappedFilter.RefinementTokens = [];
                //var resultTypeTokenWrapper = (mappedFilter.RefinerName.toLowerCase() == "contenttypeid") ? FUNCTION (x) {RETURN x;} : 
                //                                                                                           Srch.RefinementUtil.stringValueToEqualsToken;
                var resultTypeTokenWrapper = FUNCTION(token) {
                    IF (token.startsWith("0x")) {
                        RETURN token;
                    }
                    
                    RETURN Srch.RefinementUtil.stringValueToEqualsToken(token);
                };
                FOR (var j IN map[mappedRefinementName].RefinementValues) {
                    mappedFilter.RefinementTokens.push(resultTypeTokenWrapper(map[mappedRefinementName].RefinementValues[j]));
                }
 
                IF ($isNull(assocListData[mappedFilter.RefinementName])) {
                    assocListData[mappedFilter.RefinementName] = mappedFilter;
                }
                ELSE {
                    assocListData[mappedFilter.RefinementName].RefinementCount += mappedFilter.RefinementCount;
                }
            }
        }
 
        FOR (var KEY IN assocListData) {
            retListData[retListData.LENGTH] = assocListData[KEY];
        }
 
        RETURN retListData;
    }
_#-->
 
<!--#_
    AddPostRenderCallback(ctx, FUNCTION() {
        $('#Container input[type=checkbox]').ON('click', FUNCTION() {
            IF ($(this).IS(':checked')) {
                $(this).parent().find('label').css("font-weight", "bold").css("color", "#2D2D2D");
            } ELSE {
                $(this).parent().find('label').css("font-weight", "").css("color", "");
            }
        });
    });
_#-->
 
<!--#_
    FUNCTION renderRefinerItems(ctx) {
        var propertyName = ctx.RefinementControl["csr_propertyName"];
        var displayTitle = ctx.RefinementControl["csr_displayTitle"];
        var filters = ctx.RefinementControl["csr_filters"];
        var isExpanded = BOOLEAN(ctx.RefinementControl["csr_isExpanded"]);
        var renderEmptyContainer = BOOLEAN(ctx.RefinementControl["csr_renderEmptyContainer"]);
        var useContains = BOOLEAN(ctx.RefinementControl["csr_useContains"]);
        var useKQL = BOOLEAN(ctx.RefinementControl["csr_useKQL"]);
        var showCounts = BOOLEAN(ctx.RefinementControl["csr_showCounts"]);
 
        IF($isEmptyString(propertyName) || (!$isNull(renderEmptyContainer) && renderEmptyContainer)) { 
_#-->
            <div id="EmptyContainer"></div>
<!--#_
        }
        ELSE IF(!$isNull(filters) && Srch.U.isArray(filters) && !$isEmptyArray(filters)) { 
            var expandedStatus = !$isNull(isExpanded) ? isExpanded : TRUE;
            var iconClass = "ms-core-listMenu-item ";
 
            iconClass += expandedStatus ? " ms-ref-uparrow " : " ms-ref-downarrow ";
_#-->
            <div id="_#= propertyName =#_-MultiRefiner">
            <div id="Container">
                _#= Srch.U.collapsibleRefinerTitle(propertyName, ctx.ClientControl.get_id(), displayTitle, iconClass) =#_
                <div class="ms-ref-unselSec" id="UnselectedSection">
                    <div id="unselShortList" class="ms-ref-unsel-shortList">
<!--#_ 
            FOR (var i = 0; i < filters.LENGTH; i++) {
                var FILTER = filters[i];
                IF(!$isNull(FILTER)) {
                    var inputValues = "";
                    var isSelected = BOOLEAN(FILTER.IsSelected);
                    var inputName = propertyName + '_ChkGroup';
                    var inputId = inputName + "_" + FILTER.RefinementName;
                    var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");
                    var selectClass = " custom-ref-fullwidth " + ((i == (filters.LENGTH - 1)) ? "" : " bordered-bottom ") + (isSelected ? " bold-text " : "");
                    
                    IF (FILTER.RefinementTokensMap) {
                        // multi typed refinement CASE
                        FOR(var KEY IN FILTER.RefinementTokensMap) {
                            inputValues += KEY + ":" + FILTER.RefinementTokensMap[KEY] + "|";
                        }
                    }
                    ELSE {
                        inputValues = FILTER.RefinementTokens;
                    }
_#-->
                        <div id="Value" class="_#= selectClass =#_">
<!--#_
                    IF(isSelected) {
_#-->
                            <div class="cust-filter-ck">
                                <INPUT TYPE="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" VALUE="_#= $htmlEncode(inputValues) =#_" checked="" onclick="MultiRefinement.SubmitRefinement('_#= $scriptEncode(filter.PropertyName) =#_', $getClientControl(this), '_#= $scriptEncode(inputId) =#_', _#= isSelected =#_);" />
                            </div>
<!--#_
                    } ELSE {
_#-->
                            <div class="cust-filter-ck">
                                <INPUT TYPE="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" VALUE="_#= $htmlEncode(inputValues) =#_" onclick="MultiRefinement.SubmitRefinement('_#= $scriptEncode(filter.PropertyName) =#_', $getClientControl(this), '_#= $scriptEncode(inputId) =#_', _#= isSelected =#_);" />
                            </div>
<!--#_
                    }
_#-->
                            <label FOR="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_'>
                                _#= $htmlEncode(FILTER.RefinementName) =#_
                            </label>
<!--#_
                    IF (showCounts) {
_#-->
                            <label FOR="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_ cust-ref-count'>
                                <span id='RefinementCount' class='ms-ref-count ms-textSmall'> (_#= $htmlEncode(Srch.U.toFormattedNumber(FILTER.RefinementCount)) =#_) </span>
                            </label>
<!--#_
                    }
_#-->
                        </div>
<!--#_
                }
            }
_#-->
                    </div>
                </div>
<!--#_
        }
_#-->
            </div>
            </div>
<!--#_
    }
_#-->
</div>
</body>
</html>
 
 
 
 
 

Results:

How expected, now we have a custom refiner that is able to filter on two property (in this case, but you can also add more property): the extention and the content type.

 

 

Published in SharePoint

Hi everybody,

today I'm encourring in a strange problem: I need to display a value of a custom Managed Property inside the "Item_CommonItem_Body.html" Display Template, near the date visualization.

Here a little image regarding the result that I need (inside the red box there is the value of my custom Managed Property).

To add this value, you need to manipulate the "Item_CommonItem_Body.html" Display Template. How we know, to view the custom managed property, we need to declare it on top of the page and after we need to update the respective result type. So I go inside the result type page and I'm not able to see a result type that follow the "Item_CommonItem_Body.html" Display Template.

I've created a new result type that link to the Common Item Body, but when I reload the page, all items displayed are corrupted, the html il broken. Infact, all items had the id with undefined value and all not works fine.

At that time, the question was: how I can use it?

The solution is: you need to add the custom Managed Metadata definition inside the "Item_WebPage.html" Display template. After that, when you return inside the result type page, you need to copy the current one that contains the "Item_WebPage.html" display template. The system propones to update it.

Now the system is able to manage that values. So, now you can add the code to display the value inside the "Item_CommonItem_Body.html" Display template.

At the end of the history, to manage a custom Managed property inside the "Item_CommonItem_Body.html" display template, you need:

  1. Define the Managed Metadata property inside the "Item_WebPage.html" display template
  2. Create and update a result type based on "Item_WebPage.html"
  3. Use you custom managed metadata property inside the "Item_CommonItem_Body.html"

Note: if you don't want problems with a wrong displaying of the results, you mustn't never create a result type that use the "Item_CommonItem_Body.html".

Published in SharePoint