At Work 04 : Custom Document ECB Context Menu in search results PreviewPane (using Display Templates)

PreviewPaneWithOptions

PreviewPaneWithOptions

This post is about showing the context menu in SharePoint Search results preview panes using Custom Display templates.
The solution provided here is not an OOTB menu that comes in Document Library ListView webpart. This is a custom markup and JS to render the options.

I’m using a custom JS file(previewpanefunctions.js) where I wrote all my functions which are called from hovertemplates (JS file can be downloaded at the end of this post). This JS can be reffered in your custom Control template using $includeScript like this

<script type="text/javascript">
 $includeScript(this.url, "~sitecollection/siteassets/scripts/PreviewpaneFunctions.js");
</script>

<div id="Control_SearchResults">
.....

implement your custom item display template, and hover displaytemplates. make sure you give correct path of your hover display template in item template. In my example, I created a control template where I referred my custom JS file and a custom word displaty template(Item_searchresult_word.html) and a custom work hover display template(item_searchresult_word_hover.html) , and in my “Item_searchresult_word.html”

if(!$isNull(ctx.CurrentItem) && !$isNull(ctx.ClientControl)){
 var id = ctx.ClientControl.get_nextUniqueId();
 var itemId = id + Srch.U.Ids.item;
 var hoverId = id + Srch.U.Ids.hover;
 var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/myproject/item_searchresult_word_hover.js";
 $setResultItem(itemId, ctx.CurrentItem);
.......
.......

I implemented multiple display templates for each file type, all are with similar code, and created result sources and result types to use these templates in Search Results.

Now the real part is to create the hover template, I modified the existing display template markup to show some custom column values on previewpane and , custom actions like edit(owa), share, follow/unfollow, and menu(with multiple menu options) and some custom links on preview pane like ‘view version history’ and ‘add to onedrive’ links. I’ve used some custom functions to format date in this code. the template code looks like following, there are some other functionalities I’vent explained here like ‘uploading a new version’ of file. Which comes in my upcoming posts.

<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
<head>
<title>Word Item Hover Panel</title>
 
<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:MasterPageDescription msdt:dt="string">Displays a result hover panel tailored for a Microsoft Word document.</mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#SearchHoverPanel;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:ManagedPropertyMapping msdt:dt="string">'Title':'Title','Path':'Path','Description':'Description','EditorOWSUSER':'EditorOWSUSER','LastModifiedTime':'LastModifiedTime','CollapsingStatus':'CollapsingStatus','DocId':'DocId','HitHighlightedSummary':'HitHighlightedSummary','HitHighlightedProperties':'HitHighlightedProperties','FileExtension':'FileExtension','ViewsLifeTime':'ViewsLifeTime','ParentLink':'ParentLink','FileType':'FileType','IsContainer':'IsContainer','SecondaryFileExtension':'SecondaryFileExtension','DisplayAuthor':'DisplayAuthor','ServerRedirectedURL':'ServerRedirectedURL','SectionNames':'SectionNames','SectionIndexes':'SectionIndexes','ServerRedirectedEmbedURL':'ServerRedirectedEmbedURL','ServerRedirectedPreviewURL':'ServerRedirectedPreviewURL','RefinableString15':'RefinableString15','RefinableString19':'RefinableString19','RefinableString21':'RefinableString21','ListID':'ListID','ListItemID':'ListItemID','SPSiteURL':'SPSiteURL','ContactName':'ContactName','RefinableString22':'RefinableString22',,'SPWebUrl':'SPWebUrl'</mso:ManagedPropertyMapping>
<mso:_dlc_DocId msdt:dt="string">CONSC-7-1972</mso:_dlc_DocId>
<mso:_dlc_DocIdItemGuid msdt:dt="string">fda4514e-8afe-4630-8cc0-0a0a6d938dd6</mso:_dlc_DocIdItemGuid>
<mso:_dlc_DocIdUrl msdt:dt="string">https://siteurl/_layouts/15/DocIdRedir.aspx?ID=CONSC-7-1972, CONSC-7-1972</mso:_dlc_DocIdUrl>
<mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
<mso:HtmlDesignStatusAndPreview msdt:dt="string">https://siteurl/_catalogs/masterpage/Display Templates/Search/Item_Word_HoverPanel_RecentModifiedContent.html, Conversion successful.</mso:HtmlDesignStatusAndPreview>
<mso:CrawlerXSLFile msdt:dt="string"></mso:CrawlerXSLFile>
<mso:HtmlDesignPreviewUrl msdt:dt="string"></mso:HtmlDesignPreviewUrl>
</mso:CustomDocumentProperties>
</xml><![endif]-->
</head>
<body>
 

<div id="Item_Word_HoverPanel">
<!--#_ var i = 0; var wacurlExist = !Srch.U.e(ctx.CurrentItem.ServerRedirectedURL) && !Srch.U.e(ctx.CurrentItem.ServerRedirectedEmbedURL); var id = ctx.CurrentItem.csr_id; ctx.CurrentItem.csr_FileType = Srch.Res.file_Word; ctx.CurrentItem.csr_ShowFollowLink = true; ctx.CurrentItem.csr_ShowShareLink = true; ctx.CurrentItem.csr_ShowViewLibrary = true; //define ids var versionHistoryLink = id+ppConstants.versionHistoryLink; var addToOneDriveLink = id+ppConstants.addToOneDriveLink; var noVersioningMsg = id+ppConstants.noVersioningMsg; var contactNameLink = id+ppConstants.contactNameLink; var docViewEdit = id+ppConstants.docViewEdit; var followLink = id+ppConstants.docFollowStatusLink; var docShareLink = id+ppConstants.docShareLink; var ctxMenuLink = id+ppConstants.ctxMenuLink; //define menu element ids var ctxMenuContainer = id+ppConstants.ctxMenuContainer; var viewprops = id+ppConstants.viewPropsLink; var editprops = id+ppConstants.editPropsLink; var setAlerts = id+ppConstants.setAlertsLink; var checkin = id+ppConstants.checkInLink; var checkout = id+ppConstants.checkOutLink; var discardcheckout = id+ppConstants.discardCheckOutLink; var publishmajorversion =id+ppConstants.publishLink; var downloadcopy = id+ppConstants.downloadCopyLink; var deletefile = id+ppConstants.deleteFileLink; var uploadnewversion = id+ppConstants.uploadNewVersionLink _#-->

<div class="ms-srch-hover-innerContainer ms-srch-hover-wacSize preview" id="_#= $htmlEncode(id + HP.ids.inner) =#_">

<div class="ms-srch-hover-arrowBorder" id="_#= $htmlEncode(id + HP.ids.arrowBorder) =#_"></div>


<div class="ms-srch-hover-arrow" id="_#= $htmlEncode(id + HP.ids.arrow) =#_"></div>


<div class="ms-srch-hover-content" id="_#= $htmlEncode(id + HP.ids.content) =#_" data-displaytemplate="WordHoverPanel">

<div id="_#= $htmlEncode(id + HP.ids.header) =#_" class="ms-srch-hover-header">
_#= ctx.RenderHeader(ctx) =#_
</div>


<div id="_#= $htmlEncode(id + HP.ids.body) =#_" class="ms-srch-hover-body">
<!--#_ if(!Srch.U.n(ctx.CurrentItem.ServerRedirectedEmbedURL)) { ctx.CurrentItem.csr_DataShown = true; ctx.currentItem_ShowChangedBySnippet = true; _#-->

<div class="ms-srch-hover-viewerContainer">
<iframe id="_#= $htmlEncode(id + HP.ids.viewer) =#_" src="_#= $urlHtmlEncode(ctx.CurrentItem.ServerRedirectedEmbedURL) =#_" scrolling="no" frameborder="0px" class="ms-srch-hover-viewer"></iframe>
</div>


<div class="ms-srch-hover-wacImageContainer">
<img id="_#= $htmlEncode(id + HP.ids.preview) =#_" alt="_#= $htmlEncode(Srch.Res.item_Alt_Preview) =#_" onload="this.parentNode.style.display='block';" />
</div>

<!--#_ } else { ctx.CurrentItem.csr_ShowLastModifiedTime = true; ctx.CurrentItem.csr_ShowAuthors = true; } if(!Srch.U.e(ctx.CurrentItem.SectionNames)) { ctx.CurrentItem.csr_DataShown = true; _#-->

<div class="ms-srch-hover-subTitle">

<h3 class="ms-soften">_#= $htmlEncode(Srch.Res.hp_SectionHeadings) =#_</h3>

</div>

<!--#_
var sectionNames = Srch.U.getArray(ctx.CurrentItem.SectionNames);
 
var sectionIndexes = Srch.U.getArray(ctx.CurrentItem.SectionIndexes);
if(!Srch.U.n(sectionIndexes) && sectionIndexes.length != sectionNames.length)
{
sectionIndexes = null;
}
 
var hitHighlightedSectionNames = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "sectionnames");
if(!Srch.U.n(hitHighlightedSectionNames) && hitHighlightedSectionNames.length != sectionNames.length)
{
hitHighlightedSectionNames = null;
}
 
var numberOfSectionsToDisplay = Math.min(Srch.SU.maxLinesForMultiValuedProperty, sectionNames.length);
var sectionsToDisplay = new Array();
 
var usingHitHighlightedSectionNames = Srch.SU.getSectionsForDisplay(
hitHighlightedSectionNames,
numberOfSectionsToDisplay,
sectionsToDisplay);
 
for(i = 0; i < sectionsToDisplay.length; ++i) { var index = sectionsToDisplay[i]; if(Srch.U.n(index)) { continue; } var tooltipEncoded = $htmlEncode(sectionNames[index]); var htmlEncodedSectionName = ""; if(usingHitHighlightedSectionNames) { htmlEncodedSectionName = hitHighlightedSectionNames[index]; } else { htmlEncodedSectionName = tooltipEncoded; } _#-->

<div class="ms-srch-hover-text ms-srch-ellipsis" id="_#= $htmlEncode(id + HP.ids.sectionName + i) =#_" title="_#= tooltipEncoded =#_">
<!--#_ if(!Srch.U.n(sectionIndexes) && sectionIndexes.length >= i && !Srch.U.e(sectionIndexes[index]) && wacurlExist)
{
var encodedSlideIndex = "&wdSlideIndex=" + $urlKeyValueEncode(sectionIndexes[index]);
_#-->
<a clicktype="HoverSection" linkindex="_#= $htmlEncode(i) =#_" href="_#= $urlHtmlEncode(ctx.CurrentItem.ServerRedirectedURL + encodedSlideIndex) =#_" target="_blank">_#= htmlEncodedSectionName =#_
</a>
<!--#_ } _#-->
</div>

<!--#_ } } var modifieddate = window.CustomFunctions.FormatDateFromSearchResult(ctx.CurrentItem.LastModifiedTime); var author = $getItemValue(ctx, "DisplayAuthor"); if(author.value && modifieddate ) { var _authorArr = author.value.split(';'); var authorvalue = _authorArr[_authorArr.length-1]; _#-->


 Last Modified at : <span class="blue" title="_#=modifieddate =#_">_#=modifieddate =#_</span> by <span class="blue" title="_#=authorvalue=#_">_#=authorvalue =#_</span> 

<!--#_ } _#-->


<a id="_#=versionHistoryLink=#_" class="pull-left">View Version History
</a>
<a id="_#=addToOneDriveLink=#_" class="pull-right">Add To My OneDrive</a>


 

<div id="_#=noVersioningMsg=#_" class="ms-clear ms-hide ms-error">Versioning is not enabled on this library</div>

</div>

 

<div id="_#= $htmlEncode(id + HP.ids.actions) =#_" class="ms-srch-hover-actions">

<div id="Item_CommonHoverPanel_Actions">

<div class="ms-srch-hover-action">
<!--#_ var appAttribs = ""; if (!$isEmptyString(ctx.CurrentItem.csr_OpenApp)) { appAttribs += "openApp=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenApp) + "\"" }; if (!$isEmptyString(ctx.CurrentItem.csr_OpenControl)) { appAttribs += " openControl=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenControl) + "\"" }; _#-->
<a id="_#=docViewEdit=#_" class="ms-calloutLink ms-uppercase edit" >View</a>
<a id="_#=followLink=#_" class="ms-calloutLink ms-uppercase follow" >_#= $htmlEncode(Srch.Res.hp_Follow) =#_</a>
<a id="_#=docShareLink=#_" class="ms-calloutLink ms-uppercase share" title="Share" >Share</a>
<a id="_#=ctxMenuLink=#_" class="ms-calloutLink ms-uppercase menu" title="Menu" href="javascript:;" >menu</a>
 

<div id="_#=ctxMenuContainer=#_" class="ms-core-menu-box ms-hide ms-core-defaultFont ms-shadow" contenteditable="false" style="position: absolute;z-index:100;right:-10px;bottom:36px" flipped="false">
 

<ul class="ms-core-menu-list">

<li class="ms-core-menu-item ms-hide" id="_#=viewprops=#_">
<a title="View Properties" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">View Properties</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=editprops=#_">
<a title="Edit Properties" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Edit Properties</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=setAlerts=#_">
<a title="Set Alerts" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Set Alert</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=checkin=#_">
<a title="Check In" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Check In</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=checkout=#_">
<a title="CheckOut" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Check Out</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=discardcheckout=#_">
<a title="Discard Checkout" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Discard CheckOut</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=publishmajorversion=#_">
<a title="Publish A Major Version" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Publish A Major Version</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=downloadcopy=#_">
<a title="Download Copy" href="_#=ctx.CurrentItem.Path=#_" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Download a copy</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=deletefile=#_">
<a title="Delete" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Delete</span>
</div>

</a>
</li>


<li class="ms-core-menu-item ms-hide" id="_#=uploadnewversion=#_">
<a title="Upload New Version" href="javascript:;" class="ms-core-menu-link">

<div class="ms-core-menu-label">
<span class="ms-core-menu-title">Upload New Version</span>
</div>

</a>
</li>

</ul>

</div>

</div>

</div>

</div>

</div>

<!--#_ if(!Srch.U.n(ctx.CurrentItem.ServerRedirectedEmbedURL)){ AddPostRenderCallback(ctx, function(){ HP.loadViewer(ctx.CurrentItem.id, ctx.CurrentItem.id + HP.ids.inner, ctx.CurrentItem.id + HP.ids.viewer, ctx.CurrentItem.id + HP.ids.preview, ctx.CurrentItem.ServerRedirectedEmbedURL, ctx.CurrentItem.ServerRedirectedPreviewURL); }); } //Add Data and other Options var props = { csrid:id, webUrl : ctx.CurrentItem.SPWebUrl, docPath:ctx.CurrentItem.Path, listId: ctx.CurrentItem.ListID, itemId: ctx.CurrentItem.ListItemID, contactName: ctx.CurrentItem.RefinableString22, serverRedirectUrl:ctx.CurrentItem.ServerRedirectedURL }; AddPostRenderCallback(ctx, function(){ //for other options window.SPOSearchCustomizations.Automation.PreviewPaneOperations.initPreviewPaneData(props); // for "..." menu window.SPOSearchCustomizations.Automation.PreviewPaneOperations.ContextMenu.init(props); }); _#-->
</div>

</div>

</body>
</html>

this is the whole markup for a work preview pane. the two functions

window.SPOSearchCustomizations.Automation.PreviewPaneOperations.initPreviewPaneData(props);
window.SPOSearchCustomizations.Automation.PreviewPaneOperations.ContextMenu.init(props)

are defined in previewpanefunctions.js file.

You can download this js file here