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.
Perfect the same i am searching for but please provide preveiwpanefunction.js file this is not getting downloaded.please provide its urgent
LikeLike
Hi, Vipul, I updated the Link in post, thank you for letting me know the issue. Let me know if it works.
LikeLike
Thanks Shekhar. is there no way to get OOTB document library context menu on hover panel or we need to create it like this?
LikeLike
I tried to implement OOTB menu in search results, but looks like that menu is tightly coupled with listview webpart and it also includes custom actions, which might not possible to pull into search page using JS. I thought there must be a undocumented way to do this, didn’t find any OOTB JS Control. The preview pane that we see in Doc Library and search looks identical in UI, but the implementation is completely different. So, AFAIK this is the only way to do this.
LikeLike
Thanks Shekhar for the help i alos tried customizing the calloutCreateAjaxMenu and show menuForTrOutre but they are very tigtly coupled iwt hlistview webparts.i would take this approach only thanks for the help again
LikeLike
Great stuff. FYI the “window.CustomFunctions.FormatDateFromSearchResult” function call in your hover template isn’t defined anywhere.
LikeLike
Oh, yes. That was defined somewhere else in my app. Thanks for letting me know. Will update the code. 🙂
LikeLike
Good article, exactly what i needed.
By the way i can’t get “Upload New Version” option to work.
I wonder what createDialogElement function from previewpanefunctions.js does, as variable dialogElement is not initialized properly, has this anything to do with that i’ve on-prem SP 2013?
LikeLike
Hi , I was inactive on this blog since long. ‘UploadNewVersionDialogMarkup’ is id of an hidden div tag , which is a custom markup I’ve written. I forgot to include it in here. But the idea is simple, it is a div tag with file input and upload buttons in it (with classes mentioned the function ) . I was copying HTML from it, and using it to open pop. We have to pass a copy of UI element instead of actual div to ‘SP.UI.ModalDialog.showModalDialog’.
LikeLike
@Shekar Reddy
Can you please write steps to implement above.?
I stuck in what to include where?
LikeLike
$includeScript(this.url, “~sitecollection/siteassets/scripts/PreviewpaneFunctions.js”);
where should we include this code?
LikeLike
Create a control template from ootb control template and include it there.
LikeLike
I tried to put in this code in Control_SearchResults.html template but its not working
I tried to implement the above soution by copying the existing template and creating new one but it s not working.
I need exaclty like this mainly menu properties same like in library or atleast to check all version of document from search result.
its urgent please help me to make it work for me.
LikeLike
You have to add this in control template and you have to implement custom display templates and custom hover templates as well, you have to apply these display template using custom result types and result sources. If you need only the all versions link, then create a custom display template for each file type and add a link in it, it is easy to modify display templates in designer and it is self explanatory. The menu that is being shown here is not exact OOTB menu that shows in document library all the options that are available in menu are custom built and you have to modify the js and display template to add ‘all versions’ link.
LikeLike
Hi Arshad, I cannot connect with you when I’m in office, if possible I can connect in night time IST. Here is my personal number +91 xxxxxxxxx , I can connect on team viewer once I’m at home.
LikeLike
Thank you very much ….I tried this solution as per your explaination..
let me again check with control template..My minmum req. is for version histroy how ever I will prefer as it is you provided.
I will update you..
LikeLike
Contact me at shekarreddyb@live.com
LikeLike